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.service.notification.NotificationListenerService.REASON_CLICK;
20 
21 import static com.android.systemui.statusbar.phone.StatusBar.getActivityOptions;
22 
23 import android.app.ActivityManager;
24 import android.app.KeyguardManager;
25 import android.app.Notification;
26 import android.app.NotificationManager;
27 import android.app.PendingIntent;
28 import android.app.TaskStackBuilder;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.os.AsyncTask;
32 import android.os.Bundle;
33 import android.os.Handler;
34 import android.os.Looper;
35 import android.os.RemoteException;
36 import android.os.UserHandle;
37 import android.provider.Settings;
38 import android.service.dreams.IDreamManager;
39 import android.service.notification.StatusBarNotification;
40 import android.text.TextUtils;
41 import android.util.EventLog;
42 import android.view.View;
43 
44 import androidx.annotation.VisibleForTesting;
45 
46 import com.android.internal.jank.InteractionJankMonitor;
47 import com.android.internal.logging.MetricsLogger;
48 import com.android.internal.statusbar.NotificationVisibility;
49 import com.android.internal.widget.LockPatternUtils;
50 import com.android.systemui.ActivityIntentHelper;
51 import com.android.systemui.EventLogTags;
52 import com.android.systemui.animation.ActivityLaunchAnimator;
53 import com.android.systemui.assist.AssistManager;
54 import com.android.systemui.dagger.SysUISingleton;
55 import com.android.systemui.dagger.qualifiers.Main;
56 import com.android.systemui.dagger.qualifiers.UiBackground;
57 import com.android.systemui.flags.FeatureFlags;
58 import com.android.systemui.plugins.ActivityStarter;
59 import com.android.systemui.plugins.statusbar.StatusBarStateController;
60 import com.android.systemui.statusbar.CommandQueue;
61 import com.android.systemui.statusbar.NotificationClickNotifier;
62 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
63 import com.android.systemui.statusbar.NotificationPresenter;
64 import com.android.systemui.statusbar.NotificationRemoteInputManager;
65 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
66 import com.android.systemui.statusbar.notification.NotificationEntryListener;
67 import com.android.systemui.statusbar.notification.NotificationEntryManager;
68 import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider;
69 import com.android.systemui.statusbar.notification.collection.NotifPipeline;
70 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
71 import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
72 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
73 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
74 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
75 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
76 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowDragController;
77 import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
78 import com.android.systemui.statusbar.policy.HeadsUpUtil;
79 import com.android.systemui.statusbar.policy.KeyguardStateController;
80 import com.android.systemui.wmshell.BubblesManager;
81 
82 import java.util.Optional;
83 import java.util.concurrent.Executor;
84 
85 import javax.inject.Inject;
86 
87 import dagger.Lazy;
88 
89 /**
90  * Status bar implementation of {@link NotificationActivityStarter}.
91  */
92 public class StatusBarNotificationActivityStarter implements NotificationActivityStarter {
93 
94     private final Context mContext;
95 
96     private final CommandQueue mCommandQueue;
97     private final Handler mMainThreadHandler;
98     private final Executor mUiBgExecutor;
99 
100     private final NotificationEntryManager mEntryManager;
101     private final NotifPipeline mNotifPipeline;
102     private final HeadsUpManagerPhone mHeadsUpManager;
103     private final ActivityStarter mActivityStarter;
104     private final NotificationClickNotifier mClickNotifier;
105     private final StatusBarStateController mStatusBarStateController;
106     private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
107     private final KeyguardManager mKeyguardManager;
108     private final IDreamManager mDreamManager;
109     private final Optional<BubblesManager> mBubblesManagerOptional;
110     private final Lazy<AssistManager> mAssistManagerLazy;
111     private final NotificationRemoteInputManager mRemoteInputManager;
112     private final GroupMembershipManager mGroupMembershipManager;
113     private final NotificationLockscreenUserManager mLockscreenUserManager;
114     private final ShadeController mShadeController;
115     private final KeyguardStateController mKeyguardStateController;
116     private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
117     private final LockPatternUtils mLockPatternUtils;
118     private final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback;
119     private final ActivityIntentHelper mActivityIntentHelper;
120 
121     private final FeatureFlags mFeatureFlags;
122     private final MetricsLogger mMetricsLogger;
123     private final StatusBarNotificationActivityStarterLogger mLogger;
124 
125     private final StatusBar mStatusBar;
126     private final NotificationPresenter mPresenter;
127     private final NotificationPanelViewController mNotificationPanel;
128     private final ActivityLaunchAnimator mActivityLaunchAnimator;
129     private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
130     private final OnUserInteractionCallback mOnUserInteractionCallback;
131 
132     private boolean mIsCollapsingToShowActivityOverLockscreen;
133 
StatusBarNotificationActivityStarter( Context context, CommandQueue commandQueue, Handler mainThreadHandler, Executor uiBgExecutor, NotificationEntryManager entryManager, NotifPipeline notifPipeline, HeadsUpManagerPhone headsUpManager, ActivityStarter activityStarter, NotificationClickNotifier clickNotifier, StatusBarStateController statusBarStateController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, KeyguardManager keyguardManager, IDreamManager dreamManager, Optional<BubblesManager> bubblesManagerOptional, Lazy<AssistManager> assistManagerLazy, NotificationRemoteInputManager remoteInputManager, GroupMembershipManager groupMembershipManager, NotificationLockscreenUserManager lockscreenUserManager, ShadeController shadeController, KeyguardStateController keyguardStateController, NotificationInterruptStateProvider notificationInterruptStateProvider, LockPatternUtils lockPatternUtils, StatusBarRemoteInputCallback remoteInputCallback, ActivityIntentHelper activityIntentHelper, FeatureFlags featureFlags, MetricsLogger metricsLogger, StatusBarNotificationActivityStarterLogger logger, OnUserInteractionCallback onUserInteractionCallback, StatusBar statusBar, NotificationPresenter presenter, NotificationPanelViewController panel, ActivityLaunchAnimator activityLaunchAnimator, NotificationLaunchAnimatorControllerProvider notificationAnimationProvider)134     private StatusBarNotificationActivityStarter(
135             Context context,
136             CommandQueue commandQueue,
137             Handler mainThreadHandler,
138             Executor uiBgExecutor,
139             NotificationEntryManager entryManager,
140             NotifPipeline notifPipeline,
141             HeadsUpManagerPhone headsUpManager,
142             ActivityStarter activityStarter,
143             NotificationClickNotifier clickNotifier,
144             StatusBarStateController statusBarStateController,
145             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
146             KeyguardManager keyguardManager,
147             IDreamManager dreamManager,
148             Optional<BubblesManager> bubblesManagerOptional,
149             Lazy<AssistManager> assistManagerLazy,
150             NotificationRemoteInputManager remoteInputManager,
151             GroupMembershipManager groupMembershipManager,
152             NotificationLockscreenUserManager lockscreenUserManager,
153             ShadeController shadeController,
154             KeyguardStateController keyguardStateController,
155             NotificationInterruptStateProvider notificationInterruptStateProvider,
156             LockPatternUtils lockPatternUtils,
157             StatusBarRemoteInputCallback remoteInputCallback,
158             ActivityIntentHelper activityIntentHelper,
159 
160             FeatureFlags featureFlags,
161             MetricsLogger metricsLogger,
162             StatusBarNotificationActivityStarterLogger logger,
163             OnUserInteractionCallback onUserInteractionCallback,
164 
165             StatusBar statusBar,
166             NotificationPresenter presenter,
167             NotificationPanelViewController panel,
168             ActivityLaunchAnimator activityLaunchAnimator,
169             NotificationLaunchAnimatorControllerProvider notificationAnimationProvider) {
170         mContext = context;
171         mCommandQueue = commandQueue;
172         mMainThreadHandler = mainThreadHandler;
173         mUiBgExecutor = uiBgExecutor;
174         mEntryManager = entryManager;
175         mNotifPipeline = notifPipeline;
176         mHeadsUpManager = headsUpManager;
177         mActivityStarter = activityStarter;
178         mClickNotifier = clickNotifier;
179         mStatusBarStateController = statusBarStateController;
180         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
181         mKeyguardManager = keyguardManager;
182         mDreamManager = dreamManager;
183         mBubblesManagerOptional = bubblesManagerOptional;
184         mAssistManagerLazy = assistManagerLazy;
185         mRemoteInputManager = remoteInputManager;
186         mGroupMembershipManager = groupMembershipManager;
187         mLockscreenUserManager = lockscreenUserManager;
188         mShadeController = shadeController;
189         mKeyguardStateController = keyguardStateController;
190         mNotificationInterruptStateProvider = notificationInterruptStateProvider;
191         mLockPatternUtils = lockPatternUtils;
192         mStatusBarRemoteInputCallback = remoteInputCallback;
193         mActivityIntentHelper = activityIntentHelper;
194 
195         mFeatureFlags = featureFlags;
196         mMetricsLogger = metricsLogger;
197         mLogger = logger;
198         mOnUserInteractionCallback = onUserInteractionCallback;
199 
200         // TODO: use KeyguardStateController#isOccluded to remove this dependency
201         mStatusBar = statusBar;
202         mPresenter = presenter;
203         mNotificationPanel = panel;
204         mActivityLaunchAnimator = activityLaunchAnimator;
205         mNotificationAnimationProvider = notificationAnimationProvider;
206 
207         if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
208             mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
209                 @Override
210                 public void onPendingEntryAdded(NotificationEntry entry) {
211                     handleFullScreenIntent(entry);
212                 }
213             });
214         } else {
215             mNotifPipeline.addCollectionListener(new NotifCollectionListener() {
216                 @Override
217                 public void onEntryAdded(NotificationEntry entry) {
218                     handleFullScreenIntent(entry);
219                 }
220             });
221         }
222     }
223 
224     /**
225      * Called when a notification is clicked.
226      *
227      * @param sbn notification that was clicked
228      * @param row row for that notification
229      */
230     @Override
onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row)231     public void onNotificationClicked(StatusBarNotification sbn, ExpandableNotificationRow row) {
232         mLogger.logStartingActivityFromClick(sbn.getKey());
233 
234         final NotificationEntry entry = row.getEntry();
235         if (mRemoteInputManager.isRemoteInputActive(entry)
236                 && !TextUtils.isEmpty(row.getActiveRemoteInputText())) {
237             // We have an active remote input typed and the user clicked on the notification.
238             // this was probably unintentional, so we're closing the edit text instead.
239             mRemoteInputManager.closeRemoteInputs();
240             return;
241         }
242         Notification notification = sbn.getNotification();
243         final PendingIntent intent = notification.contentIntent != null
244                 ? notification.contentIntent
245                 : notification.fullScreenIntent;
246         final boolean isBubble = entry.isBubble();
247 
248         // This code path is now executed for notification without a contentIntent.
249         // The only valid case is Bubble notifications. Guard against other cases
250         // entering here.
251         if (intent == null && !isBubble) {
252             mLogger.logNonClickableNotification(sbn.getKey());
253             return;
254         }
255 
256         boolean isActivityIntent = intent != null && intent.isActivity() && !isBubble;
257         final boolean willLaunchResolverActivity = isActivityIntent
258                 && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
259                 mLockscreenUserManager.getCurrentUserId());
260         final boolean animate = !willLaunchResolverActivity
261                 && mStatusBar.shouldAnimateLaunch(isActivityIntent);
262         boolean showOverLockscreen = mKeyguardStateController.isShowing() && intent != null
263                 && mActivityIntentHelper.wouldShowOverLockscreen(intent.getIntent(),
264                 mLockscreenUserManager.getCurrentUserId());
265         ActivityStarter.OnDismissAction postKeyguardAction = new ActivityStarter.OnDismissAction() {
266             @Override
267             public boolean onDismiss() {
268                 return handleNotificationClickAfterKeyguardDismissed(
269                         entry, row, intent, isActivityIntent, animate, showOverLockscreen);
270             }
271 
272             @Override
273             public boolean willRunAnimationOnKeyguard() {
274                 return animate;
275             }
276         };
277         if (showOverLockscreen) {
278             mIsCollapsingToShowActivityOverLockscreen = true;
279             postKeyguardAction.onDismiss();
280         } else {
281             mActivityStarter.dismissKeyguardThenExecute(
282                     postKeyguardAction, null /* cancel */, willLaunchResolverActivity);
283         }
284     }
285 
handleNotificationClickAfterKeyguardDismissed( NotificationEntry entry, ExpandableNotificationRow row, PendingIntent intent, boolean isActivityIntent, boolean animate, boolean showOverLockscreen)286     private boolean handleNotificationClickAfterKeyguardDismissed(
287             NotificationEntry entry,
288             ExpandableNotificationRow row,
289             PendingIntent intent,
290             boolean isActivityIntent,
291             boolean animate,
292             boolean showOverLockscreen) {
293         mLogger.logHandleClickAfterKeyguardDismissed(entry.getKey());
294 
295         final Runnable runnable = () -> handleNotificationClickAfterPanelCollapsed(
296                 entry, row, intent, isActivityIntent, animate);
297 
298         if (showOverLockscreen) {
299             mShadeController.addPostCollapseAction(runnable);
300             mShadeController.collapsePanel(true /* animate */);
301         } else if (mKeyguardStateController.isShowing()
302                 && mStatusBar.isOccluded()) {
303             mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable);
304             mShadeController.collapsePanel();
305         } else {
306             runnable.run();
307         }
308 
309         // Always defer the keyguard dismiss when animating.
310         return animate || !mNotificationPanel.isFullyCollapsed();
311     }
312 
handleNotificationClickAfterPanelCollapsed( NotificationEntry entry, ExpandableNotificationRow row, PendingIntent intent, boolean isActivityIntent, boolean animate)313     private void handleNotificationClickAfterPanelCollapsed(
314             NotificationEntry entry,
315             ExpandableNotificationRow row,
316             PendingIntent intent,
317             boolean isActivityIntent,
318             boolean animate) {
319         String notificationKey = entry.getKey();
320         mLogger.logHandleClickAfterPanelCollapsed(notificationKey);
321 
322         try {
323             // The intent we are sending is for the application, which
324             // won't have permission to immediately start an activity after
325             // the user switches to home.  We know it is safe to do at this
326             // point, so make sure new activity switches are now allowed.
327             ActivityManager.getService().resumeAppSwitches();
328         } catch (RemoteException e) {
329         }
330         // If we are launching a work activity and require to launch
331         // separate work challenge, we defer the activity action and cancel
332         // notification until work challenge is unlocked.
333         if (isActivityIntent) {
334             final int userId = intent.getCreatorUserHandle().getIdentifier();
335             if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
336                     && mKeyguardManager.isDeviceLocked(userId)) {
337                 // TODO(b/28935539): should allow certain activities to
338                 // bypass work challenge
339                 if (mStatusBarRemoteInputCallback.startWorkChallengeIfNecessary(userId,
340                         intent.getIntentSender(), notificationKey)) {
341                     removeHunAfterClick(row);
342                     // Show work challenge, do not run PendingIntent and
343                     // remove notification
344                     collapseOnMainThread();
345                     return;
346                 }
347             }
348         }
349         Intent fillInIntent = null;
350         CharSequence remoteInputText = null;
351         if (!TextUtils.isEmpty(entry.remoteInputText)) {
352             remoteInputText = entry.remoteInputText;
353         }
354         if (!TextUtils.isEmpty(remoteInputText)
355                 && !mRemoteInputManager.isSpinning(notificationKey)) {
356             fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT,
357                     remoteInputText.toString());
358         }
359         final boolean canBubble = entry.canBubble();
360         if (canBubble) {
361             mLogger.logExpandingBubble(notificationKey);
362             removeHunAfterClick(row);
363             expandBubbleStackOnMainThread(entry);
364         } else {
365             startNotificationIntent(intent, fillInIntent, entry, row, animate, isActivityIntent);
366         }
367         if (isActivityIntent || canBubble) {
368             mAssistManagerLazy.get().hideAssist();
369         }
370 
371         NotificationVisibility.NotificationLocation location =
372                 NotificationLogger.getNotificationLocation(entry);
373         final NotificationVisibility nv = NotificationVisibility.obtain(entry.getKey(),
374                 entry.getRanking().getRank(), getVisibleNotificationsCount(), true, location);
375 
376         // retrieve the group summary to remove with this entry before we tell NMS the
377         // notification was clicked to avoid a race condition
378         final boolean shouldAutoCancel = shouldAutoCancel(entry.getSbn());
379         final NotificationEntry summaryToRemove = shouldAutoCancel
380                 ? mOnUserInteractionCallback.getGroupSummaryToDismiss(entry) : null;
381 
382         // inform NMS that the notification was clicked
383         mClickNotifier.onNotificationClick(notificationKey, nv);
384 
385         if (!canBubble) {
386             if (shouldAutoCancel || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(
387                     notificationKey)) {
388                 // Immediately remove notification from visually showing.
389                 // We have to post the removal to the UI thread for synchronization.
390                 mMainThreadHandler.post(() -> {
391                     final Runnable removeNotification = () ->
392                             mOnUserInteractionCallback.onDismiss(
393                                     entry, REASON_CLICK, summaryToRemove);
394                     if (mPresenter.isCollapsing()) {
395                         // To avoid lags we're only performing the remove
396                         // after the shade is collapsed
397                         mShadeController.addPostCollapseAction(removeNotification);
398                     } else {
399                         removeNotification.run();
400                     }
401                 });
402             }
403         }
404 
405         mIsCollapsingToShowActivityOverLockscreen = false;
406     }
407 
408     /**
409      * Called when a notification is dropped on proper target window.
410      * Intent that is included in this entry notification,
411      * will be sent by {@link ExpandableNotificationRowDragController}
412      *
413      * @param entry notification entry that is dropped.
414      */
415     @Override
onDragSuccess(NotificationEntry entry)416     public void onDragSuccess(NotificationEntry entry) {
417         // this method is not responsible for intent sending.
418         // will focus follow operation only after drag-and-drop that notification.
419         NotificationVisibility.NotificationLocation location =
420                 NotificationLogger.getNotificationLocation(entry);
421         final NotificationVisibility nv = NotificationVisibility.obtain(entry.getKey(),
422                 entry.getRanking().getRank(), getVisibleNotificationsCount(), true, location);
423 
424         // retrieve the group summary to remove with this entry before we tell NMS the
425         // notification was clicked to avoid a race condition
426         final boolean shouldAutoCancel = shouldAutoCancel(entry.getSbn());
427         final NotificationEntry summaryToRemove = shouldAutoCancel
428                 ? mOnUserInteractionCallback.getGroupSummaryToDismiss(entry) : null;
429 
430         String notificationKey = entry.getKey();
431         // inform NMS that the notification was clicked
432         mClickNotifier.onNotificationClick(notificationKey, nv);
433 
434         if (shouldAutoCancel || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(
435                 notificationKey)) {
436             // Immediately remove notification from visually showing.
437             // We have to post the removal to the UI thread for synchronization.
438             mMainThreadHandler.post(() -> {
439                 final Runnable removeNotification = () ->
440                         mOnUserInteractionCallback.onDismiss(
441                                 entry, REASON_CLICK, summaryToRemove);
442                 if (mPresenter.isCollapsing()) {
443                     // To avoid lags we're only performing the remove
444                     // after the shade is collapsed
445                     mShadeController.addPostCollapseAction(removeNotification);
446                 } else {
447                     removeNotification.run();
448                 }
449             });
450         }
451 
452         mIsCollapsingToShowActivityOverLockscreen = false;
453     }
454 
expandBubbleStackOnMainThread(NotificationEntry entry)455     private void expandBubbleStackOnMainThread(NotificationEntry entry) {
456         if (!mBubblesManagerOptional.isPresent()) {
457             return;
458         }
459 
460         if (Looper.getMainLooper().isCurrentThread()) {
461             expandBubbleStack(entry);
462         } else {
463             mMainThreadHandler.post(() -> expandBubbleStack(entry));
464         }
465     }
466 
expandBubbleStack(NotificationEntry entry)467     private void expandBubbleStack(NotificationEntry entry) {
468         mBubblesManagerOptional.get().expandStackAndSelectBubble(entry);
469         mShadeController.collapsePanel();
470     }
471 
startNotificationIntent( PendingIntent intent, Intent fillInIntent, NotificationEntry entry, ExpandableNotificationRow row, boolean animate, boolean isActivityIntent)472     private void startNotificationIntent(
473             PendingIntent intent,
474             Intent fillInIntent,
475             NotificationEntry entry,
476             ExpandableNotificationRow row,
477             boolean animate,
478             boolean isActivityIntent) {
479         mLogger.logStartNotificationIntent(entry.getKey(), intent);
480         try {
481             ActivityLaunchAnimator.Controller animationController =
482                     new StatusBarLaunchAnimatorController(
483                             mNotificationAnimationProvider.getAnimatorController(row), mStatusBar,
484                             isActivityIntent);
485 
486             mActivityLaunchAnimator.startPendingIntentWithAnimation(animationController,
487                     animate, intent.getCreatorPackage(), (adapter) -> {
488                         long eventTime = row.getAndResetLastActionUpTime();
489                         Bundle options = eventTime > 0
490                                 ? getActivityOptions(
491                                 mStatusBar.getDisplayId(),
492                                 adapter,
493                                 mKeyguardStateController.isShowing(),
494                                 eventTime)
495                                 : getActivityOptions(mStatusBar.getDisplayId(), adapter);
496                         return intent.sendAndReturnResult(mContext, 0, fillInIntent, null,
497                                 null, null, options);
498                     });
499         } catch (PendingIntent.CanceledException e) {
500             // the stack trace isn't very helpful here.
501             // Just log the exception message.
502             mLogger.logSendingIntentFailed(e);
503             // TODO: Dismiss Keyguard.
504         }
505     }
506 
507     @Override
startNotificationGutsIntent(final Intent intent, final int appUid, ExpandableNotificationRow row)508     public void startNotificationGutsIntent(final Intent intent, final int appUid,
509             ExpandableNotificationRow row) {
510         boolean animate = mStatusBar.shouldAnimateLaunch(true /* isActivityIntent */);
511         ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() {
512             @Override
513             public boolean onDismiss() {
514                 AsyncTask.execute(() -> {
515                     ActivityLaunchAnimator.Controller animationController =
516                             new StatusBarLaunchAnimatorController(
517                                     mNotificationAnimationProvider.getAnimatorController(row),
518                                     mStatusBar, true /* isActivityIntent */);
519 
520                     mActivityLaunchAnimator.startIntentWithAnimation(
521                             animationController, animate, intent.getPackage(),
522                             (adapter) -> TaskStackBuilder.create(mContext)
523                                     .addNextIntentWithParentStack(intent)
524                                     .startActivities(getActivityOptions(
525                                             mStatusBar.getDisplayId(),
526                                             adapter),
527                                             new UserHandle(UserHandle.getUserId(appUid))));
528                 });
529                 return true;
530             }
531 
532             @Override
533             public boolean willRunAnimationOnKeyguard() {
534                 return animate;
535             }
536         };
537         mActivityStarter.dismissKeyguardThenExecute(onDismissAction, null,
538                 false /* afterKeyguardGone */);
539     }
540 
541     @Override
startHistoryIntent(View view, boolean showHistory)542     public void startHistoryIntent(View view, boolean showHistory) {
543         boolean animate = mStatusBar.shouldAnimateLaunch(true /* isActivityIntent */);
544         ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() {
545             @Override
546             public boolean onDismiss() {
547                 AsyncTask.execute(() -> {
548                     Intent intent = showHistory ? new Intent(
549                             Settings.ACTION_NOTIFICATION_HISTORY) : new Intent(
550                             Settings.ACTION_NOTIFICATION_SETTINGS);
551                     TaskStackBuilder tsb = TaskStackBuilder.create(mContext)
552                             .addNextIntent(new Intent(Settings.ACTION_NOTIFICATION_SETTINGS));
553                     if (showHistory) {
554                         tsb.addNextIntent(intent);
555                     }
556 
557                     ActivityLaunchAnimator.Controller viewController =
558                             ActivityLaunchAnimator.Controller.fromView(view,
559                                     InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON
560                             );
561                     ActivityLaunchAnimator.Controller animationController =
562                             viewController == null ? null
563                                 : new StatusBarLaunchAnimatorController(viewController, mStatusBar,
564                                     true /* isActivityIntent */);
565 
566                     mActivityLaunchAnimator.startIntentWithAnimation(animationController, animate,
567                             intent.getPackage(),
568                             (adapter) -> tsb.startActivities(
569                                     getActivityOptions(mStatusBar.getDisplayId(), adapter),
570                                     UserHandle.CURRENT));
571                 });
572                 return true;
573             }
574 
575             @Override
576             public boolean willRunAnimationOnKeyguard() {
577                 return animate;
578             }
579         };
580         mActivityStarter.dismissKeyguardThenExecute(onDismissAction, null,
581                 false /* afterKeyguardGone */);
582     }
583 
removeHunAfterClick(ExpandableNotificationRow row)584     private void removeHunAfterClick(ExpandableNotificationRow row) {
585         String key = row.getEntry().getSbn().getKey();
586         if (mHeadsUpManager != null && mHeadsUpManager.isAlerting(key)) {
587             // Release the HUN notification to the shade.
588             if (mPresenter.isPresenterFullyCollapsed()) {
589                 HeadsUpUtil.setNeedsHeadsUpDisappearAnimationAfterClick(row, true);
590             }
591 
592             // In most cases, when FLAG_AUTO_CANCEL is set, the notification will
593             // become canceled shortly by NoMan, but we can't assume that.
594             mHeadsUpManager.removeNotification(key, true /* releaseImmediately */);
595         }
596     }
597 
598     @VisibleForTesting
handleFullScreenIntent(NotificationEntry entry)599     void handleFullScreenIntent(NotificationEntry entry) {
600         if (mNotificationInterruptStateProvider.shouldLaunchFullScreenIntentWhenAdded(entry)) {
601             if (shouldSuppressFullScreenIntent(entry)) {
602                 mLogger.logFullScreenIntentSuppressedByDnD(entry.getKey());
603             } else if (entry.getImportance() < NotificationManager.IMPORTANCE_HIGH) {
604                 mLogger.logFullScreenIntentNotImportantEnough(entry.getKey());
605             } else {
606                 // Stop screensaver if the notification has a fullscreen intent.
607                 // (like an incoming phone call)
608                 mUiBgExecutor.execute(() -> {
609                     try {
610                         mDreamManager.awaken();
611                     } catch (RemoteException e) {
612                         e.printStackTrace();
613                     }
614                 });
615 
616                 // not immersive & a fullscreen alert should be shown
617                 final PendingIntent fullscreenIntent =
618                         entry.getSbn().getNotification().fullScreenIntent;
619                 mLogger.logSendingFullScreenIntent(entry.getKey(), fullscreenIntent);
620                 try {
621                     EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION,
622                             entry.getKey());
623                     mStatusBar.wakeUpForFullScreenIntent();
624                     fullscreenIntent.send();
625                     entry.notifyFullScreenIntentLaunched();
626                     mMetricsLogger.count("note_fullscreen", 1);
627                 } catch (PendingIntent.CanceledException e) {
628                     // ignore
629                 }
630             }
631         }
632     }
633 
634     @Override
isCollapsingToShowActivityOverLockscreen()635     public boolean isCollapsingToShowActivityOverLockscreen() {
636         return mIsCollapsingToShowActivityOverLockscreen;
637     }
638 
shouldAutoCancel(StatusBarNotification sbn)639     private static boolean shouldAutoCancel(StatusBarNotification sbn) {
640         int flags = sbn.getNotification().flags;
641         if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) {
642             return false;
643         }
644         if ((flags & Notification.FLAG_FOREGROUND_SERVICE) != 0) {
645             return false;
646         }
647         return true;
648     }
649 
collapseOnMainThread()650     private void collapseOnMainThread() {
651         if (Looper.getMainLooper().isCurrentThread()) {
652             mShadeController.collapsePanel();
653         } else {
654             mMainThreadHandler.post(mShadeController::collapsePanel);
655         }
656     }
657 
shouldSuppressFullScreenIntent(NotificationEntry entry)658     private boolean shouldSuppressFullScreenIntent(NotificationEntry entry) {
659         if (mPresenter.isDeviceInVrMode()) {
660             return true;
661         }
662 
663         return entry.shouldSuppressFullScreenIntent();
664     }
665 
666     // --------------------- NotificationEntryManager/NotifPipeline methods ------------------------
667 
getVisibleNotificationsCount()668     private int getVisibleNotificationsCount() {
669         if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
670             return mNotifPipeline.getShadeListCount();
671         } else {
672             return mEntryManager.getActiveNotificationsCount();
673         }
674     }
675 
676     /**
677      * Public builder for {@link StatusBarNotificationActivityStarter}.
678      */
679     @SysUISingleton
680     public static class Builder {
681         private final Context mContext;
682         private final CommandQueue mCommandQueue;
683         private final Handler mMainThreadHandler;
684 
685         private final Executor mUiBgExecutor;
686         private final NotificationEntryManager mEntryManager;
687         private final NotifPipeline mNotifPipeline;
688         private final HeadsUpManagerPhone mHeadsUpManager;
689         private final ActivityStarter mActivityStarter;
690         private final NotificationClickNotifier mClickNotifier;
691         private final StatusBarStateController mStatusBarStateController;
692         private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
693         private final KeyguardManager mKeyguardManager;
694         private final IDreamManager mDreamManager;
695         private final Optional<BubblesManager> mBubblesManagerOptional;
696         private final Lazy<AssistManager> mAssistManagerLazy;
697         private final NotificationRemoteInputManager mRemoteInputManager;
698         private final GroupMembershipManager mGroupMembershipManager;
699         private final NotificationLockscreenUserManager mLockscreenUserManager;
700         private final ShadeController mShadeController;
701         private final KeyguardStateController mKeyguardStateController;
702         private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
703         private final LockPatternUtils mLockPatternUtils;
704         private final StatusBarRemoteInputCallback mRemoteInputCallback;
705         private final ActivityIntentHelper mActivityIntentHelper;
706 
707         private final FeatureFlags mFeatureFlags;
708         private final MetricsLogger mMetricsLogger;
709         private final StatusBarNotificationActivityStarterLogger mLogger;
710         private final OnUserInteractionCallback mOnUserInteractionCallback;
711 
712         private StatusBar mStatusBar;
713         private NotificationPresenter mNotificationPresenter;
714         private NotificationPanelViewController mNotificationPanelViewController;
715         private ActivityLaunchAnimator mActivityLaunchAnimator;
716         private NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider;
717 
718         @Inject
Builder( Context context, CommandQueue commandQueue, @Main Handler mainThreadHandler, @UiBackground Executor uiBgExecutor, NotificationEntryManager entryManager, NotifPipeline notifPipeline, HeadsUpManagerPhone headsUpManager, ActivityStarter activityStarter, NotificationClickNotifier clickNotifier, StatusBarStateController statusBarStateController, StatusBarKeyguardViewManager statusBarKeyguardViewManager, KeyguardManager keyguardManager, IDreamManager dreamManager, Optional<BubblesManager> bubblesManager, Lazy<AssistManager> assistManagerLazy, NotificationRemoteInputManager remoteInputManager, GroupMembershipManager groupMembershipManager, NotificationLockscreenUserManager lockscreenUserManager, ShadeController shadeController, KeyguardStateController keyguardStateController, NotificationInterruptStateProvider notificationInterruptStateProvider, LockPatternUtils lockPatternUtils, StatusBarRemoteInputCallback remoteInputCallback, ActivityIntentHelper activityIntentHelper, FeatureFlags featureFlags, MetricsLogger metricsLogger, StatusBarNotificationActivityStarterLogger logger, OnUserInteractionCallback onUserInteractionCallback)719         public Builder(
720                 Context context,
721                 CommandQueue commandQueue,
722                 @Main Handler mainThreadHandler,
723                 @UiBackground Executor uiBgExecutor,
724                 NotificationEntryManager entryManager,
725                 NotifPipeline notifPipeline,
726                 HeadsUpManagerPhone headsUpManager,
727                 ActivityStarter activityStarter,
728                 NotificationClickNotifier clickNotifier,
729                 StatusBarStateController statusBarStateController,
730                 StatusBarKeyguardViewManager statusBarKeyguardViewManager,
731                 KeyguardManager keyguardManager,
732                 IDreamManager dreamManager,
733                 Optional<BubblesManager> bubblesManager,
734                 Lazy<AssistManager> assistManagerLazy,
735                 NotificationRemoteInputManager remoteInputManager,
736                 GroupMembershipManager groupMembershipManager,
737                 NotificationLockscreenUserManager lockscreenUserManager,
738                 ShadeController shadeController,
739                 KeyguardStateController keyguardStateController,
740                 NotificationInterruptStateProvider notificationInterruptStateProvider,
741                 LockPatternUtils lockPatternUtils,
742                 StatusBarRemoteInputCallback remoteInputCallback,
743                 ActivityIntentHelper activityIntentHelper,
744 
745                 FeatureFlags featureFlags,
746                 MetricsLogger metricsLogger,
747                 StatusBarNotificationActivityStarterLogger logger,
748                 OnUserInteractionCallback onUserInteractionCallback) {
749 
750             mContext = context;
751             mCommandQueue = commandQueue;
752             mMainThreadHandler = mainThreadHandler;
753             mUiBgExecutor = uiBgExecutor;
754             mEntryManager = entryManager;
755             mNotifPipeline = notifPipeline;
756             mHeadsUpManager = headsUpManager;
757             mActivityStarter = activityStarter;
758             mClickNotifier = clickNotifier;
759             mStatusBarStateController = statusBarStateController;
760             mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
761             mKeyguardManager = keyguardManager;
762             mDreamManager = dreamManager;
763             mBubblesManagerOptional = bubblesManager;
764             mAssistManagerLazy = assistManagerLazy;
765             mRemoteInputManager = remoteInputManager;
766             mGroupMembershipManager = groupMembershipManager;
767             mLockscreenUserManager = lockscreenUserManager;
768             mShadeController = shadeController;
769             mKeyguardStateController = keyguardStateController;
770             mNotificationInterruptStateProvider = notificationInterruptStateProvider;
771             mLockPatternUtils = lockPatternUtils;
772             mRemoteInputCallback = remoteInputCallback;
773             mActivityIntentHelper = activityIntentHelper;
774 
775             mFeatureFlags = featureFlags;
776             mMetricsLogger = metricsLogger;
777             mLogger = logger;
778             mOnUserInteractionCallback = onUserInteractionCallback;
779         }
780 
781         /** Sets the status bar to use as {@link StatusBar}. */
setStatusBar(StatusBar statusBar)782         public Builder setStatusBar(StatusBar statusBar) {
783             mStatusBar = statusBar;
784             return this;
785         }
786 
setNotificationPresenter(NotificationPresenter notificationPresenter)787         public Builder setNotificationPresenter(NotificationPresenter notificationPresenter) {
788             mNotificationPresenter = notificationPresenter;
789             return this;
790         }
791 
792         /** Set the ActivityLaunchAnimator. */
setActivityLaunchAnimator(ActivityLaunchAnimator activityLaunchAnimator)793         public Builder setActivityLaunchAnimator(ActivityLaunchAnimator activityLaunchAnimator) {
794             mActivityLaunchAnimator = activityLaunchAnimator;
795             return this;
796         }
797 
798         /** Set the NotificationLaunchAnimatorControllerProvider. */
setNotificationAnimatorControllerProvider( NotificationLaunchAnimatorControllerProvider notificationAnimationProvider)799         public Builder setNotificationAnimatorControllerProvider(
800                 NotificationLaunchAnimatorControllerProvider notificationAnimationProvider) {
801             mNotificationAnimationProvider = notificationAnimationProvider;
802             return this;
803         }
804 
805         /** Set the NotificationPanelViewController. */
setNotificationPanelViewController( NotificationPanelViewController notificationPanelViewController)806         public Builder setNotificationPanelViewController(
807                 NotificationPanelViewController notificationPanelViewController) {
808             mNotificationPanelViewController = notificationPanelViewController;
809             return this;
810         }
811 
build()812         public StatusBarNotificationActivityStarter build() {
813             return new StatusBarNotificationActivityStarter(
814                     mContext,
815                     mCommandQueue,
816                     mMainThreadHandler,
817                     mUiBgExecutor,
818                     mEntryManager,
819                     mNotifPipeline,
820                     mHeadsUpManager,
821                     mActivityStarter,
822                     mClickNotifier,
823                     mStatusBarStateController,
824                     mStatusBarKeyguardViewManager,
825                     mKeyguardManager,
826                     mDreamManager,
827                     mBubblesManagerOptional,
828                     mAssistManagerLazy,
829                     mRemoteInputManager,
830                     mGroupMembershipManager,
831                     mLockscreenUserManager,
832                     mShadeController,
833                     mKeyguardStateController,
834                     mNotificationInterruptStateProvider,
835                     mLockPatternUtils,
836                     mRemoteInputCallback,
837                     mActivityIntentHelper,
838                     mFeatureFlags,
839                     mMetricsLogger,
840                     mLogger,
841                     mOnUserInteractionCallback,
842                     mStatusBar,
843                     mNotificationPresenter,
844                     mNotificationPanelViewController,
845                     mActivityLaunchAnimator,
846                     mNotificationAnimationProvider);
847         }
848     }
849 }
850