• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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 
17 package com.android.systemui.statusbar.phone;
18 
19 import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED;
20 import static android.service.notification.NotificationListenerService.REASON_CLICK;
21 
22 import static com.android.systemui.statusbar.phone.CentralSurfaces.getActivityOptions;
23 
24 import android.app.ActivityManager;
25 import android.app.ActivityOptions;
26 import android.app.KeyguardManager;
27 import android.app.Notification;
28 import android.app.PendingIntent;
29 import android.app.TaskStackBuilder;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.pm.ResolveInfo;
33 import android.os.AsyncTask;
34 import android.os.Bundle;
35 import android.os.Handler;
36 import android.os.Looper;
37 import android.os.RemoteException;
38 import android.os.UserHandle;
39 import android.provider.Settings;
40 import android.service.dreams.IDreamManager;
41 import android.service.notification.StatusBarNotification;
42 import android.text.TextUtils;
43 import android.util.EventLog;
44 import android.view.View;
45 
46 import androidx.annotation.NonNull;
47 import androidx.annotation.VisibleForTesting;
48 
49 import com.android.internal.jank.InteractionJankMonitor;
50 import com.android.internal.logging.MetricsLogger;
51 import com.android.internal.statusbar.NotificationVisibility;
52 import com.android.internal.util.FrameworkStatsLog;
53 import com.android.internal.widget.LockPatternUtils;
54 import com.android.systemui.ActivityIntentHelper;
55 import com.android.systemui.EventLogTags;
56 import com.android.systemui.animation.ActivityTransitionAnimator;
57 import com.android.systemui.assist.AssistManager;
58 import com.android.systemui.dagger.SysUISingleton;
59 import com.android.systemui.dagger.qualifiers.Background;
60 import com.android.systemui.dagger.qualifiers.Main;
61 import com.android.systemui.plugins.ActivityStarter;
62 import com.android.systemui.power.domain.interactor.PowerInteractor;
63 import com.android.systemui.settings.UserTracker;
64 import com.android.systemui.shade.ShadeController;
65 import com.android.systemui.shade.ShadeDisplayAware;
66 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor;
67 import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor;
68 import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor;
69 import com.android.systemui.statusbar.CommandQueue;
70 import com.android.systemui.statusbar.NotificationClickNotifier;
71 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
72 import com.android.systemui.statusbar.NotificationPresenter;
73 import com.android.systemui.statusbar.NotificationRemoteInputManager;
74 import com.android.systemui.statusbar.NotificationShadeWindowController;
75 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
76 import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
77 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
78 import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider;
79 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider;
80 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix;
81 import com.android.systemui.statusbar.notification.headsup.HeadsUpManager;
82 import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil;
83 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
84 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowDragController;
85 import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
86 import com.android.systemui.statusbar.policy.KeyguardStateController;
87 import com.android.systemui.wmshell.BubblesManager;
88 
89 import dagger.Lazy;
90 
91 import java.util.List;
92 import java.util.Optional;
93 import java.util.concurrent.Executor;
94 
95 import javax.inject.Inject;
96 
97 /**
98  * Status bar implementation of {@link NotificationActivityStarter}.
99  */
100 @SysUISingleton
101 public class StatusBarNotificationActivityStarter implements NotificationActivityStarter {
102 
103     /**
104      * Helps to avoid recalculation of values provided to
105      * {@link #onDismiss(PendingIntent, boolean, boolean, boolean)}} method
106      */
107     private interface OnKeyguardDismissedAction {
108         /**
109          * Invoked when keyguard is dismissed
110          *
111          * @return is used as return value for {@link ActivityStarter.OnDismissAction#onDismiss()}
112          */
onDismiss(PendingIntent intent, boolean isActivityIntent, boolean animate, boolean showOverTheLockScreen)113         boolean onDismiss(PendingIntent intent, boolean isActivityIntent, boolean animate,
114                 boolean showOverTheLockScreen);
115     }
116 
117     private final static String TAG = "StatusBarNotificationActivityStarter";
118 
119     private final Context mContext;
120     private final ShadeDialogContextInteractor mContextInteractor;
121 
122     private final Handler mMainThreadHandler;
123     private final Executor mUiBgExecutor;
124 
125     private final NotificationVisibilityProvider mVisibilityProvider;
126     private final HeadsUpManager mHeadsUpManager;
127     private final ActivityStarter mActivityStarter;
128     private final CommandQueue mCommandQueue;
129     private final NotificationClickNotifier mClickNotifier;
130     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
131     private final KeyguardManager mKeyguardManager;
132     private final IDreamManager mDreamManager;
133     private final Optional<BubblesManager> mBubblesManagerOptional;
134     private final Lazy<AssistManager> mAssistManagerLazy;
135     private final NotificationRemoteInputManager mRemoteInputManager;
136     private final NotificationLockscreenUserManager mLockscreenUserManager;
137     private final ShadeController mShadeController;
138     private final KeyguardStateController mKeyguardStateController;
139     private final LockPatternUtils mLockPatternUtils;
140     private final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback;
141     private final ActivityIntentHelper mActivityIntentHelper;
142     private final ShadeAnimationInteractor mShadeAnimationInteractor;
143 
144     private final MetricsLogger mMetricsLogger;
145     private final StatusBarNotificationActivityStarterLogger mLogger;
146 
147     private final NotificationPresenter mPresenter;
148     private final PanelExpansionInteractor mPanelExpansionInteractor;
149     private final NotificationShadeWindowController mNotificationShadeWindowController;
150     private final ActivityTransitionAnimator mActivityTransitionAnimator;
151     private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
152     private final PowerInteractor mPowerInteractor;
153     private final UserTracker mUserTracker;
154     private final OnUserInteractionCallback mOnUserInteractionCallback;
155 
156     private boolean mIsCollapsingToShowActivityOverLockscreen;
157 
158     @Inject
StatusBarNotificationActivityStarter( @hadeDisplayAware Context context, ShadeDialogContextInteractor contextInteractor, @Main Handler mainThreadHandler, @Background Executor uiBgExecutor, NotificationVisibilityProvider visibilityProvider, HeadsUpManager headsUpManager, ActivityStarter activityStarter, CommandQueue commandQueue, NotificationClickNotifier clickNotifier, StatusBarKeyguardViewManager statusBarKeyguardViewManager, KeyguardManager keyguardManager, IDreamManager dreamManager, Optional<BubblesManager> bubblesManagerOptional, Lazy<AssistManager> assistManagerLazy, NotificationRemoteInputManager remoteInputManager, NotificationLockscreenUserManager lockscreenUserManager, ShadeController shadeController, KeyguardStateController keyguardStateController, LockPatternUtils lockPatternUtils, StatusBarRemoteInputCallback remoteInputCallback, ActivityIntentHelper activityIntentHelper, MetricsLogger metricsLogger, StatusBarNotificationActivityStarterLogger logger, OnUserInteractionCallback onUserInteractionCallback, NotificationPresenter presenter, PanelExpansionInteractor panelExpansionInteractor, NotificationShadeWindowController notificationShadeWindowController, ActivityTransitionAnimator activityTransitionAnimator, ShadeAnimationInteractor shadeAnimationInteractor, NotificationLaunchAnimatorControllerProvider notificationAnimationProvider, LaunchFullScreenIntentProvider launchFullScreenIntentProvider, PowerInteractor powerInteractor, UserTracker userTracker)159     StatusBarNotificationActivityStarter(
160             @ShadeDisplayAware Context context,
161             ShadeDialogContextInteractor contextInteractor,
162             @Main Handler mainThreadHandler,
163             @Background Executor uiBgExecutor,
164             NotificationVisibilityProvider visibilityProvider,
165             HeadsUpManager headsUpManager,
166             ActivityStarter activityStarter,
167             CommandQueue commandQueue,
168             NotificationClickNotifier clickNotifier,
169             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
170             KeyguardManager keyguardManager,
171             IDreamManager dreamManager,
172             Optional<BubblesManager> bubblesManagerOptional,
173             Lazy<AssistManager> assistManagerLazy,
174             NotificationRemoteInputManager remoteInputManager,
175             NotificationLockscreenUserManager lockscreenUserManager,
176             ShadeController shadeController,
177             KeyguardStateController keyguardStateController,
178             LockPatternUtils lockPatternUtils,
179             StatusBarRemoteInputCallback remoteInputCallback,
180             ActivityIntentHelper activityIntentHelper,
181             MetricsLogger metricsLogger,
182             StatusBarNotificationActivityStarterLogger logger,
183             OnUserInteractionCallback onUserInteractionCallback,
184             NotificationPresenter presenter,
185             PanelExpansionInteractor panelExpansionInteractor,
186             NotificationShadeWindowController notificationShadeWindowController,
187             ActivityTransitionAnimator activityTransitionAnimator,
188             ShadeAnimationInteractor shadeAnimationInteractor,
189             NotificationLaunchAnimatorControllerProvider notificationAnimationProvider,
190             LaunchFullScreenIntentProvider launchFullScreenIntentProvider,
191             PowerInteractor powerInteractor,
192             UserTracker userTracker) {
193         mContext = context;
194         mContextInteractor = contextInteractor;
195         mMainThreadHandler = mainThreadHandler;
196         mUiBgExecutor = uiBgExecutor;
197         mVisibilityProvider = visibilityProvider;
198         mHeadsUpManager = headsUpManager;
199         mActivityStarter = activityStarter;
200         mCommandQueue = commandQueue;
201         mClickNotifier = clickNotifier;
202         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
203         mKeyguardManager = keyguardManager;
204         mDreamManager = dreamManager;
205         mBubblesManagerOptional = bubblesManagerOptional;
206         mAssistManagerLazy = assistManagerLazy;
207         mRemoteInputManager = remoteInputManager;
208         mLockscreenUserManager = lockscreenUserManager;
209         mShadeController = shadeController;
210         mKeyguardStateController = keyguardStateController;
211         mLockPatternUtils = lockPatternUtils;
212         mStatusBarRemoteInputCallback = remoteInputCallback;
213         mActivityIntentHelper = activityIntentHelper;
214         mPanelExpansionInteractor = panelExpansionInteractor;
215         mNotificationShadeWindowController = notificationShadeWindowController;
216         mShadeAnimationInteractor = shadeAnimationInteractor;
217         mMetricsLogger = metricsLogger;
218         mLogger = logger;
219         mOnUserInteractionCallback = onUserInteractionCallback;
220         mPresenter = presenter;
221         mActivityTransitionAnimator = activityTransitionAnimator;
222         mNotificationAnimationProvider = notificationAnimationProvider;
223         mPowerInteractor = powerInteractor;
224         mUserTracker = userTracker;
225 
226         launchFullScreenIntentProvider.registerListener(entry -> launchFullScreenIntent(entry));
227     }
228 
229     /**
230      * Called when the user clicks on the notification bubble icon.
231      *
232      * @param entry notification that bubble icon was clicked
233      */
234     @Override
onNotificationBubbleIconClicked(@onNull NotificationEntry entry)235     public void onNotificationBubbleIconClicked(@NonNull NotificationEntry entry) {
236         Runnable action = () -> {
237             mBubblesManagerOptional.ifPresent(bubblesManager ->
238                     bubblesManager.onUserChangedBubble(entry, !entry.isBubble()));
239             mHeadsUpManager.removeNotification(entry.getKey(), /* releaseImmediately= */ true,
240                     /* reason= */ "onNotificationBubbleIconClicked");
241         };
242         if (entry.isBubble()) {
243             // entry is being un-bubbled, no need to unlock
244             action.run();
245         } else {
246             performActionAfterKeyguardDismissed(entry,
247                     (intent, isActivityIntent, animate, showOverTheLockScreen) -> {
248                         action.run();
249                         return false;
250                     });
251         }
252     }
253 
254     /**
255      * Called when a notification is clicked.
256      *
257      * @param entry notification that was clicked
258      * @param row   row for that notification
259      */
260     @Override
onNotificationClicked(@onNull NotificationEntry entry, @NonNull ExpandableNotificationRow row)261     public void onNotificationClicked(@NonNull NotificationEntry entry,
262             @NonNull ExpandableNotificationRow row) {
263         mLogger.logStartingActivityFromClick(entry, row.isHeadsUpState(),
264                 mKeyguardStateController.isVisible(),
265                 mNotificationShadeWindowController.getPanelExpanded());
266         OnKeyguardDismissedAction action =
267                 (intent, isActivityIntent, animate, showOverTheLockScreen) ->
268                         performActionOnKeyguardDismissed(entry, row, intent, isActivityIntent,
269                                 animate, showOverTheLockScreen);
270         performActionAfterKeyguardDismissed(entry, action);
271     }
272 
performActionAfterKeyguardDismissed(NotificationEntry entry, OnKeyguardDismissedAction action)273     private void performActionAfterKeyguardDismissed(NotificationEntry entry,
274             OnKeyguardDismissedAction action) {
275         if (mRemoteInputManager.isRemoteInputActive(entry)) {
276             // We have an active remote input typed and the user clicked on the notification.
277             // this was probably unintentional, so we're closing the edit text instead.
278             mRemoteInputManager.closeRemoteInputs();
279             mLogger.logCloseRemoteInput(entry);
280             return;
281         }
282         Notification notification = entry.getSbn().getNotification();
283         final PendingIntent intent = notification.contentIntent != null
284                 ? notification.contentIntent
285                 : notification.fullScreenIntent;
286         final boolean isBubble = entry.isBubble();
287 
288         // This code path is now executed for notification without a contentIntent.
289         // The only valid case is Bubble notifications. Guard against other cases
290         // entering here.
291         if (intent == null && !isBubble) {
292             mLogger.logNonClickableNotification(entry);
293             return;
294         }
295 
296         boolean isActivityIntent = intent != null && intent.isActivity() && !isBubble;
297         final boolean willLaunchResolverActivity = isActivityIntent
298                 && mActivityIntentHelper.wouldPendingLaunchResolverActivity(intent,
299                 mLockscreenUserManager.getCurrentUserId());
300         final boolean animate = !willLaunchResolverActivity
301                 && mActivityStarter.shouldAnimateLaunch(isActivityIntent);
302         boolean showOverLockscreen = mKeyguardStateController.isShowing() && intent != null
303                 && mActivityIntentHelper.wouldPendingShowOverLockscreen(intent,
304                 mLockscreenUserManager.getCurrentUserId());
305         ActivityStarter.OnDismissAction postKeyguardAction = new ActivityStarter.OnDismissAction() {
306             @Override
307             public boolean onDismiss() {
308                 return action.onDismiss(intent, isActivityIntent, animate, showOverLockscreen);
309             }
310 
311             @Override
312             public boolean willRunAnimationOnKeyguard() {
313                 return animate;
314             }
315         };
316         if (showOverLockscreen) {
317             mIsCollapsingToShowActivityOverLockscreen = true;
318             postKeyguardAction.onDismiss();
319         } else {
320             mActivityStarter.dismissKeyguardThenExecute(
321                     postKeyguardAction,
322                     null,
323                     willLaunchResolverActivity);
324         }
325     }
326 
performActionOnKeyguardDismissed( NotificationEntry entry, ExpandableNotificationRow row, PendingIntent intent, boolean isActivityIntent, boolean animate, boolean showOverLockscreen)327     private boolean performActionOnKeyguardDismissed(
328             NotificationEntry entry,
329             ExpandableNotificationRow row,
330             PendingIntent intent,
331             boolean isActivityIntent,
332             boolean animate,
333             boolean showOverLockscreen) {
334         mLogger.logHandleClickAfterKeyguardDismissed(entry);
335 
336         final Runnable runnable = () -> handleNotificationClickAfterPanelCollapsed(
337                 entry, row, intent, isActivityIntent, animate);
338         if (showOverLockscreen) {
339             mShadeController.addPostCollapseAction(runnable);
340             mShadeController.collapseShade(true /* animate */);
341         } else if (mKeyguardStateController.isShowing()
342                 && mKeyguardStateController.isOccluded()) {
343             mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
344             mShadeController.collapseShade();
345         } else {
346             runnable.run();
347         }
348 
349         // Always defer the keyguard dismiss when animating.
350         return animate || !mPanelExpansionInteractor.isFullyCollapsed();
351     }
352 
handleNotificationClickAfterPanelCollapsed( NotificationEntry entry, ExpandableNotificationRow row, PendingIntent intent, boolean isActivityIntent, boolean animate)353     private void handleNotificationClickAfterPanelCollapsed(
354             NotificationEntry entry,
355             ExpandableNotificationRow row,
356             PendingIntent intent,
357             boolean isActivityIntent,
358             boolean animate) {
359         String notificationKey = entry.getKey();
360         mLogger.logHandleClickAfterPanelCollapsed(entry);
361 
362         try {
363             // The intent we are sending is for the application, which
364             // won't have permission to immediately start an activity after
365             // the user switches to home.  We know it is safe to do at this
366             // point, so make sure new activity switches are now allowed.
367             ActivityManager.getService().resumeAppSwitches();
368         } catch (RemoteException e) {
369         }
370         // If the notification should be cancelled on click and we are launching a work activity in
371         // a locked profile with separate challenge, we defer the activity action and cancelling of
372         // the notification until work challenge is unlocked. If the notification shouldn't be
373         // cancelled, the work challenge will be shown by ActivityManager if necessary anyway.
374         if (isActivityIntent && shouldAutoCancel(entry.getSbn())) {
375             final int userId = intent.getCreatorUserHandle().getIdentifier();
376             if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
377                     && mKeyguardManager.isDeviceLocked(userId)) {
378                 // TODO(b/28935539): should allow certain activities to
379                 // bypass work challenge
380                 if (mStatusBarRemoteInputCallback.startWorkChallengeIfNecessary(userId,
381                         intent.getIntentSender(), notificationKey)) {
382                     removeHunAfterClick(row);
383                     // Show work challenge, do not run PendingIntent and
384                     // remove notification
385                     mShadeController.collapseOnMainThread();
386                     return;
387                 }
388             }
389         }
390         Intent fillInIntent = null;
391         CharSequence remoteInputText = null;
392         if (!TextUtils.isEmpty(entry.remoteInputText)) {
393             remoteInputText = entry.remoteInputText;
394         }
395         if (!TextUtils.isEmpty(remoteInputText)
396                 && !mRemoteInputManager.isSpinning(notificationKey)) {
397             fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT,
398                     remoteInputText.toString());
399         }
400         final boolean canBubble = entry.canBubble();
401         if (canBubble) {
402             mLogger.logExpandingBubble(entry);
403             removeHunAfterClick(row);
404             expandBubbleStackOnMainThread(entry);
405         } else {
406             startNotificationIntent(intent, fillInIntent, entry, row, animate, isActivityIntent);
407         }
408         if (isActivityIntent || canBubble) {
409             mAssistManagerLazy.get().hideAssist();
410         }
411 
412         final NotificationVisibility nv = mVisibilityProvider.obtain(entry, true);
413 
414         if (!canBubble && (shouldAutoCancel(entry.getSbn())
415                 || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(notificationKey))) {
416             final Runnable removeNotification =
417                     mOnUserInteractionCallback.registerFutureDismissal(entry, REASON_CLICK);
418             // Immediately remove notification from visually showing.
419             // We have to post the removal to the UI thread for synchronization.
420             mMainThreadHandler.post(() -> {
421                 if (mPresenter.isCollapsing()) {
422                     // To avoid lags we're only performing the remove after the shade is collapsed
423                     mShadeController.addPostCollapseAction(removeNotification);
424                 } else {
425                     removeNotification.run();
426                 }
427             });
428         }
429 
430         // inform NMS that the notification was clicked
431         mClickNotifier.onNotificationClick(notificationKey, nv);
432 
433         mIsCollapsingToShowActivityOverLockscreen = false;
434     }
435 
436     /**
437      * Called when a notification is dropped on proper target window.
438      * Intent that is included in this entry notification,
439      * will be sent by {@link ExpandableNotificationRowDragController}
440      *
441      * @param entry notification entry that is dropped.
442      */
443     @Override
onDragSuccess(@onNull NotificationEntry entry)444     public void onDragSuccess(@NonNull NotificationEntry entry) {
445         // this method is not responsible for intent sending.
446         // will focus follow operation only after drag-and-drop that notification.
447         final NotificationVisibility nv = mVisibilityProvider.obtain(entry, true);
448 
449         String notificationKey = entry.getKey();
450         if (shouldAutoCancel(entry.getSbn())
451                 || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(notificationKey)) {
452             final Runnable removeNotification =
453                     mOnUserInteractionCallback.registerFutureDismissal(entry, REASON_CLICK);
454             // Immediately remove notification from visually showing.
455             // We have to post the removal to the UI thread for synchronization.
456             mMainThreadHandler.post(() -> {
457                 if (mPresenter.isCollapsing()) {
458                     // To avoid lags we're only performing the remove
459                     // after the shade is collapsed
460                     mShadeController.addPostCollapseAction(removeNotification);
461                 } else {
462                     removeNotification.run();
463                 }
464             });
465         }
466 
467         // inform NMS that the notification was clicked
468         mClickNotifier.onNotificationClick(notificationKey, nv);
469 
470         mIsCollapsingToShowActivityOverLockscreen = false;
471     }
472 
expandBubbleStackOnMainThread(NotificationEntry entry)473     private void expandBubbleStackOnMainThread(NotificationEntry entry) {
474         if (!mBubblesManagerOptional.isPresent()) {
475             return;
476         }
477 
478         if (Looper.getMainLooper().isCurrentThread()) {
479             expandBubbleStack(entry);
480         } else {
481             mMainThreadHandler.post(() -> expandBubbleStack(entry));
482         }
483     }
484 
expandBubbleStack(NotificationEntry entry)485     private void expandBubbleStack(NotificationEntry entry) {
486         mBubblesManagerOptional.get().expandStackAndSelectBubble(entry);
487         mShadeController.collapseShade();
488     }
489 
startNotificationIntent( PendingIntent intent, Intent fillInIntent, NotificationEntry entry, ExpandableNotificationRow row, boolean animate, boolean isActivityIntent)490     private void startNotificationIntent(
491             PendingIntent intent,
492             Intent fillInIntent,
493             NotificationEntry entry,
494             ExpandableNotificationRow row,
495             boolean animate,
496             boolean isActivityIntent) {
497         mLogger.logStartNotificationIntent(entry);
498         final int displayId = mContextInteractor.getContext().getDisplayId();
499         try {
500             ActivityTransitionAnimator.Controller animationController =
501                     new StatusBarTransitionAnimatorController(
502                             mNotificationAnimationProvider.getAnimatorController(row, null),
503                             mShadeAnimationInteractor,
504                             mShadeController,
505                             mNotificationShadeWindowController,
506                             mCommandQueue,
507                             displayId,
508                             isActivityIntent);
509             mActivityTransitionAnimator.startPendingIntentWithAnimation(
510                     animationController,
511                     animate,
512                     intent.getCreatorPackage(),
513                     (adapter) -> {
514                         long eventTime = row.getAndResetLastActionUpTime();
515                         Bundle options = eventTime > 0
516                                 ? getActivityOptions(
517                                 displayId,
518                                 adapter,
519                                 mKeyguardStateController.isShowing(),
520                                 eventTime)
521                                 : getActivityOptions(displayId, adapter);
522                         int result = intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
523                                 null, null, options);
524                         mLogger.logSendPendingIntent(entry, intent, result);
525                         return result;
526                     });
527         } catch (PendingIntent.CanceledException e) {
528             // the stack trace isn't very helpful here.
529             // Just log the exception message.
530             mLogger.logSendingIntentFailed(e);
531             // TODO: Dismiss Keyguard.
532         }
533     }
534 
535     @Override
startNotificationGutsIntent(@onNull final Intent intent, final int appUid, @NonNull ExpandableNotificationRow row)536     public void startNotificationGutsIntent(@NonNull final Intent intent, final int appUid,
537             @NonNull ExpandableNotificationRow row) {
538         boolean animate = mActivityStarter.shouldAnimateLaunch(true /* isActivityIntent */);
539         final int displayId = mContextInteractor.getContext().getDisplayId();
540         ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() {
541             @Override
542             public boolean onDismiss() {
543                 AsyncTask.execute(() -> {
544                     ActivityTransitionAnimator.Controller animationController =
545                             new StatusBarTransitionAnimatorController(
546                                     mNotificationAnimationProvider.getAnimatorController(row),
547                                     mShadeAnimationInteractor,
548                                     mShadeController,
549                                     mNotificationShadeWindowController,
550                                     mCommandQueue,
551                                     displayId,
552                                     true /* isActivityIntent */);
553 
554                     mActivityTransitionAnimator.startIntentWithAnimation(
555                             animationController, animate, intent.getPackage(),
556                             (adapter) -> TaskStackBuilder.create(mContext)
557                                     .addNextIntentWithParentStack(intent)
558                                     .startActivities(getActivityOptions(
559                                                     displayId,
560                                                     adapter),
561                                             new UserHandle(UserHandle.getUserId(appUid))));
562                 });
563                 return true;
564             }
565 
566             @Override
567             public boolean willRunAnimationOnKeyguard() {
568                 return animate;
569             }
570         };
571         mActivityStarter.dismissKeyguardThenExecute(onDismissAction, null,
572                 false /* afterKeyguardGone */);
573     }
574 
575     @Override
startHistoryIntent(View view, boolean showHistory)576     public void startHistoryIntent(View view, boolean showHistory) {
577         ModesEmptyShadeFix.assertInLegacyMode();
578         final int displayId = mContextInteractor.getContext().getDisplayId();
579         boolean animate = mActivityStarter.shouldAnimateLaunch(true /* isActivityIntent */);
580         ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() {
581             @Override
582             public boolean onDismiss() {
583                 AsyncTask.execute(() -> {
584                     Intent intent = showHistory ? new Intent(
585                             Settings.ACTION_NOTIFICATION_HISTORY) : new Intent(
586                             Settings.ACTION_NOTIFICATION_SETTINGS);
587                     TaskStackBuilder tsb = TaskStackBuilder.create(mContext)
588                             .addNextIntent(new Intent(Settings.ACTION_NOTIFICATION_SETTINGS));
589                     if (showHistory) {
590                         tsb.addNextIntent(intent);
591                     }
592 
593                     ActivityTransitionAnimator.Controller viewController =
594                             ActivityTransitionAnimator.Controller.fromView(view,
595                                     InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON
596                             );
597                     ActivityTransitionAnimator.Controller animationController =
598                             viewController == null ? null
599                                     : new StatusBarTransitionAnimatorController(
600                                             viewController,
601                                             mShadeAnimationInteractor,
602                                             mShadeController,
603                                             mNotificationShadeWindowController,
604                                             mCommandQueue,
605                                             displayId,
606                                             true /* isActivityIntent */);
607 
608                     mActivityTransitionAnimator.startIntentWithAnimation(
609                             animationController, animate, intent.getPackage(),
610                             (adapter) -> tsb.startActivities(
611                                     getActivityOptions(displayId, adapter),
612                                     mUserTracker.getUserHandle()));
613                 });
614                 return true;
615             }
616 
617             @Override
618             public boolean willRunAnimationOnKeyguard() {
619                 return animate;
620             }
621         };
622         mActivityStarter.dismissKeyguardThenExecute(onDismissAction, null,
623                 false /* afterKeyguardGone */);
624     }
625 
626     @Override
startSettingsIntent(@onNull View view, @NonNull SettingsIntent intentInfo)627     public void startSettingsIntent(@NonNull View view, @NonNull SettingsIntent intentInfo) {
628         final int displayId = mContextInteractor.getContext().getDisplayId();
629         boolean animate = mActivityStarter.shouldAnimateLaunch(true /* isActivityIntent */);
630         ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() {
631             @Override
632             public boolean onDismiss() {
633                 AsyncTask.execute(() -> {
634                     TaskStackBuilder tsb = TaskStackBuilder.create(mContext);
635                     for (Intent intent : intentInfo.getBackStack()) {
636                         tsb.addNextIntent(intent);
637                     }
638                     tsb.addNextIntent(intentInfo.getTargetIntent());
639 
640                     ActivityTransitionAnimator.Controller viewController =
641                             ActivityTransitionAnimator.Controller.fromView(view,
642                                     intentInfo.getCujType());
643                     ActivityTransitionAnimator.Controller animationController =
644                             viewController == null ? null
645                                     : new StatusBarTransitionAnimatorController(
646                                             viewController,
647                                             mShadeAnimationInteractor,
648                                             mShadeController,
649                                             mNotificationShadeWindowController,
650                                             mCommandQueue,
651                                             displayId,
652                                             true /* isActivityIntent */);
653 
654                     mActivityTransitionAnimator.startIntentWithAnimation(
655                             animationController, animate, intentInfo.getTargetIntent().getPackage(),
656                             (adapter) -> tsb.startActivities(
657                                     getActivityOptions(displayId, adapter),
658                                     mUserTracker.getUserHandle()));
659                 });
660                 return true;
661             }
662 
663             @Override
664             public boolean willRunAnimationOnKeyguard() {
665                 return animate;
666             }
667         };
668         mActivityStarter.dismissKeyguardThenExecute(onDismissAction, null,
669                 false /* afterKeyguardGone */);
670     }
671 
removeHunAfterClick(ExpandableNotificationRow row)672     private void removeHunAfterClick(ExpandableNotificationRow row) {
673         String key = row.getKey();
674         if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUpEntry(key)) {
675             // Release the HUN notification to the shade.
676             if (mPresenter.isPresenterFullyCollapsed()) {
677                 HeadsUpUtil.setNeedsHeadsUpDisappearAnimationAfterClick(row, true);
678             }
679 
680             // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
681             // become canceled shortly by NoMan, but we can't assume that.
682             mHeadsUpManager.removeNotification(key, /* releaseImmediately= */ true,
683                     "removeHunAfterClick");
684         }
685     }
686 
687     @VisibleForTesting
launchFullScreenIntent(NotificationEntry entry)688     void launchFullScreenIntent(NotificationEntry entry) {
689         // Skip if device is in VR mode.
690         if (mPresenter.isDeviceInVrMode()) {
691             mLogger.logFullScreenIntentSuppressedByVR(entry);
692             return;
693         }
694         // Stop screensaver if the notification has a fullscreen intent.
695         // (like an incoming phone call)
696         mUiBgExecutor.execute(() -> {
697             try {
698                 mDreamManager.awaken();
699             } catch (RemoteException e) {
700                 e.printStackTrace();
701             }
702         });
703 
704         // not immersive & a fullscreen alert should be shown
705         final PendingIntent fullScreenIntent =
706                 entry.getSbn().getNotification().fullScreenIntent;
707         mLogger.logSendingFullScreenIntent(entry, fullScreenIntent);
708         try {
709             EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
710                     entry.getKey());
711             mPowerInteractor.wakeUpForFullScreenIntent();
712 
713             ActivityOptions options = ActivityOptions.makeBasic();
714             options.setPendingIntentBackgroundActivityStartMode(
715                     MODE_BACKGROUND_ACTIVITY_START_ALLOWED);
716             fullScreenIntent.sendAndReturnResult(null, 0, null, null, null, null,
717                     options.toBundle());
718             entry.notifyFullScreenIntentLaunched();
719 
720             mMetricsLogger.count("note_fullscreen", 1);
721 
722             String activityName;
723             List<ResolveInfo> resolveInfos = fullScreenIntent.queryIntentComponents(0);
724             if (resolveInfos.size() > 0 && resolveInfos.get(0) != null
725                     && resolveInfos.get(0).activityInfo != null
726                     && resolveInfos.get(0).activityInfo.name != null) {
727                 activityName = resolveInfos.get(0).activityInfo.name;
728             } else {
729                 activityName = "";
730             }
731             FrameworkStatsLog.write(FrameworkStatsLog.FULL_SCREEN_INTENT_LAUNCHED,
732                     fullScreenIntent.getCreatorUid(),
733                     activityName);
734         } catch (PendingIntent.CanceledException e) {
735             // ignore
736         }
737     }
738 
739     @Override
isCollapsingToShowActivityOverLockscreen()740     public boolean isCollapsingToShowActivityOverLockscreen() {
741         return mIsCollapsingToShowActivityOverLockscreen;
742     }
743 
shouldAutoCancel(StatusBarNotification sbn)744     private static boolean shouldAutoCancel(StatusBarNotification sbn) {
745         int flags = sbn.getNotification().flags;
746         if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) {
747             return false;
748         }
749         return true;
750     }
751 
752 }
753