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 17 package com.android.wm.shell.desktopmode.compatui 18 19 import android.animation.ValueAnimator 20 import android.app.ActivityManager.RunningTaskInfo 21 import android.content.Context 22 import android.os.IBinder 23 import android.view.Display.DEFAULT_DISPLAY 24 import android.view.SurfaceControl 25 import android.window.TransitionInfo 26 import android.window.TransitionRequestInfo 27 import android.window.WindowContainerTransaction 28 import androidx.core.animation.addListener 29 import com.android.app.animation.Interpolators 30 import com.android.internal.protolog.ProtoLog 31 import com.android.wm.shell.common.ShellExecutor 32 import com.android.wm.shell.desktopmode.DesktopUserRepositories 33 import com.android.wm.shell.desktopmode.DesktopWallpaperActivity 34 import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE 35 import com.android.wm.shell.shared.TransitionUtil.isClosingMode 36 import com.android.wm.shell.shared.TransitionUtil.isClosingType 37 import com.android.wm.shell.shared.TransitionUtil.isOpeningMode 38 import com.android.wm.shell.shared.TransitionUtil.isOpeningType 39 import com.android.wm.shell.shared.desktopmode.DesktopModeCompatPolicy 40 import com.android.wm.shell.sysui.ShellInit 41 import com.android.wm.shell.transition.Transitions 42 import com.android.wm.shell.transition.Transitions.TransitionHandler 43 44 /** Handles transitions related to system modals, e.g. launch and close transitions. */ 45 class SystemModalsTransitionHandler( 46 private val context: Context, 47 private val mainExecutor: ShellExecutor, 48 private val animExecutor: ShellExecutor, 49 private val shellInit: ShellInit, 50 private val transitions: Transitions, 51 private val desktopUserRepositories: DesktopUserRepositories, 52 private val desktopModeCompatPolicy: DesktopModeCompatPolicy, 53 ) : TransitionHandler { 54 55 private val showingSystemModalsIds = mutableSetOf<Int>() 56 57 init { 58 shellInit.addInitCallback({ transitions.addHandler(this) }, this) 59 } 60 61 override fun startAnimation( 62 transition: IBinder, 63 info: TransitionInfo, 64 startTransaction: SurfaceControl.Transaction, 65 finishTransaction: SurfaceControl.Transaction, 66 finishCallback: Transitions.TransitionFinishCallback, 67 ): Boolean { 68 if (!isDesktopModeShowing(DEFAULT_DISPLAY)) return false 69 if (isOpeningType(info.type)) { 70 val launchChange = getLaunchingSystemModal(info) ?: return false 71 val taskInfo = launchChange.taskInfo 72 requireNotNull(taskInfo) 73 logV("Animating system modal launch: taskId=%d", taskInfo.taskId) 74 showingSystemModalsIds.add(taskInfo.taskId) 75 animateSystemModal( 76 launchChange.leash, 77 startTransaction, 78 finishTransaction, 79 finishCallback, 80 /* toShow= */ true, 81 ) 82 return true 83 } 84 if (isClosingType(info.type)) { 85 val closeChange = getClosingSystemModal(info) ?: return false 86 val taskInfo = closeChange.taskInfo 87 requireNotNull(taskInfo) 88 logV("Animating system modal close: taskId=%d", taskInfo.taskId) 89 showingSystemModalsIds.remove(taskInfo.taskId) 90 animateSystemModal( 91 closeChange.leash, 92 startTransaction, 93 finishTransaction, 94 finishCallback, 95 /* toShow= */ false, 96 ) 97 return true 98 } 99 return false 100 } 101 102 private fun animateSystemModal( 103 leash: SurfaceControl, 104 startTransaction: SurfaceControl.Transaction, 105 finishTransaction: SurfaceControl.Transaction, 106 finishCallback: Transitions.TransitionFinishCallback, 107 toShow: Boolean, // Whether to show or to hide the system modal 108 ) { 109 val startAlpha = if (toShow) 0f else 1f 110 val endAlpha = if (toShow) 1f else 0f 111 val animator = 112 createAlphaAnimator(SurfaceControl.Transaction(), leash, startAlpha, endAlpha) 113 animator.addListener( 114 onEnd = { _ -> 115 mainExecutor.execute { finishCallback.onTransitionFinished(/* wct= */ null) } 116 } 117 ) 118 if (toShow) { 119 finishTransaction.show(leash) 120 } else { 121 finishTransaction.hide(leash) 122 } 123 startTransaction.setAlpha(leash, startAlpha) 124 startTransaction.apply() 125 animExecutor.execute { animator.start() } 126 } 127 128 private fun getLaunchingSystemModal(info: TransitionInfo): TransitionInfo.Change? = 129 info.changes.find { change -> 130 if (!isOpeningMode(change.mode)) { 131 return@find false 132 } 133 val taskInfo = change.taskInfo ?: return@find false 134 return@find isSystemModal(taskInfo) 135 } 136 137 private fun getClosingSystemModal(info: TransitionInfo): TransitionInfo.Change? = 138 info.changes.find { change -> 139 if (!isClosingMode(change.mode)) { 140 return@find false 141 } 142 val taskInfo = change.taskInfo ?: return@find false 143 return@find isSystemModal(taskInfo) || showingSystemModalsIds.contains(taskInfo.taskId) 144 } 145 146 private fun isSystemModal(taskInfo: RunningTaskInfo): Boolean = 147 !DesktopWallpaperActivity.isWallpaperTask(taskInfo) && 148 desktopModeCompatPolicy.isTopActivityExemptFromDesktopWindowing(taskInfo) 149 150 private fun createAlphaAnimator( 151 transaction: SurfaceControl.Transaction, 152 leash: SurfaceControl, 153 startVal: Float, 154 endVal: Float, 155 ): ValueAnimator = 156 ValueAnimator.ofFloat(startVal, endVal).apply { 157 duration = LAUNCH_ANIM_ALPHA_DURATION_MS 158 interpolator = Interpolators.LINEAR 159 addUpdateListener { animation -> 160 transaction.setAlpha(leash, animation.animatedValue as Float).apply() 161 } 162 } 163 164 private fun isDesktopModeShowing(displayId: Int): Boolean = 165 desktopUserRepositories.current.isAnyDeskActive(displayId) 166 167 override fun handleRequest( 168 transition: IBinder, 169 request: TransitionRequestInfo, 170 ): WindowContainerTransaction? = null 171 172 private fun logV(msg: String, vararg arguments: Any?) { 173 ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) 174 } 175 176 companion object { 177 private const val TAG = "SystemModalsTransitionHandler" 178 private const val LAUNCH_ANIM_ALPHA_DURATION_MS = 150L 179 } 180 } 181