1 /* 2 * Copyright (C) 2023 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.transition; 18 19 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_UNOCCLUDING; 20 21 import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED; 22 import static com.android.wm.shell.transition.DefaultMixedHandler.handoverTransitionLeashes; 23 import static com.android.wm.shell.transition.MixedTransitionHelper.animateEnterPipFromSplit; 24 import static com.android.wm.shell.transition.MixedTransitionHelper.animateKeyguard; 25 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.os.IBinder; 29 import android.view.SurfaceControl; 30 import android.window.TransitionInfo; 31 import android.window.WindowContainerTransaction; 32 33 import com.android.internal.protolog.ProtoLog; 34 import com.android.wm.shell.desktopmode.DesktopTasksController; 35 import com.android.wm.shell.keyguard.KeyguardTransitionHandler; 36 import com.android.wm.shell.pip.PipTransitionController; 37 import com.android.wm.shell.protolog.ShellProtoLogGroup; 38 import com.android.wm.shell.recents.RecentsTransitionHandler; 39 import com.android.wm.shell.splitscreen.StageCoordinator; 40 41 class RecentsMixedTransition extends DefaultMixedHandler.MixedTransition { 42 private final RecentsTransitionHandler mRecentsHandler; 43 private final DesktopTasksController mDesktopTasksController; 44 RecentsMixedTransition(int type, IBinder transition, Transitions player, MixedTransitionHandler mixedHandler, PipTransitionController pipHandler, StageCoordinator splitHandler, KeyguardTransitionHandler keyguardHandler, RecentsTransitionHandler recentsHandler, DesktopTasksController desktopTasksController)45 RecentsMixedTransition(int type, IBinder transition, Transitions player, 46 MixedTransitionHandler mixedHandler, PipTransitionController pipHandler, 47 StageCoordinator splitHandler, KeyguardTransitionHandler keyguardHandler, 48 RecentsTransitionHandler recentsHandler, 49 DesktopTasksController desktopTasksController) { 50 super(type, transition, player, mixedHandler, pipHandler, splitHandler, keyguardHandler); 51 mRecentsHandler = recentsHandler; 52 mDesktopTasksController = desktopTasksController; 53 mLeftoversHandler = mRecentsHandler; 54 } 55 56 @Override startAnimation( @onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback)57 boolean startAnimation( 58 @NonNull IBinder transition, @NonNull TransitionInfo info, 59 @NonNull SurfaceControl.Transaction startTransaction, 60 @NonNull SurfaceControl.Transaction finishTransaction, 61 @NonNull Transitions.TransitionFinishCallback finishCallback) { 62 return switch (mType) { 63 case TYPE_RECENTS_DURING_DESKTOP -> 64 animateRecentsDuringDesktop( 65 info, startTransaction, finishTransaction, finishCallback); 66 case TYPE_RECENTS_DURING_KEYGUARD -> 67 animateRecentsDuringKeyguard( 68 info, startTransaction, finishTransaction, finishCallback); 69 case TYPE_RECENTS_DURING_SPLIT -> 70 animateRecentsDuringSplit( 71 info, startTransaction, finishTransaction, finishCallback); 72 default -> throw new IllegalStateException( 73 "Starting Recents mixed animation with unknown or illegal type: " + mType); 74 }; 75 } 76 animateRecentsDuringDesktop( @onNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback)77 private boolean animateRecentsDuringDesktop( 78 @NonNull TransitionInfo info, 79 @NonNull SurfaceControl.Transaction startTransaction, 80 @NonNull SurfaceControl.Transaction finishTransaction, 81 @NonNull Transitions.TransitionFinishCallback finishCallback) { 82 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition for Recents during" 83 + " Desktop #%d", info.getDebugId()); 84 85 if (mInfo == null) { 86 mInfo = info; 87 mFinishT = finishTransaction; 88 mFinishCB = finishCallback; 89 } 90 Transitions.TransitionFinishCallback finishCB = wct -> { 91 mInFlightSubAnimations--; 92 if (mInFlightSubAnimations == 0) { 93 finishCallback.onTransitionFinished(wct); 94 } 95 }; 96 97 mInFlightSubAnimations++; 98 boolean consumed = mRecentsHandler.startAnimation( 99 mTransition, info, startTransaction, finishTransaction, finishCB); 100 if (!consumed) { 101 mInFlightSubAnimations--; 102 return false; 103 } 104 if (mDesktopTasksController != null) { 105 mDesktopTasksController.syncSurfaceState(info, finishTransaction); 106 return true; 107 } 108 109 return false; 110 } 111 animateRecentsDuringKeyguard( @onNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback)112 private boolean animateRecentsDuringKeyguard( 113 @NonNull TransitionInfo info, 114 @NonNull SurfaceControl.Transaction startTransaction, 115 @NonNull SurfaceControl.Transaction finishTransaction, 116 @NonNull Transitions.TransitionFinishCallback finishCallback) { 117 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Mixed transition for Recents during" 118 + " Keyguard #%d", info.getDebugId()); 119 120 if (!mKeyguardHandler.isKeyguardShowing() || mKeyguardHandler.isKeyguardAnimating()) { 121 ProtoLog.w(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Cancel mixed transition because " 122 + "keyguard state was changed #%d", info.getDebugId()); 123 return false; 124 } 125 if (mInfo == null) { 126 mInfo = info; 127 mFinishT = finishTransaction; 128 mFinishCB = finishCallback; 129 } 130 return startSubAnimation(mRecentsHandler, info, startTransaction, finishTransaction); 131 } 132 animateRecentsDuringSplit( @onNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction, @NonNull Transitions.TransitionFinishCallback finishCallback)133 private boolean animateRecentsDuringSplit( 134 @NonNull TransitionInfo info, 135 @NonNull SurfaceControl.Transaction startTransaction, 136 @NonNull SurfaceControl.Transaction finishTransaction, 137 @NonNull Transitions.TransitionFinishCallback finishCallback) { 138 ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Mixed transition for Recents during" 139 + " split screen #%d", info.getDebugId()); 140 141 for (int i = info.getChanges().size() - 1; i >= 0; --i) { 142 final TransitionInfo.Change change = info.getChanges().get(i); 143 // Pip auto-entering info might be appended to recent transition like pressing 144 // home-key in 3-button navigation. This offers split handler the opportunity to 145 // handle split to pip animation. 146 if (mPipHandler.isEnteringPip(change, info.getType()) 147 && mSplitHandler.getSplitItemPosition(change.getLastParent()) 148 != SPLIT_POSITION_UNDEFINED) { 149 return animateEnterPipFromSplit(this, info, startTransaction, finishTransaction, 150 finishCallback, mPlayer, mMixedHandler, mPipHandler, mSplitHandler, 151 /*replacingPip*/ false); 152 } 153 } 154 155 // Split-screen is only interested in the recents transition finishing (and merging), so 156 // just wrap finish and start recents animation directly. 157 Transitions.TransitionFinishCallback finishCB = (wct) -> { 158 mInFlightSubAnimations = 0; 159 // If pair-to-pair switching, the post-recents clean-up isn't needed. 160 wct = wct != null ? wct : new WindowContainerTransaction(); 161 if (mAnimType != ANIM_TYPE_PAIR_TO_PAIR) { 162 // We've dispatched to the mLeftoversHandler to handle the rest of the transition 163 // and called onRecentsInSplitAnimationStart(), but if the recents handler is not 164 // actually handling the transition, then onRecentsInSplitAnimationFinishing() 165 // won't actually get called by the recents handler. In such cases, we still need 166 // to clean up after the changes from the start call. 167 boolean splitNotifiedByRecents = mRecentsHandler == mLeftoversHandler; 168 if (!splitNotifiedByRecents) { 169 mSplitHandler.onRecentsInSplitAnimationFinishing( 170 mSplitHandler.wctIsReorderingSplitToTop(wct), 171 wct, finishTransaction); 172 } 173 } else { 174 // notify pair-to-pair recents animation finish 175 mSplitHandler.onRecentsPairToPairAnimationFinish(wct); 176 } 177 mSplitHandler.onTransitionAnimationComplete(); 178 finishCallback.onTransitionFinished(wct); 179 }; 180 mInFlightSubAnimations = 1; 181 mSplitHandler.onRecentsInSplitAnimationStart(info); 182 final boolean handled = mLeftoversHandler.startAnimation( 183 mTransition, info, startTransaction, finishTransaction, finishCB); 184 if (!handled) { 185 mSplitHandler.onRecentsInSplitAnimationCanceled(); 186 } 187 return handled; 188 } 189 190 /** 191 * Called when the recents animation during split is about to finish. 192 */ onAnimateRecentsDuringSplitFinishing(boolean returnToApp, @NonNull WindowContainerTransaction finishWct, @NonNull SurfaceControl.Transaction finishT)193 void onAnimateRecentsDuringSplitFinishing(boolean returnToApp, 194 @NonNull WindowContainerTransaction finishWct, 195 @NonNull SurfaceControl.Transaction finishT) { 196 if (mAnimType != ANIM_TYPE_PAIR_TO_PAIR) { 197 mSplitHandler.onRecentsInSplitAnimationFinishing(returnToApp, finishWct, finishT); 198 } 199 } 200 201 /** 202 * Called when the recents animation during desktop is about to finish. 203 */ onAnimateRecentsDuringDesktopFinishing(boolean returnToApp, @NonNull WindowContainerTransaction finishWct)204 void onAnimateRecentsDuringDesktopFinishing(boolean returnToApp, 205 @NonNull WindowContainerTransaction finishWct) { 206 mDesktopTasksController.onRecentsInDesktopAnimationFinishing(mTransition, finishWct, 207 returnToApp); 208 } 209 210 @Override mergeAnimation( @onNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, @NonNull IBinder mergeTarget, @NonNull Transitions.TransitionFinishCallback finishCallback)211 void mergeAnimation( 212 @NonNull IBinder transition, @NonNull TransitionInfo info, 213 @NonNull SurfaceControl.Transaction startT, @NonNull SurfaceControl.Transaction finishT, 214 @NonNull IBinder mergeTarget, 215 @NonNull Transitions.TransitionFinishCallback finishCallback) { 216 switch (mType) { 217 case TYPE_RECENTS_DURING_DESKTOP: 218 mLeftoversHandler.mergeAnimation(transition, info, startT, finishT, mergeTarget, 219 finishCallback); 220 return; 221 case TYPE_RECENTS_DURING_KEYGUARD: 222 if ((info.getFlags() & TRANSIT_FLAG_KEYGUARD_UNOCCLUDING) != 0) { 223 handoverTransitionLeashes(mInfo, info, startT, finishT); 224 if (animateKeyguard( 225 this, info, startT, finishT, mFinishCB, mKeyguardHandler, 226 mPipHandler)) { 227 finishCallback.onTransitionFinished(null); 228 } 229 } 230 mLeftoversHandler.mergeAnimation(transition, info, startT, finishT, mergeTarget, 231 finishCallback); 232 return; 233 case TYPE_RECENTS_DURING_SPLIT: 234 if (mSplitHandler.isPendingEnter(transition)) { 235 // Recents -> enter-split means that we are switching from one pair to 236 // another pair. 237 mAnimType = DefaultMixedHandler.MixedTransition.ANIM_TYPE_PAIR_TO_PAIR; 238 } 239 mLeftoversHandler.mergeAnimation(transition, info, startT, finishT, mergeTarget, 240 finishCallback); 241 return; 242 default: 243 throw new IllegalStateException("Playing a Recents mixed transition with unknown or" 244 + " illegal type: " + mType); 245 } 246 } 247 248 @Override onTransitionConsumed( @onNull IBinder transition, boolean aborted, @Nullable SurfaceControl.Transaction finishT)249 void onTransitionConsumed( 250 @NonNull IBinder transition, boolean aborted, 251 @Nullable SurfaceControl.Transaction finishT) { 252 switch (mType) { 253 case TYPE_RECENTS_DURING_DESKTOP: 254 case TYPE_RECENTS_DURING_SPLIT: 255 case TYPE_RECENTS_DURING_KEYGUARD: 256 mLeftoversHandler.onTransitionConsumed(transition, aborted, finishT); 257 break; 258 default: 259 break; 260 } 261 262 if (mHasRequestToRemote) { 263 mPlayer.getRemoteTransitionHandler().onTransitionConsumed(transition, aborted, finishT); 264 } 265 } 266 } 267