1 /* <lambda>null2 * Copyright (C) 2025 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.wm.shell.desktopmode 18 19 import android.animation.Animator 20 import android.animation.AnimatorSet 21 import android.animation.ValueAnimator 22 import android.os.Handler 23 import android.os.IBinder 24 import android.view.Choreographer 25 import android.view.SurfaceControl 26 import android.window.TransitionInfo 27 import android.window.TransitionRequestInfo 28 import android.window.WindowContainerTransaction 29 import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY 30 import com.android.internal.jank.InteractionJankMonitor 31 import com.android.wm.shell.common.DisplayController 32 import com.android.wm.shell.shared.animation.Interpolators 33 import com.android.wm.shell.transition.Transitions 34 import kotlin.time.Duration.Companion.milliseconds 35 36 /** Transition handler for moving a window to a different display. */ 37 class DesktopModeMoveToDisplayTransitionHandler( 38 private val animationTransaction: SurfaceControl.Transaction, 39 private val interactionJankMonitor: InteractionJankMonitor, 40 private val shellMainHandler: Handler, 41 private val displayController: DisplayController, 42 ) : Transitions.TransitionHandler { 43 44 override fun handleRequest( 45 transition: IBinder, 46 request: TransitionRequestInfo, 47 ): WindowContainerTransaction? = null 48 49 override fun startAnimation( 50 transition: IBinder, 51 info: TransitionInfo, 52 startTransaction: SurfaceControl.Transaction, 53 finishTransaction: SurfaceControl.Transaction, 54 finishCallback: Transitions.TransitionFinishCallback, 55 ): Boolean { 56 val changes = info.changes.filter { it.startDisplayId != it.endDisplayId } 57 if (changes.isEmpty()) return false 58 for (change in changes) { 59 val endBounds = change.endAbsBounds 60 // The position should be relative to the parent. For example, in ActivityEmbedding, the 61 // leash surface for the embedded Activity is parented to the container. 62 val endPosition = change.endRelOffset 63 startTransaction 64 .setPosition(change.leash, endPosition.x.toFloat(), endPosition.y.toFloat()) 65 .setWindowCrop(change.leash, endBounds.width(), endBounds.height()) 66 } 67 startTransaction.apply() 68 69 val animator = AnimatorSet() 70 animator.playTogether( 71 changes.map { 72 ValueAnimator.ofFloat(0f, 1f).apply { 73 duration = ANIM_DURATION.inWholeMilliseconds 74 interpolator = Interpolators.LINEAR 75 addUpdateListener { animation -> 76 animationTransaction 77 .setAlpha(it.leash, animation.animatedValue as Float) 78 .setFrameTimeline(Choreographer.getInstance().vsyncId) 79 .apply() 80 } 81 } 82 } 83 ) 84 85 animator.addListener( 86 object : Animator.AnimatorListener { 87 override fun onAnimationStart(animation: Animator) { 88 val displayContext = 89 displayController.getDisplayContext(changes[0].endDisplayId) 90 if (displayContext == null) return 91 interactionJankMonitor.begin( 92 changes[0].leash, 93 displayContext, 94 shellMainHandler, 95 CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY, 96 ) 97 } 98 99 override fun onAnimationEnd(animation: Animator) { 100 finishTransaction.apply() 101 finishCallback.onTransitionFinished(null) 102 interactionJankMonitor.end(CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY) 103 } 104 105 override fun onAnimationCancel(animation: Animator) { 106 finishTransaction.apply() 107 finishCallback.onTransitionFinished(null) 108 interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY) 109 } 110 111 override fun onAnimationRepeat(animation: Animator) = Unit 112 } 113 ) 114 animator.start() 115 return true 116 } 117 118 private companion object { 119 val ANIM_DURATION = 100.milliseconds 120 } 121 } 122