• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2024 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 package com.android.wm.shell.desktopmode
17 
18 import android.animation.Animator
19 import android.animation.AnimatorListenerAdapter
20 import android.animation.ValueAnimator
21 import android.os.IBinder
22 import android.view.SurfaceControl
23 import android.view.WindowManager.TRANSIT_OPEN
24 import android.window.TransitionInfo
25 import android.window.TransitionRequestInfo
26 import android.window.WindowContainerTransaction
27 import com.android.wm.shell.transition.Transitions
28 import com.android.wm.shell.transition.Transitions.TransitionFinishCallback
29 
30 /** Transition handler for drag-and-drop (i.e., tab tear) transitions that occur in desktop mode. */
31 class DesktopModeDragAndDropTransitionHandler(private val transitions: Transitions) :
32     Transitions.TransitionHandler {
33     private val pendingTransitionTokens: MutableList<IBinder> = mutableListOf()
34 
35     /**
36      * Begin a transition when a [android.app.PendingIntent] is dropped without a window to accept
37      * it.
38      */
39     fun handleDropEvent(wct: WindowContainerTransaction): IBinder {
40         val token = transitions.startTransition(TRANSIT_OPEN, wct, this)
41         pendingTransitionTokens.add(token)
42         return token
43     }
44 
45     override fun startAnimation(
46         transition: IBinder,
47         info: TransitionInfo,
48         startTransaction: SurfaceControl.Transaction,
49         finishTransaction: SurfaceControl.Transaction,
50         finishCallback: TransitionFinishCallback,
51     ): Boolean {
52         if (!pendingTransitionTokens.contains(transition)) return false
53         val change = findRelevantChange(info)
54         val leash = change.leash
55         val endBounds = change.endAbsBounds
56         startTransaction
57             .hide(leash)
58             .setWindowCrop(leash, endBounds.width(), endBounds.height())
59             .apply()
60         val animator = ValueAnimator()
61         animator.setFloatValues(0f, 1f)
62         animator.setDuration(FADE_IN_ANIMATION_DURATION)
63         val t = SurfaceControl.Transaction()
64         animator.addListener(
65             object : AnimatorListenerAdapter() {
66                 override fun onAnimationStart(animation: Animator) {
67                     t.show(leash)
68                     t.apply()
69                 }
70 
71                 override fun onAnimationEnd(animation: Animator) {
72                     finishCallback.onTransitionFinished(null)
73                 }
74             }
75         )
76         animator.addUpdateListener { animation: ValueAnimator ->
77             t.setAlpha(leash, animation.animatedFraction)
78             t.apply()
79         }
80         animator.start()
81         pendingTransitionTokens.remove(transition)
82         return true
83     }
84 
85     private fun findRelevantChange(info: TransitionInfo): TransitionInfo.Change {
86         val matchingChanges =
87             info.changes.filter { c -> isValidTaskChange(c) && c.mode == TRANSIT_OPEN }
88         if (matchingChanges.size != 1) {
89             throw IllegalStateException(
90                 "Expected 1 relevant change but found: ${matchingChanges.size}"
91             )
92         }
93         return matchingChanges.first()
94     }
95 
96     private fun isValidTaskChange(change: TransitionInfo.Change): Boolean =
97         change.taskInfo != null && change.taskInfo?.taskId != -1
98 
99     override fun handleRequest(
100         transition: IBinder,
101         request: TransitionRequestInfo,
102     ): WindowContainerTransaction? {
103         return null
104     }
105 
106     companion object {
107         const val FADE_IN_ANIMATION_DURATION = 300L
108     }
109 }
110