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