1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.systemui.statusbar.phone 16 17 import android.app.ActivityManager 18 import android.app.ActivityOptions 19 import android.app.ActivityTaskManager 20 import android.app.PendingIntent 21 import android.app.TaskStackBuilder 22 import android.content.Context 23 import android.content.Intent 24 import android.os.RemoteException 25 import android.os.UserHandle 26 import android.provider.Settings 27 import android.util.Log 28 import android.view.RemoteAnimationAdapter 29 import android.view.View 30 import android.view.WindowManager 31 import com.android.keyguard.KeyguardUpdateMonitor 32 import com.android.systemui.ActivityIntentHelper 33 import com.android.systemui.R 34 import com.android.systemui.animation.ActivityLaunchAnimator 35 import com.android.systemui.animation.ActivityLaunchAnimator.PendingIntentStarter 36 import com.android.systemui.animation.DelegateLaunchAnimatorController 37 import com.android.systemui.assist.AssistManager 38 import com.android.systemui.camera.CameraIntents.Companion.isInsecureCameraIntent 39 import com.android.systemui.dagger.SysUISingleton 40 import com.android.systemui.dagger.qualifiers.DisplayId 41 import com.android.systemui.dagger.qualifiers.Main 42 import com.android.systemui.keyguard.KeyguardViewMediator 43 import com.android.systemui.keyguard.WakefulnessLifecycle 44 import com.android.systemui.plugins.ActivityStarter 45 import com.android.systemui.plugins.ActivityStarter.OnDismissAction 46 import com.android.systemui.settings.UserTracker 47 import com.android.systemui.shade.ShadeController 48 import com.android.systemui.shade.ShadeViewController 49 import com.android.systemui.statusbar.NotificationLockscreenUserManager 50 import com.android.systemui.statusbar.NotificationShadeWindowController 51 import com.android.systemui.statusbar.SysuiStatusBarStateController 52 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow 53 import com.android.systemui.statusbar.policy.DeviceProvisionedController 54 import com.android.systemui.statusbar.policy.KeyguardStateController 55 import com.android.systemui.statusbar.window.StatusBarWindowController 56 import com.android.systemui.util.concurrency.DelayableExecutor 57 import com.android.systemui.util.kotlin.getOrNull 58 import dagger.Lazy 59 import java.util.Optional 60 import javax.inject.Inject 61 62 /** Handles start activity logic in SystemUI. */ 63 @SysUISingleton 64 class ActivityStarterImpl 65 @Inject 66 constructor( 67 private val centralSurfacesOptLazy: Lazy<Optional<CentralSurfaces>>, 68 private val assistManagerLazy: Lazy<AssistManager>, 69 private val dozeServiceHostLazy: Lazy<DozeServiceHost>, 70 private val biometricUnlockControllerLazy: Lazy<BiometricUnlockController>, 71 private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>, 72 private val shadeControllerLazy: Lazy<ShadeController>, 73 private val shadeViewControllerLazy: Lazy<ShadeViewController>, 74 private val statusBarKeyguardViewManagerLazy: Lazy<StatusBarKeyguardViewManager>, 75 private val notifShadeWindowControllerLazy: Lazy<NotificationShadeWindowController>, 76 private val activityLaunchAnimator: ActivityLaunchAnimator, 77 private val context: Context, 78 @DisplayId private val displayId: Int, 79 private val lockScreenUserManager: NotificationLockscreenUserManager, 80 private val statusBarWindowController: StatusBarWindowController, 81 private val wakefulnessLifecycle: WakefulnessLifecycle, 82 private val keyguardStateController: KeyguardStateController, 83 private val statusBarStateController: SysuiStatusBarStateController, 84 private val keyguardUpdateMonitor: KeyguardUpdateMonitor, 85 private val deviceProvisionedController: DeviceProvisionedController, 86 private val userTracker: UserTracker, 87 private val activityIntentHelper: ActivityIntentHelper, 88 @Main private val mainExecutor: DelayableExecutor, 89 ) : ActivityStarter { 90 companion object { 91 const val TAG = "ActivityStarterImpl" 92 } 93 94 private val centralSurfaces: CentralSurfaces? 95 get() = centralSurfacesOptLazy.get().getOrNull() 96 97 private val activityStarterInternal = ActivityStarterInternal() 98 99 override fun startPendingIntentDismissingKeyguard(intent: PendingIntent) { 100 activityStarterInternal.startPendingIntentDismissingKeyguard(intent = intent) 101 } 102 103 override fun startPendingIntentDismissingKeyguard( 104 intent: PendingIntent, 105 intentSentUiThreadCallback: Runnable?, 106 ) { 107 activityStarterInternal.startPendingIntentDismissingKeyguard( 108 intent = intent, 109 intentSentUiThreadCallback = intentSentUiThreadCallback, 110 ) 111 } 112 113 override fun startPendingIntentDismissingKeyguard( 114 intent: PendingIntent, 115 intentSentUiThreadCallback: Runnable?, 116 associatedView: View?, 117 ) { 118 activityStarterInternal.startPendingIntentDismissingKeyguard( 119 intent = intent, 120 intentSentUiThreadCallback = intentSentUiThreadCallback, 121 associatedView = associatedView, 122 ) 123 } 124 125 override fun startPendingIntentDismissingKeyguard( 126 intent: PendingIntent, 127 intentSentUiThreadCallback: Runnable?, 128 animationController: ActivityLaunchAnimator.Controller?, 129 ) { 130 activityStarterInternal.startPendingIntentDismissingKeyguard( 131 intent = intent, 132 intentSentUiThreadCallback = intentSentUiThreadCallback, 133 animationController = animationController, 134 ) 135 } 136 137 /** 138 * TODO(b/279084380): Change callers to just call startActivityDismissingKeyguard and deprecate 139 * this. 140 */ 141 override fun startActivity(intent: Intent, dismissShade: Boolean) { 142 activityStarterInternal.startActivityDismissingKeyguard( 143 intent = intent, 144 dismissShade = dismissShade, 145 ) 146 } 147 148 /** 149 * TODO(b/279084380): Change callers to just call startActivityDismissingKeyguard and deprecate 150 * this. 151 */ 152 override fun startActivity(intent: Intent, onlyProvisioned: Boolean, dismissShade: Boolean) { 153 activityStarterInternal.startActivityDismissingKeyguard( 154 intent = intent, 155 onlyProvisioned = onlyProvisioned, 156 dismissShade = dismissShade, 157 ) 158 } 159 160 /** 161 * TODO(b/279084380): Change callers to just call startActivityDismissingKeyguard and deprecate 162 * this. 163 */ 164 override fun startActivity( 165 intent: Intent, 166 dismissShade: Boolean, 167 callback: ActivityStarter.Callback?, 168 ) { 169 activityStarterInternal.startActivityDismissingKeyguard( 170 intent = intent, 171 dismissShade = dismissShade, 172 callback = callback, 173 ) 174 } 175 176 /** 177 * TODO(b/279084380): Change callers to just call startActivityDismissingKeyguard and deprecate 178 * this. 179 */ 180 override fun startActivity( 181 intent: Intent, 182 onlyProvisioned: Boolean, 183 dismissShade: Boolean, 184 flags: Int, 185 ) { 186 activityStarterInternal.startActivityDismissingKeyguard( 187 intent = intent, 188 onlyProvisioned = onlyProvisioned, 189 dismissShade = dismissShade, 190 flags = flags, 191 ) 192 } 193 194 override fun startActivity( 195 intent: Intent, 196 dismissShade: Boolean, 197 animationController: ActivityLaunchAnimator.Controller?, 198 showOverLockscreenWhenLocked: Boolean, 199 ) { 200 activityStarterInternal.startActivity( 201 intent = intent, 202 dismissShade = dismissShade, 203 animationController = animationController, 204 showOverLockscreenWhenLocked = showOverLockscreenWhenLocked, 205 ) 206 } 207 override fun startActivity( 208 intent: Intent, 209 dismissShade: Boolean, 210 animationController: ActivityLaunchAnimator.Controller?, 211 showOverLockscreenWhenLocked: Boolean, 212 userHandle: UserHandle?, 213 ) { 214 activityStarterInternal.startActivity( 215 intent = intent, 216 dismissShade = dismissShade, 217 animationController = animationController, 218 showOverLockscreenWhenLocked = showOverLockscreenWhenLocked, 219 userHandle = userHandle, 220 ) 221 } 222 223 override fun postStartActivityDismissingKeyguard(intent: PendingIntent) { 224 postOnUiThread { 225 activityStarterInternal.startPendingIntentDismissingKeyguard( 226 intent = intent, 227 ) 228 } 229 } 230 231 override fun postStartActivityDismissingKeyguard( 232 intent: PendingIntent, 233 animationController: ActivityLaunchAnimator.Controller? 234 ) { 235 postOnUiThread { 236 activityStarterInternal.startPendingIntentDismissingKeyguard( 237 intent = intent, 238 animationController = animationController, 239 ) 240 } 241 } 242 243 override fun postStartActivityDismissingKeyguard(intent: Intent, delay: Int) { 244 postOnUiThread(delay) { 245 activityStarterInternal.startActivityDismissingKeyguard( 246 intent = intent, 247 onlyProvisioned = true, 248 dismissShade = true, 249 ) 250 } 251 } 252 253 override fun postStartActivityDismissingKeyguard( 254 intent: Intent, 255 delay: Int, 256 animationController: ActivityLaunchAnimator.Controller?, 257 ) { 258 postOnUiThread(delay) { 259 activityStarterInternal.startActivityDismissingKeyguard( 260 intent = intent, 261 onlyProvisioned = true, 262 dismissShade = true, 263 animationController = animationController, 264 ) 265 } 266 } 267 268 override fun postStartActivityDismissingKeyguard( 269 intent: Intent, 270 delay: Int, 271 animationController: ActivityLaunchAnimator.Controller?, 272 customMessage: String?, 273 ) { 274 postOnUiThread(delay) { 275 activityStarterInternal.startActivityDismissingKeyguard( 276 intent = intent, 277 onlyProvisioned = true, 278 dismissShade = true, 279 animationController = animationController, 280 customMessage = customMessage, 281 ) 282 } 283 } 284 285 override fun dismissKeyguardThenExecute( 286 action: OnDismissAction, 287 cancel: Runnable?, 288 afterKeyguardGone: Boolean, 289 ) { 290 activityStarterInternal.dismissKeyguardThenExecute( 291 action = action, 292 cancel = cancel, 293 afterKeyguardGone = afterKeyguardGone, 294 ) 295 } 296 297 override fun dismissKeyguardThenExecute( 298 action: OnDismissAction, 299 cancel: Runnable?, 300 afterKeyguardGone: Boolean, 301 customMessage: String?, 302 ) { 303 activityStarterInternal.dismissKeyguardThenExecute( 304 action = action, 305 cancel = cancel, 306 afterKeyguardGone = afterKeyguardGone, 307 customMessage = customMessage, 308 ) 309 } 310 311 override fun startActivityDismissingKeyguard( 312 intent: Intent, 313 onlyProvisioned: Boolean, 314 dismissShade: Boolean, 315 ) { 316 activityStarterInternal.startActivityDismissingKeyguard( 317 intent = intent, 318 onlyProvisioned = onlyProvisioned, 319 dismissShade = dismissShade, 320 ) 321 } 322 323 override fun startActivityDismissingKeyguard( 324 intent: Intent, 325 onlyProvisioned: Boolean, 326 dismissShade: Boolean, 327 disallowEnterPictureInPictureWhileLaunching: Boolean, 328 callback: ActivityStarter.Callback?, 329 flags: Int, 330 animationController: ActivityLaunchAnimator.Controller?, 331 userHandle: UserHandle?, 332 ) { 333 activityStarterInternal.startActivityDismissingKeyguard( 334 intent = intent, 335 onlyProvisioned = onlyProvisioned, 336 dismissShade = dismissShade, 337 disallowEnterPictureInPictureWhileLaunching = 338 disallowEnterPictureInPictureWhileLaunching, 339 callback = callback, 340 flags = flags, 341 animationController = animationController, 342 userHandle = userHandle, 343 ) 344 } 345 346 override fun executeRunnableDismissingKeyguard( 347 runnable: Runnable?, 348 cancelAction: Runnable?, 349 dismissShade: Boolean, 350 afterKeyguardGone: Boolean, 351 deferred: Boolean, 352 ) { 353 activityStarterInternal.executeRunnableDismissingKeyguard( 354 runnable = runnable, 355 cancelAction = cancelAction, 356 dismissShade = dismissShade, 357 afterKeyguardGone = afterKeyguardGone, 358 deferred = deferred, 359 ) 360 } 361 362 override fun executeRunnableDismissingKeyguard( 363 runnable: Runnable?, 364 cancelAction: Runnable?, 365 dismissShade: Boolean, 366 afterKeyguardGone: Boolean, 367 deferred: Boolean, 368 willAnimateOnKeyguard: Boolean, 369 customMessage: String?, 370 ) { 371 activityStarterInternal.executeRunnableDismissingKeyguard( 372 runnable = runnable, 373 cancelAction = cancelAction, 374 dismissShade = dismissShade, 375 afterKeyguardGone = afterKeyguardGone, 376 deferred = deferred, 377 willAnimateOnKeyguard = willAnimateOnKeyguard, 378 customMessage = customMessage, 379 ) 380 } 381 382 override fun postQSRunnableDismissingKeyguard(runnable: Runnable?) { 383 postOnUiThread { 384 statusBarStateController.setLeaveOpenOnKeyguardHide(true) 385 activityStarterInternal.executeRunnableDismissingKeyguard( 386 runnable = { runnable?.let { postOnUiThread(runnable = it) } }, 387 ) 388 } 389 } 390 391 private fun postOnUiThread(delay: Int = 0, runnable: Runnable) { 392 mainExecutor.executeDelayed(runnable, delay.toLong()) 393 } 394 395 /** 396 * Whether we should animate an activity launch. 397 * 398 * Note: This method must be called *before* dismissing the keyguard. 399 */ 400 private fun shouldAnimateLaunch( 401 isActivityIntent: Boolean, 402 showOverLockscreen: Boolean, 403 ): Boolean { 404 // TODO(b/294418322): Support launch animations when occluded. 405 if (keyguardStateController.isOccluded) { 406 return false 407 } 408 409 // Always animate if we are not showing the keyguard or if we animate over the lockscreen 410 // (without unlocking it). 411 if (showOverLockscreen || !keyguardStateController.isShowing) { 412 return true 413 } 414 415 // We don't animate non-activity launches as they can break the animation. 416 // TODO(b/184121838): Support non activity launches on the lockscreen. 417 return isActivityIntent 418 } 419 420 override fun shouldAnimateLaunch(isActivityIntent: Boolean): Boolean { 421 return shouldAnimateLaunch(isActivityIntent, false) 422 } 423 424 /** 425 * Encapsulates the activity logic for activity starter. 426 * 427 * Logic is duplicated in {@link CentralSurfacesImpl} 428 */ 429 private inner class ActivityStarterInternal { 430 /** Starts an activity after dismissing keyguard. */ 431 fun startActivityDismissingKeyguard( 432 intent: Intent, 433 onlyProvisioned: Boolean = false, 434 dismissShade: Boolean = false, 435 disallowEnterPictureInPictureWhileLaunching: Boolean = false, 436 callback: ActivityStarter.Callback? = null, 437 flags: Int = 0, 438 animationController: ActivityLaunchAnimator.Controller? = null, 439 userHandle: UserHandle? = null, 440 customMessage: String? = null, 441 ) { 442 val userHandle: UserHandle = userHandle ?: getActivityUserHandle(intent) 443 444 if (onlyProvisioned && !deviceProvisionedController.isDeviceProvisioned) return 445 446 val willLaunchResolverActivity: Boolean = 447 activityIntentHelper.wouldLaunchResolverActivity( 448 intent, 449 lockScreenUserManager.currentUserId 450 ) 451 452 val animate = 453 animationController != null && 454 !willLaunchResolverActivity && 455 shouldAnimateLaunch(isActivityIntent = true) 456 val animController = 457 wrapAnimationController( 458 animationController = animationController, 459 dismissShade = dismissShade, 460 isLaunchForActivity = true, 461 ) 462 463 // If we animate, we will dismiss the shade only once the animation is done. This is 464 // taken care of by the StatusBarLaunchAnimationController. 465 val dismissShadeDirectly = dismissShade && animController == null 466 467 val runnable = Runnable { 468 assistManagerLazy.get().hideAssist() 469 intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP 470 intent.addFlags(flags) 471 val result = intArrayOf(ActivityManager.START_CANCELED) 472 activityLaunchAnimator.startIntentWithAnimation( 473 animController, 474 animate, 475 intent.getPackage() 476 ) { adapter: RemoteAnimationAdapter? -> 477 val options = 478 ActivityOptions(CentralSurfaces.getActivityOptions(displayId, adapter)) 479 480 // We know that the intent of the caller is to dismiss the keyguard and 481 // this runnable is called right after the keyguard is solved, so we tell 482 // WM that we should dismiss it to avoid flickers when opening an activity 483 // that can also be shown over the keyguard. 484 options.setDismissKeyguard() 485 options.setDisallowEnterPictureInPictureWhileLaunching( 486 disallowEnterPictureInPictureWhileLaunching 487 ) 488 if (isInsecureCameraIntent(intent)) { 489 // Normally an activity will set it's requested rotation 490 // animation on its window. However when launching an activity 491 // causes the orientation to change this is too late. In these cases 492 // the default animation is used. This doesn't look good for 493 // the camera (as it rotates the camera contents out of sync 494 // with physical reality). So, we ask the WindowManager to 495 // force the cross fade animation if an orientation change 496 // happens to occur during the launch. 497 options.rotationAnimationHint = 498 WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS 499 } 500 if (Settings.Panel.ACTION_VOLUME == intent.action) { 501 // Settings Panel is implemented as activity(not a dialog), so 502 // underlying app is paused and may enter picture-in-picture mode 503 // as a result. 504 // So we need to disable picture-in-picture mode here 505 // if it is volume panel. 506 options.setDisallowEnterPictureInPictureWhileLaunching(true) 507 } 508 try { 509 result[0] = 510 ActivityTaskManager.getService() 511 .startActivityAsUser( 512 null, 513 context.basePackageName, 514 context.attributionTag, 515 intent, 516 intent.resolveTypeIfNeeded(context.contentResolver), 517 null, 518 null, 519 0, 520 Intent.FLAG_ACTIVITY_NEW_TASK, 521 null, 522 options.toBundle(), 523 userHandle.identifier, 524 ) 525 } catch (e: RemoteException) { 526 Log.w(TAG, "Unable to start activity", e) 527 } 528 result[0] 529 } 530 callback?.onActivityStarted(result[0]) 531 } 532 val cancelRunnable = Runnable { 533 callback?.onActivityStarted(ActivityManager.START_CANCELED) 534 } 535 // Do not deferKeyguard when occluded because, when keyguard is occluded, 536 // we do not launch the activity until keyguard is done. 537 val occluded = (keyguardStateController.isShowing && keyguardStateController.isOccluded) 538 val deferred = !occluded 539 executeRunnableDismissingKeyguard( 540 runnable, 541 cancelRunnable, 542 dismissShadeDirectly, 543 willLaunchResolverActivity, 544 deferred, 545 animate, 546 customMessage, 547 ) 548 } 549 550 /** Starts a pending intent after dismissing keyguard. */ 551 fun startPendingIntentDismissingKeyguard( 552 intent: PendingIntent, 553 intentSentUiThreadCallback: Runnable? = null, 554 associatedView: View? = null, 555 animationController: ActivityLaunchAnimator.Controller? = null, 556 ) { 557 val animationController = 558 if (associatedView is ExpandableNotificationRow) { 559 centralSurfaces?.getAnimatorControllerFromNotification(associatedView) 560 } else animationController 561 562 val willLaunchResolverActivity = 563 (intent.isActivity && 564 activityIntentHelper.wouldPendingLaunchResolverActivity( 565 intent, 566 lockScreenUserManager.currentUserId, 567 )) 568 569 val animate = 570 !willLaunchResolverActivity && 571 animationController != null && 572 shouldAnimateLaunch(intent.isActivity) 573 574 // If we animate, don't collapse the shade and defer the keyguard dismiss (in case we 575 // run the animation on the keyguard). The animation will take care of (instantly) 576 // collapsing the shade and hiding the keyguard once it is done. 577 val collapse = !animate 578 executeRunnableDismissingKeyguard( 579 runnable = { 580 try { 581 // We wrap animationCallback with a StatusBarLaunchAnimatorController so 582 // that the shade is collapsed after the animation (or when it is cancelled, 583 // aborted, etc). 584 val controller: ActivityLaunchAnimator.Controller? = 585 wrapAnimationController( 586 animationController = animationController, 587 dismissShade = true, 588 isLaunchForActivity = intent.isActivity, 589 ) 590 activityLaunchAnimator.startPendingIntentWithAnimation( 591 controller, 592 animate, 593 intent.creatorPackage, 594 object : PendingIntentStarter { 595 override fun startPendingIntent( 596 animationAdapter: RemoteAnimationAdapter? 597 ): Int { 598 val options = 599 ActivityOptions( 600 CentralSurfaces.getActivityOptions( 601 displayId, 602 animationAdapter 603 ) 604 ) 605 // TODO b/221255671: restrict this to only be set for 606 // notifications 607 options.isEligibleForLegacyPermissionPrompt = true 608 options.setPendingIntentBackgroundActivityStartMode( 609 ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED 610 ) 611 return intent.sendAndReturnResult( 612 null, 613 0, 614 null, 615 null, 616 null, 617 null, 618 options.toBundle() 619 ) 620 } 621 }, 622 ) 623 } catch (e: PendingIntent.CanceledException) { 624 // the stack trace isn't very helpful here. 625 // Just log the exception message. 626 Log.w(TAG, "Sending intent failed: $e") 627 if (!collapse) { 628 // executeRunnableDismissingKeyguard did not collapse for us already. 629 shadeControllerLazy.get().collapseOnMainThread() 630 } 631 // TODO: Dismiss Keyguard. 632 } 633 if (intent.isActivity) { 634 assistManagerLazy.get().hideAssist() 635 } 636 intentSentUiThreadCallback?.let { postOnUiThread(runnable = it) } 637 }, 638 afterKeyguardGone = willLaunchResolverActivity, 639 dismissShade = collapse, 640 willAnimateOnKeyguard = animate, 641 ) 642 } 643 644 /** Starts an Activity. */ 645 fun startActivity( 646 intent: Intent, 647 dismissShade: Boolean = false, 648 animationController: ActivityLaunchAnimator.Controller? = null, 649 showOverLockscreenWhenLocked: Boolean = false, 650 userHandle: UserHandle? = null, 651 ) { 652 val userHandle = userHandle ?: getActivityUserHandle(intent) 653 // Make sure that we dismiss the keyguard if it is directly dismissible or when we don't 654 // want to show the activity above it. 655 if (keyguardStateController.isUnlocked || !showOverLockscreenWhenLocked) { 656 startActivityDismissingKeyguard( 657 intent = intent, 658 onlyProvisioned = false, 659 dismissShade = dismissShade, 660 disallowEnterPictureInPictureWhileLaunching = false, 661 callback = null, 662 flags = 0, 663 animationController = animationController, 664 userHandle = userHandle, 665 ) 666 return 667 } 668 669 val animate = 670 animationController != null && 671 shouldAnimateLaunch( 672 /* isActivityIntent= */ true, 673 showOverLockscreenWhenLocked 674 ) == true 675 676 var controller: ActivityLaunchAnimator.Controller? = null 677 if (animate) { 678 // Wrap the animation controller to dismiss the shade and set 679 // mIsLaunchingActivityOverLockscreen during the animation. 680 val delegate = 681 wrapAnimationController( 682 animationController = animationController, 683 dismissShade = dismissShade, 684 isLaunchForActivity = true, 685 ) 686 delegate?.let { 687 controller = 688 object : DelegateLaunchAnimatorController(delegate) { 689 override fun onIntentStarted(willAnimate: Boolean) { 690 delegate?.onIntentStarted(willAnimate) 691 if (willAnimate) { 692 centralSurfaces?.setIsLaunchingActivityOverLockscreen(true) 693 } 694 } 695 696 override fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) { 697 super.onLaunchAnimationStart(isExpandingFullyAbove) 698 699 // Double check that the keyguard is still showing and not going 700 // away, but if so set the keyguard occluded. Typically, WM will let 701 // KeyguardViewMediator know directly, but we're overriding that to 702 // play the custom launch animation, so we need to take care of that 703 // here. The unocclude animation is not overridden, so WM will call 704 // KeyguardViewMediator's unocclude animation runner when the 705 // activity is exited. 706 if ( 707 keyguardStateController.isShowing && 708 !keyguardStateController.isKeyguardGoingAway 709 ) { 710 Log.d(TAG, "Setting occluded = true in #startActivity.") 711 keyguardViewMediatorLazy 712 .get() 713 .setOccluded(true /* isOccluded */, true /* animate */) 714 } 715 } 716 717 override fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) { 718 // Set mIsLaunchingActivityOverLockscreen to false before actually 719 // finishing the animation so that we can assume that 720 // mIsLaunchingActivityOverLockscreen being true means that we will 721 // collapse the shade (or at least run the post collapse runnables) 722 // later on. 723 centralSurfaces?.setIsLaunchingActivityOverLockscreen(false) 724 delegate?.onLaunchAnimationEnd(isExpandingFullyAbove) 725 } 726 727 override fun onLaunchAnimationCancelled( 728 newKeyguardOccludedState: Boolean? 729 ) { 730 if (newKeyguardOccludedState != null) { 731 keyguardViewMediatorLazy 732 .get() 733 .setOccluded(newKeyguardOccludedState, false /* animate */) 734 } 735 736 // Set mIsLaunchingActivityOverLockscreen to false before actually 737 // finishing the animation so that we can assume that 738 // mIsLaunchingActivityOverLockscreen being true means that we will 739 // collapse the shade (or at least run the // post collapse 740 // runnables) later on. 741 centralSurfaces?.setIsLaunchingActivityOverLockscreen(false) 742 delegate.onLaunchAnimationCancelled(newKeyguardOccludedState) 743 } 744 } 745 } 746 } else if (dismissShade) { 747 // The animation will take care of dismissing the shade at the end of the animation. 748 // If we don't animate, collapse it directly. 749 shadeControllerLazy.get().cancelExpansionAndCollapseShade() 750 } 751 752 // We should exit the dream to prevent the activity from starting below the 753 // dream. 754 if (keyguardUpdateMonitor.isDreaming) { 755 centralSurfaces?.awakenDreams() 756 } 757 758 activityLaunchAnimator.startIntentWithAnimation( 759 controller, 760 animate, 761 intent.getPackage(), 762 showOverLockscreenWhenLocked 763 ) { adapter: RemoteAnimationAdapter? -> 764 TaskStackBuilder.create(context) 765 .addNextIntent(intent) 766 .startActivities( 767 CentralSurfaces.getActivityOptions(displayId, adapter), 768 userHandle 769 ) 770 } 771 } 772 773 /** Executes an action after dismissing keyguard. */ 774 fun dismissKeyguardThenExecute( 775 action: OnDismissAction, 776 cancel: Runnable? = null, 777 afterKeyguardGone: Boolean = false, 778 customMessage: String? = null, 779 ) { 780 if ( 781 !action.willRunAnimationOnKeyguard() && 782 wakefulnessLifecycle.wakefulness == WakefulnessLifecycle.WAKEFULNESS_ASLEEP && 783 keyguardStateController.canDismissLockScreen() && 784 !statusBarStateController.leaveOpenOnKeyguardHide() && 785 dozeServiceHostLazy.get().isPulsing 786 ) { 787 // Reuse the biometric wake-and-unlock transition if we dismiss keyguard from a 788 // pulse. 789 // TODO: Factor this transition out of BiometricUnlockController. 790 biometricUnlockControllerLazy 791 .get() 792 .startWakeAndUnlock(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING) 793 } 794 if (keyguardStateController.isShowing) { 795 statusBarKeyguardViewManagerLazy 796 .get() 797 .dismissWithAction(action, cancel, afterKeyguardGone, customMessage) 798 } else { 799 // If the keyguard isn't showing but the device is dreaming, we should exit the 800 // dream. 801 if (keyguardUpdateMonitor.isDreaming) { 802 centralSurfaces?.awakenDreams() 803 } 804 action.onDismiss() 805 } 806 } 807 808 /** Executes an action after dismissing keyguard. */ 809 fun executeRunnableDismissingKeyguard( 810 runnable: Runnable? = null, 811 cancelAction: Runnable? = null, 812 dismissShade: Boolean = false, 813 afterKeyguardGone: Boolean = false, 814 deferred: Boolean = false, 815 willAnimateOnKeyguard: Boolean = false, 816 customMessage: String? = null, 817 ) { 818 val onDismissAction: OnDismissAction = 819 object : OnDismissAction { 820 override fun onDismiss(): Boolean { 821 if (runnable != null) { 822 if ( 823 keyguardStateController.isShowing && 824 keyguardStateController.isOccluded 825 ) { 826 statusBarKeyguardViewManagerLazy 827 .get() 828 .addAfterKeyguardGoneRunnable(runnable) 829 } else { 830 mainExecutor.execute(runnable) 831 } 832 } 833 if (dismissShade) { 834 if ( 835 shadeControllerLazy.get().isExpandedVisible && 836 !statusBarKeyguardViewManagerLazy.get().isBouncerShowing 837 ) { 838 shadeControllerLazy.get().animateCollapseShadeForcedDelayed() 839 } else { 840 // Do it after DismissAction has been processed to conserve the 841 // needed ordering. 842 postOnUiThread { 843 shadeControllerLazy.get().runPostCollapseRunnables() 844 } 845 } 846 } 847 return deferred 848 } 849 850 override fun willRunAnimationOnKeyguard(): Boolean { 851 return willAnimateOnKeyguard 852 } 853 } 854 dismissKeyguardThenExecute( 855 onDismissAction, 856 cancelAction, 857 afterKeyguardGone, 858 customMessage, 859 ) 860 } 861 862 /** 863 * Return a [ActivityLaunchAnimator.Controller] wrapping `animationController` so that: 864 * - if it launches in the notification shade window and `dismissShade` is true, then the 865 * shade will be instantly dismissed at the end of the animation. 866 * - if it launches in status bar window, it will make the status bar window match the 867 * device size during the animation (that way, the animation won't be clipped by the 868 * status bar size). 869 * 870 * @param animationController the controller that is wrapped and will drive the main 871 * animation. 872 * @param dismissShade whether the notification shade will be dismissed at the end of the 873 * animation. This is ignored if `animationController` is not animating in the shade 874 * window. 875 * @param isLaunchForActivity whether the launch is for an activity. 876 */ 877 private fun wrapAnimationController( 878 animationController: ActivityLaunchAnimator.Controller?, 879 dismissShade: Boolean, 880 isLaunchForActivity: Boolean, 881 ): ActivityLaunchAnimator.Controller? { 882 if (animationController == null) { 883 return null 884 } 885 val rootView = animationController.launchContainer.rootView 886 val controllerFromStatusBar: Optional<ActivityLaunchAnimator.Controller> = 887 statusBarWindowController.wrapAnimationControllerIfInStatusBar( 888 rootView, 889 animationController 890 ) 891 if (controllerFromStatusBar.isPresent) { 892 return controllerFromStatusBar.get() 893 } 894 895 centralSurfaces?.let { 896 // If the view is not in the status bar, then we are animating a view in the shade. 897 // We have to make sure that we collapse it when the animation ends or is cancelled. 898 if (dismissShade) { 899 return StatusBarLaunchAnimatorController( 900 animationController, 901 shadeViewControllerLazy.get(), 902 shadeControllerLazy.get(), 903 notifShadeWindowControllerLazy.get(), 904 isLaunchForActivity 905 ) 906 } 907 } 908 909 return animationController 910 } 911 912 /** Retrieves the current user handle to start the Activity. */ 913 private fun getActivityUserHandle(intent: Intent): UserHandle { 914 val packages: Array<String> = 915 context.resources.getStringArray(R.array.system_ui_packages) 916 for (pkg in packages) { 917 val componentName = intent.component ?: break 918 if (pkg == componentName.packageName) { 919 return UserHandle(UserHandle.myUserId()) 920 } 921 } 922 return userTracker.userHandle 923 } 924 } 925 } 926