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.NonNull; 47 import androidx.annotation.VisibleForTesting; 48 49 import com.android.internal.jank.InteractionJankMonitor; 50 import com.android.internal.logging.MetricsLogger; 51 import com.android.internal.statusbar.NotificationVisibility; 52 import com.android.internal.util.FrameworkStatsLog; 53 import com.android.internal.widget.LockPatternUtils; 54 import com.android.systemui.ActivityIntentHelper; 55 import com.android.systemui.EventLogTags; 56 import com.android.systemui.animation.ActivityTransitionAnimator; 57 import com.android.systemui.assist.AssistManager; 58 import com.android.systemui.dagger.SysUISingleton; 59 import com.android.systemui.dagger.qualifiers.Background; 60 import com.android.systemui.dagger.qualifiers.Main; 61 import com.android.systemui.plugins.ActivityStarter; 62 import com.android.systemui.power.domain.interactor.PowerInteractor; 63 import com.android.systemui.settings.UserTracker; 64 import com.android.systemui.shade.ShadeController; 65 import com.android.systemui.shade.ShadeDisplayAware; 66 import com.android.systemui.shade.domain.interactor.PanelExpansionInteractor; 67 import com.android.systemui.shade.domain.interactor.ShadeAnimationInteractor; 68 import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor; 69 import com.android.systemui.statusbar.CommandQueue; 70 import com.android.systemui.statusbar.NotificationClickNotifier; 71 import com.android.systemui.statusbar.NotificationLockscreenUserManager; 72 import com.android.systemui.statusbar.NotificationPresenter; 73 import com.android.systemui.statusbar.NotificationRemoteInputManager; 74 import com.android.systemui.statusbar.NotificationShadeWindowController; 75 import com.android.systemui.statusbar.notification.NotificationActivityStarter; 76 import com.android.systemui.statusbar.notification.NotificationLaunchAnimatorControllerProvider; 77 import com.android.systemui.statusbar.notification.collection.NotificationEntry; 78 import com.android.systemui.statusbar.notification.collection.provider.LaunchFullScreenIntentProvider; 79 import com.android.systemui.statusbar.notification.collection.render.NotificationVisibilityProvider; 80 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix; 81 import com.android.systemui.statusbar.notification.headsup.HeadsUpManager; 82 import com.android.systemui.statusbar.notification.headsup.HeadsUpUtil; 83 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; 84 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowDragController; 85 import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback; 86 import com.android.systemui.statusbar.policy.KeyguardStateController; 87 import com.android.systemui.wmshell.BubblesManager; 88 89 import dagger.Lazy; 90 91 import java.util.List; 92 import java.util.Optional; 93 import java.util.concurrent.Executor; 94 95 import javax.inject.Inject; 96 97 /** 98 * Status bar implementation of {@link NotificationActivityStarter}. 99 */ 100 @SysUISingleton 101 public class StatusBarNotificationActivityStarter implements NotificationActivityStarter { 102 103 /** 104 * Helps to avoid recalculation of values provided to 105 * {@link #onDismiss(PendingIntent, boolean, boolean, boolean)}} method 106 */ 107 private interface OnKeyguardDismissedAction { 108 /** 109 * Invoked when keyguard is dismissed 110 * 111 * @return is used as return value for {@link ActivityStarter.OnDismissAction#onDismiss()} 112 */ onDismiss(PendingIntent intent, boolean isActivityIntent, boolean animate, boolean showOverTheLockScreen)113 boolean onDismiss(PendingIntent intent, boolean isActivityIntent, boolean animate, 114 boolean showOverTheLockScreen); 115 } 116 117 private final static String TAG = "StatusBarNotificationActivityStarter"; 118 119 private final Context mContext; 120 private final ShadeDialogContextInteractor mContextInteractor; 121 122 private final Handler mMainThreadHandler; 123 private final Executor mUiBgExecutor; 124 125 private final NotificationVisibilityProvider mVisibilityProvider; 126 private final HeadsUpManager mHeadsUpManager; 127 private final ActivityStarter mActivityStarter; 128 private final CommandQueue mCommandQueue; 129 private final NotificationClickNotifier mClickNotifier; 130 private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; 131 private final KeyguardManager mKeyguardManager; 132 private final IDreamManager mDreamManager; 133 private final Optional<BubblesManager> mBubblesManagerOptional; 134 private final Lazy<AssistManager> mAssistManagerLazy; 135 private final NotificationRemoteInputManager mRemoteInputManager; 136 private final NotificationLockscreenUserManager mLockscreenUserManager; 137 private final ShadeController mShadeController; 138 private final KeyguardStateController mKeyguardStateController; 139 private final LockPatternUtils mLockPatternUtils; 140 private final StatusBarRemoteInputCallback mStatusBarRemoteInputCallback; 141 private final ActivityIntentHelper mActivityIntentHelper; 142 private final ShadeAnimationInteractor mShadeAnimationInteractor; 143 144 private final MetricsLogger mMetricsLogger; 145 private final StatusBarNotificationActivityStarterLogger mLogger; 146 147 private final NotificationPresenter mPresenter; 148 private final PanelExpansionInteractor mPanelExpansionInteractor; 149 private final NotificationShadeWindowController mNotificationShadeWindowController; 150 private final ActivityTransitionAnimator mActivityTransitionAnimator; 151 private final NotificationLaunchAnimatorControllerProvider mNotificationAnimationProvider; 152 private final PowerInteractor mPowerInteractor; 153 private final UserTracker mUserTracker; 154 private final OnUserInteractionCallback mOnUserInteractionCallback; 155 156 private boolean mIsCollapsingToShowActivityOverLockscreen; 157 158 @Inject StatusBarNotificationActivityStarter( @hadeDisplayAware Context context, ShadeDialogContextInteractor contextInteractor, @Main Handler mainThreadHandler, @Background Executor uiBgExecutor, NotificationVisibilityProvider visibilityProvider, HeadsUpManager headsUpManager, ActivityStarter activityStarter, CommandQueue commandQueue, NotificationClickNotifier clickNotifier, StatusBarKeyguardViewManager statusBarKeyguardViewManager, KeyguardManager keyguardManager, IDreamManager dreamManager, Optional<BubblesManager> bubblesManagerOptional, Lazy<AssistManager> assistManagerLazy, NotificationRemoteInputManager remoteInputManager, NotificationLockscreenUserManager lockscreenUserManager, ShadeController shadeController, KeyguardStateController keyguardStateController, LockPatternUtils lockPatternUtils, StatusBarRemoteInputCallback remoteInputCallback, ActivityIntentHelper activityIntentHelper, MetricsLogger metricsLogger, StatusBarNotificationActivityStarterLogger logger, OnUserInteractionCallback onUserInteractionCallback, NotificationPresenter presenter, PanelExpansionInteractor panelExpansionInteractor, NotificationShadeWindowController notificationShadeWindowController, ActivityTransitionAnimator activityTransitionAnimator, ShadeAnimationInteractor shadeAnimationInteractor, NotificationLaunchAnimatorControllerProvider notificationAnimationProvider, LaunchFullScreenIntentProvider launchFullScreenIntentProvider, PowerInteractor powerInteractor, UserTracker userTracker)159 StatusBarNotificationActivityStarter( 160 @ShadeDisplayAware Context context, 161 ShadeDialogContextInteractor contextInteractor, 162 @Main Handler mainThreadHandler, 163 @Background Executor uiBgExecutor, 164 NotificationVisibilityProvider visibilityProvider, 165 HeadsUpManager headsUpManager, 166 ActivityStarter activityStarter, 167 CommandQueue commandQueue, 168 NotificationClickNotifier clickNotifier, 169 StatusBarKeyguardViewManager statusBarKeyguardViewManager, 170 KeyguardManager keyguardManager, 171 IDreamManager dreamManager, 172 Optional<BubblesManager> bubblesManagerOptional, 173 Lazy<AssistManager> assistManagerLazy, 174 NotificationRemoteInputManager remoteInputManager, 175 NotificationLockscreenUserManager lockscreenUserManager, 176 ShadeController shadeController, 177 KeyguardStateController keyguardStateController, 178 LockPatternUtils lockPatternUtils, 179 StatusBarRemoteInputCallback remoteInputCallback, 180 ActivityIntentHelper activityIntentHelper, 181 MetricsLogger metricsLogger, 182 StatusBarNotificationActivityStarterLogger logger, 183 OnUserInteractionCallback onUserInteractionCallback, 184 NotificationPresenter presenter, 185 PanelExpansionInteractor panelExpansionInteractor, 186 NotificationShadeWindowController notificationShadeWindowController, 187 ActivityTransitionAnimator activityTransitionAnimator, 188 ShadeAnimationInteractor shadeAnimationInteractor, 189 NotificationLaunchAnimatorControllerProvider notificationAnimationProvider, 190 LaunchFullScreenIntentProvider launchFullScreenIntentProvider, 191 PowerInteractor powerInteractor, 192 UserTracker userTracker) { 193 mContext = context; 194 mContextInteractor = contextInteractor; 195 mMainThreadHandler = mainThreadHandler; 196 mUiBgExecutor = uiBgExecutor; 197 mVisibilityProvider = visibilityProvider; 198 mHeadsUpManager = headsUpManager; 199 mActivityStarter = activityStarter; 200 mCommandQueue = commandQueue; 201 mClickNotifier = clickNotifier; 202 mStatusBarKeyguardViewManager = statusBarKeyguardViewManager; 203 mKeyguardManager = keyguardManager; 204 mDreamManager = dreamManager; 205 mBubblesManagerOptional = bubblesManagerOptional; 206 mAssistManagerLazy = assistManagerLazy; 207 mRemoteInputManager = remoteInputManager; 208 mLockscreenUserManager = lockscreenUserManager; 209 mShadeController = shadeController; 210 mKeyguardStateController = keyguardStateController; 211 mLockPatternUtils = lockPatternUtils; 212 mStatusBarRemoteInputCallback = remoteInputCallback; 213 mActivityIntentHelper = activityIntentHelper; 214 mPanelExpansionInteractor = panelExpansionInteractor; 215 mNotificationShadeWindowController = notificationShadeWindowController; 216 mShadeAnimationInteractor = shadeAnimationInteractor; 217 mMetricsLogger = metricsLogger; 218 mLogger = logger; 219 mOnUserInteractionCallback = onUserInteractionCallback; 220 mPresenter = presenter; 221 mActivityTransitionAnimator = activityTransitionAnimator; 222 mNotificationAnimationProvider = notificationAnimationProvider; 223 mPowerInteractor = powerInteractor; 224 mUserTracker = userTracker; 225 226 launchFullScreenIntentProvider.registerListener(entry -> launchFullScreenIntent(entry)); 227 } 228 229 /** 230 * Called when the user clicks on the notification bubble icon. 231 * 232 * @param entry notification that bubble icon was clicked 233 */ 234 @Override onNotificationBubbleIconClicked(@onNull NotificationEntry entry)235 public void onNotificationBubbleIconClicked(@NonNull NotificationEntry entry) { 236 Runnable action = () -> { 237 mBubblesManagerOptional.ifPresent(bubblesManager -> 238 bubblesManager.onUserChangedBubble(entry, !entry.isBubble())); 239 mHeadsUpManager.removeNotification(entry.getKey(), /* releaseImmediately= */ true, 240 /* reason= */ "onNotificationBubbleIconClicked"); 241 }; 242 if (entry.isBubble()) { 243 // entry is being un-bubbled, no need to unlock 244 action.run(); 245 } else { 246 performActionAfterKeyguardDismissed(entry, 247 (intent, isActivityIntent, animate, showOverTheLockScreen) -> { 248 action.run(); 249 return false; 250 }); 251 } 252 } 253 254 /** 255 * Called when a notification is clicked. 256 * 257 * @param entry notification that was clicked 258 * @param row row for that notification 259 */ 260 @Override onNotificationClicked(@onNull NotificationEntry entry, @NonNull ExpandableNotificationRow row)261 public void onNotificationClicked(@NonNull NotificationEntry entry, 262 @NonNull ExpandableNotificationRow row) { 263 mLogger.logStartingActivityFromClick(entry, row.isHeadsUpState(), 264 mKeyguardStateController.isVisible(), 265 mNotificationShadeWindowController.getPanelExpanded()); 266 OnKeyguardDismissedAction action = 267 (intent, isActivityIntent, animate, showOverTheLockScreen) -> 268 performActionOnKeyguardDismissed(entry, row, intent, isActivityIntent, 269 animate, showOverTheLockScreen); 270 performActionAfterKeyguardDismissed(entry, action); 271 } 272 performActionAfterKeyguardDismissed(NotificationEntry entry, OnKeyguardDismissedAction action)273 private void performActionAfterKeyguardDismissed(NotificationEntry entry, 274 OnKeyguardDismissedAction action) { 275 if (mRemoteInputManager.isRemoteInputActive(entry)) { 276 // We have an active remote input typed and the user clicked on the notification. 277 // this was probably unintentional, so we're closing the edit text instead. 278 mRemoteInputManager.closeRemoteInputs(); 279 mLogger.logCloseRemoteInput(entry); 280 return; 281 } 282 Notification notification = entry.getSbn().getNotification(); 283 final PendingIntent intent = notification.contentIntent != null 284 ? notification.contentIntent 285 : notification.fullScreenIntent; 286 final boolean isBubble = entry.isBubble(); 287 288 // This code path is now executed for notification without a contentIntent. 289 // The only valid case is Bubble notifications. Guard against other cases 290 // entering here. 291 if (intent == null && !isBubble) { 292 mLogger.logNonClickableNotification(entry); 293 return; 294 } 295 296 boolean isActivityIntent = intent != null && intent.isActivity() && !isBubble; 297 final boolean willLaunchResolverActivity = isActivityIntent 298 && mActivityIntentHelper.wouldPendingLaunchResolverActivity(intent, 299 mLockscreenUserManager.getCurrentUserId()); 300 final boolean animate = !willLaunchResolverActivity 301 && mActivityStarter.shouldAnimateLaunch(isActivityIntent); 302 boolean showOverLockscreen = mKeyguardStateController.isShowing() && intent != null 303 && mActivityIntentHelper.wouldPendingShowOverLockscreen(intent, 304 mLockscreenUserManager.getCurrentUserId()); 305 ActivityStarter.OnDismissAction postKeyguardAction = new ActivityStarter.OnDismissAction() { 306 @Override 307 public boolean onDismiss() { 308 return action.onDismiss(intent, isActivityIntent, animate, showOverLockscreen); 309 } 310 311 @Override 312 public boolean willRunAnimationOnKeyguard() { 313 return animate; 314 } 315 }; 316 if (showOverLockscreen) { 317 mIsCollapsingToShowActivityOverLockscreen = true; 318 postKeyguardAction.onDismiss(); 319 } else { 320 mActivityStarter.dismissKeyguardThenExecute( 321 postKeyguardAction, 322 null, 323 willLaunchResolverActivity); 324 } 325 } 326 performActionOnKeyguardDismissed( NotificationEntry entry, ExpandableNotificationRow row, PendingIntent intent, boolean isActivityIntent, boolean animate, boolean showOverLockscreen)327 private boolean performActionOnKeyguardDismissed( 328 NotificationEntry entry, 329 ExpandableNotificationRow row, 330 PendingIntent intent, 331 boolean isActivityIntent, 332 boolean animate, 333 boolean showOverLockscreen) { 334 mLogger.logHandleClickAfterKeyguardDismissed(entry); 335 336 final Runnable runnable = () -> handleNotificationClickAfterPanelCollapsed( 337 entry, row, intent, isActivityIntent, animate); 338 if (showOverLockscreen) { 339 mShadeController.addPostCollapseAction(runnable); 340 mShadeController.collapseShade(true /* animate */); 341 } else if (mKeyguardStateController.isShowing() 342 && mKeyguardStateController.isOccluded()) { 343 mStatusBarKeyguardViewManager.addAfterKeyguardGoneRunnable(runnable); 344 mShadeController.collapseShade(); 345 } else { 346 runnable.run(); 347 } 348 349 // Always defer the keyguard dismiss when animating. 350 return animate || !mPanelExpansionInteractor.isFullyCollapsed(); 351 } 352 handleNotificationClickAfterPanelCollapsed( NotificationEntry entry, ExpandableNotificationRow row, PendingIntent intent, boolean isActivityIntent, boolean animate)353 private void handleNotificationClickAfterPanelCollapsed( 354 NotificationEntry entry, 355 ExpandableNotificationRow row, 356 PendingIntent intent, 357 boolean isActivityIntent, 358 boolean animate) { 359 String notificationKey = entry.getKey(); 360 mLogger.logHandleClickAfterPanelCollapsed(entry); 361 362 try { 363 // The intent we are sending is for the application, which 364 // won't have permission to immediately start an activity after 365 // the user switches to home. We know it is safe to do at this 366 // point, so make sure new activity switches are now allowed. 367 ActivityManager.getService().resumeAppSwitches(); 368 } catch (RemoteException e) { 369 } 370 // If the notification should be cancelled on click and we are launching a work activity in 371 // a locked profile with separate challenge, we defer the activity action and cancelling of 372 // the notification until work challenge is unlocked. If the notification shouldn't be 373 // cancelled, the work challenge will be shown by ActivityManager if necessary anyway. 374 if (isActivityIntent && shouldAutoCancel(entry.getSbn())) { 375 final int userId = intent.getCreatorUserHandle().getIdentifier(); 376 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId) 377 && mKeyguardManager.isDeviceLocked(userId)) { 378 // TODO(b/28935539): should allow certain activities to 379 // bypass work challenge 380 if (mStatusBarRemoteInputCallback.startWorkChallengeIfNecessary(userId, 381 intent.getIntentSender(), notificationKey)) { 382 removeHunAfterClick(row); 383 // Show work challenge, do not run PendingIntent and 384 // remove notification 385 mShadeController.collapseOnMainThread(); 386 return; 387 } 388 } 389 } 390 Intent fillInIntent = null; 391 CharSequence remoteInputText = null; 392 if (!TextUtils.isEmpty(entry.remoteInputText)) { 393 remoteInputText = entry.remoteInputText; 394 } 395 if (!TextUtils.isEmpty(remoteInputText) 396 && !mRemoteInputManager.isSpinning(notificationKey)) { 397 fillInIntent = new Intent().putExtra(Notification.EXTRA_REMOTE_INPUT_DRAFT, 398 remoteInputText.toString()); 399 } 400 final boolean canBubble = entry.canBubble(); 401 if (canBubble) { 402 mLogger.logExpandingBubble(entry); 403 removeHunAfterClick(row); 404 expandBubbleStackOnMainThread(entry); 405 } else { 406 startNotificationIntent(intent, fillInIntent, entry, row, animate, isActivityIntent); 407 } 408 if (isActivityIntent || canBubble) { 409 mAssistManagerLazy.get().hideAssist(); 410 } 411 412 final NotificationVisibility nv = mVisibilityProvider.obtain(entry, true); 413 414 if (!canBubble && (shouldAutoCancel(entry.getSbn()) 415 || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(notificationKey))) { 416 final Runnable removeNotification = 417 mOnUserInteractionCallback.registerFutureDismissal(entry, REASON_CLICK); 418 // Immediately remove notification from visually showing. 419 // We have to post the removal to the UI thread for synchronization. 420 mMainThreadHandler.post(() -> { 421 if (mPresenter.isCollapsing()) { 422 // To avoid lags we're only performing the remove after the shade is collapsed 423 mShadeController.addPostCollapseAction(removeNotification); 424 } else { 425 removeNotification.run(); 426 } 427 }); 428 } 429 430 // inform NMS that the notification was clicked 431 mClickNotifier.onNotificationClick(notificationKey, nv); 432 433 mIsCollapsingToShowActivityOverLockscreen = false; 434 } 435 436 /** 437 * Called when a notification is dropped on proper target window. 438 * Intent that is included in this entry notification, 439 * will be sent by {@link ExpandableNotificationRowDragController} 440 * 441 * @param entry notification entry that is dropped. 442 */ 443 @Override onDragSuccess(@onNull NotificationEntry entry)444 public void onDragSuccess(@NonNull NotificationEntry entry) { 445 // this method is not responsible for intent sending. 446 // will focus follow operation only after drag-and-drop that notification. 447 final NotificationVisibility nv = mVisibilityProvider.obtain(entry, true); 448 449 String notificationKey = entry.getKey(); 450 if (shouldAutoCancel(entry.getSbn()) 451 || mRemoteInputManager.isNotificationKeptForRemoteInputHistory(notificationKey)) { 452 final Runnable removeNotification = 453 mOnUserInteractionCallback.registerFutureDismissal(entry, REASON_CLICK); 454 // Immediately remove notification from visually showing. 455 // We have to post the removal to the UI thread for synchronization. 456 mMainThreadHandler.post(() -> { 457 if (mPresenter.isCollapsing()) { 458 // To avoid lags we're only performing the remove 459 // after the shade is collapsed 460 mShadeController.addPostCollapseAction(removeNotification); 461 } else { 462 removeNotification.run(); 463 } 464 }); 465 } 466 467 // inform NMS that the notification was clicked 468 mClickNotifier.onNotificationClick(notificationKey, nv); 469 470 mIsCollapsingToShowActivityOverLockscreen = false; 471 } 472 expandBubbleStackOnMainThread(NotificationEntry entry)473 private void expandBubbleStackOnMainThread(NotificationEntry entry) { 474 if (!mBubblesManagerOptional.isPresent()) { 475 return; 476 } 477 478 if (Looper.getMainLooper().isCurrentThread()) { 479 expandBubbleStack(entry); 480 } else { 481 mMainThreadHandler.post(() -> expandBubbleStack(entry)); 482 } 483 } 484 expandBubbleStack(NotificationEntry entry)485 private void expandBubbleStack(NotificationEntry entry) { 486 mBubblesManagerOptional.get().expandStackAndSelectBubble(entry); 487 mShadeController.collapseShade(); 488 } 489 startNotificationIntent( PendingIntent intent, Intent fillInIntent, NotificationEntry entry, ExpandableNotificationRow row, boolean animate, boolean isActivityIntent)490 private void startNotificationIntent( 491 PendingIntent intent, 492 Intent fillInIntent, 493 NotificationEntry entry, 494 ExpandableNotificationRow row, 495 boolean animate, 496 boolean isActivityIntent) { 497 mLogger.logStartNotificationIntent(entry); 498 final int displayId = mContextInteractor.getContext().getDisplayId(); 499 try { 500 ActivityTransitionAnimator.Controller animationController = 501 new StatusBarTransitionAnimatorController( 502 mNotificationAnimationProvider.getAnimatorController(row, null), 503 mShadeAnimationInteractor, 504 mShadeController, 505 mNotificationShadeWindowController, 506 mCommandQueue, 507 displayId, 508 isActivityIntent); 509 mActivityTransitionAnimator.startPendingIntentWithAnimation( 510 animationController, 511 animate, 512 intent.getCreatorPackage(), 513 (adapter) -> { 514 long eventTime = row.getAndResetLastActionUpTime(); 515 Bundle options = eventTime > 0 516 ? getActivityOptions( 517 displayId, 518 adapter, 519 mKeyguardStateController.isShowing(), 520 eventTime) 521 : getActivityOptions(displayId, adapter); 522 int result = intent.sendAndReturnResult(mContext, 0, fillInIntent, null, 523 null, null, options); 524 mLogger.logSendPendingIntent(entry, intent, result); 525 return result; 526 }); 527 } catch (PendingIntent.CanceledException e) { 528 // the stack trace isn't very helpful here. 529 // Just log the exception message. 530 mLogger.logSendingIntentFailed(e); 531 // TODO: Dismiss Keyguard. 532 } 533 } 534 535 @Override startNotificationGutsIntent(@onNull final Intent intent, final int appUid, @NonNull ExpandableNotificationRow row)536 public void startNotificationGutsIntent(@NonNull final Intent intent, final int appUid, 537 @NonNull ExpandableNotificationRow row) { 538 boolean animate = mActivityStarter.shouldAnimateLaunch(true /* isActivityIntent */); 539 final int displayId = mContextInteractor.getContext().getDisplayId(); 540 ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() { 541 @Override 542 public boolean onDismiss() { 543 AsyncTask.execute(() -> { 544 ActivityTransitionAnimator.Controller animationController = 545 new StatusBarTransitionAnimatorController( 546 mNotificationAnimationProvider.getAnimatorController(row), 547 mShadeAnimationInteractor, 548 mShadeController, 549 mNotificationShadeWindowController, 550 mCommandQueue, 551 displayId, 552 true /* isActivityIntent */); 553 554 mActivityTransitionAnimator.startIntentWithAnimation( 555 animationController, animate, intent.getPackage(), 556 (adapter) -> TaskStackBuilder.create(mContext) 557 .addNextIntentWithParentStack(intent) 558 .startActivities(getActivityOptions( 559 displayId, 560 adapter), 561 new UserHandle(UserHandle.getUserId(appUid)))); 562 }); 563 return true; 564 } 565 566 @Override 567 public boolean willRunAnimationOnKeyguard() { 568 return animate; 569 } 570 }; 571 mActivityStarter.dismissKeyguardThenExecute(onDismissAction, null, 572 false /* afterKeyguardGone */); 573 } 574 575 @Override startHistoryIntent(View view, boolean showHistory)576 public void startHistoryIntent(View view, boolean showHistory) { 577 ModesEmptyShadeFix.assertInLegacyMode(); 578 final int displayId = mContextInteractor.getContext().getDisplayId(); 579 boolean animate = mActivityStarter.shouldAnimateLaunch(true /* isActivityIntent */); 580 ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() { 581 @Override 582 public boolean onDismiss() { 583 AsyncTask.execute(() -> { 584 Intent intent = showHistory ? new Intent( 585 Settings.ACTION_NOTIFICATION_HISTORY) : new Intent( 586 Settings.ACTION_NOTIFICATION_SETTINGS); 587 TaskStackBuilder tsb = TaskStackBuilder.create(mContext) 588 .addNextIntent(new Intent(Settings.ACTION_NOTIFICATION_SETTINGS)); 589 if (showHistory) { 590 tsb.addNextIntent(intent); 591 } 592 593 ActivityTransitionAnimator.Controller viewController = 594 ActivityTransitionAnimator.Controller.fromView(view, 595 InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON 596 ); 597 ActivityTransitionAnimator.Controller animationController = 598 viewController == null ? null 599 : new StatusBarTransitionAnimatorController( 600 viewController, 601 mShadeAnimationInteractor, 602 mShadeController, 603 mNotificationShadeWindowController, 604 mCommandQueue, 605 displayId, 606 true /* isActivityIntent */); 607 608 mActivityTransitionAnimator.startIntentWithAnimation( 609 animationController, animate, intent.getPackage(), 610 (adapter) -> tsb.startActivities( 611 getActivityOptions(displayId, adapter), 612 mUserTracker.getUserHandle())); 613 }); 614 return true; 615 } 616 617 @Override 618 public boolean willRunAnimationOnKeyguard() { 619 return animate; 620 } 621 }; 622 mActivityStarter.dismissKeyguardThenExecute(onDismissAction, null, 623 false /* afterKeyguardGone */); 624 } 625 626 @Override startSettingsIntent(@onNull View view, @NonNull SettingsIntent intentInfo)627 public void startSettingsIntent(@NonNull View view, @NonNull SettingsIntent intentInfo) { 628 final int displayId = mContextInteractor.getContext().getDisplayId(); 629 boolean animate = mActivityStarter.shouldAnimateLaunch(true /* isActivityIntent */); 630 ActivityStarter.OnDismissAction onDismissAction = new ActivityStarter.OnDismissAction() { 631 @Override 632 public boolean onDismiss() { 633 AsyncTask.execute(() -> { 634 TaskStackBuilder tsb = TaskStackBuilder.create(mContext); 635 for (Intent intent : intentInfo.getBackStack()) { 636 tsb.addNextIntent(intent); 637 } 638 tsb.addNextIntent(intentInfo.getTargetIntent()); 639 640 ActivityTransitionAnimator.Controller viewController = 641 ActivityTransitionAnimator.Controller.fromView(view, 642 intentInfo.getCujType()); 643 ActivityTransitionAnimator.Controller animationController = 644 viewController == null ? null 645 : new StatusBarTransitionAnimatorController( 646 viewController, 647 mShadeAnimationInteractor, 648 mShadeController, 649 mNotificationShadeWindowController, 650 mCommandQueue, 651 displayId, 652 true /* isActivityIntent */); 653 654 mActivityTransitionAnimator.startIntentWithAnimation( 655 animationController, animate, intentInfo.getTargetIntent().getPackage(), 656 (adapter) -> tsb.startActivities( 657 getActivityOptions(displayId, adapter), 658 mUserTracker.getUserHandle())); 659 }); 660 return true; 661 } 662 663 @Override 664 public boolean willRunAnimationOnKeyguard() { 665 return animate; 666 } 667 }; 668 mActivityStarter.dismissKeyguardThenExecute(onDismissAction, null, 669 false /* afterKeyguardGone */); 670 } 671 removeHunAfterClick(ExpandableNotificationRow row)672 private void removeHunAfterClick(ExpandableNotificationRow row) { 673 String key = row.getKey(); 674 if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUpEntry(key)) { 675 // Release the HUN notification to the shade. 676 if (mPresenter.isPresenterFullyCollapsed()) { 677 HeadsUpUtil.setNeedsHeadsUpDisappearAnimationAfterClick(row, true); 678 } 679 680 // In most cases, when FLAG_AUTO_CANCEL is set, the notification will 681 // become canceled shortly by NoMan, but we can't assume that. 682 mHeadsUpManager.removeNotification(key, /* releaseImmediately= */ true, 683 "removeHunAfterClick"); 684 } 685 } 686 687 @VisibleForTesting launchFullScreenIntent(NotificationEntry entry)688 void launchFullScreenIntent(NotificationEntry entry) { 689 // Skip if device is in VR mode. 690 if (mPresenter.isDeviceInVrMode()) { 691 mLogger.logFullScreenIntentSuppressedByVR(entry); 692 return; 693 } 694 // Stop screensaver if the notification has a fullscreen intent. 695 // (like an incoming phone call) 696 mUiBgExecutor.execute(() -> { 697 try { 698 mDreamManager.awaken(); 699 } catch (RemoteException e) { 700 e.printStackTrace(); 701 } 702 }); 703 704 // not immersive & a fullscreen alert should be shown 705 final PendingIntent fullScreenIntent = 706 entry.getSbn().getNotification().fullScreenIntent; 707 mLogger.logSendingFullScreenIntent(entry, fullScreenIntent); 708 try { 709 EventLog.writeEvent(EventLogTags.SYSUI_FULLSCREEN_NOTIFICATION, 710 entry.getKey()); 711 mPowerInteractor.wakeUpForFullScreenIntent(); 712 713 ActivityOptions options = ActivityOptions.makeBasic(); 714 options.setPendingIntentBackgroundActivityStartMode( 715 MODE_BACKGROUND_ACTIVITY_START_ALLOWED); 716 fullScreenIntent.sendAndReturnResult(null, 0, null, null, null, null, 717 options.toBundle()); 718 entry.notifyFullScreenIntentLaunched(); 719 720 mMetricsLogger.count("note_fullscreen", 1); 721 722 String activityName; 723 List<ResolveInfo> resolveInfos = fullScreenIntent.queryIntentComponents(0); 724 if (resolveInfos.size() > 0 && resolveInfos.get(0) != null 725 && resolveInfos.get(0).activityInfo != null 726 && resolveInfos.get(0).activityInfo.name != null) { 727 activityName = resolveInfos.get(0).activityInfo.name; 728 } else { 729 activityName = ""; 730 } 731 FrameworkStatsLog.write(FrameworkStatsLog.FULL_SCREEN_INTENT_LAUNCHED, 732 fullScreenIntent.getCreatorUid(), 733 activityName); 734 } catch (PendingIntent.CanceledException e) { 735 // ignore 736 } 737 } 738 739 @Override isCollapsingToShowActivityOverLockscreen()740 public boolean isCollapsingToShowActivityOverLockscreen() { 741 return mIsCollapsingToShowActivityOverLockscreen; 742 } 743 shouldAutoCancel(StatusBarNotification sbn)744 private static boolean shouldAutoCancel(StatusBarNotification sbn) { 745 int flags = sbn.getNotification().flags; 746 if ((flags & Notification.FLAG_AUTO_CANCEL) != Notification.FLAG_AUTO_CANCEL) { 747 return false; 748 } 749 return true; 750 } 751 752 } 753