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