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