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