1 /* 2 * Copyright (C) 2021 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.pip; 18 19 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 20 import static android.view.WindowManager.TRANSIT_PIP; 21 22 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK; 23 import static com.android.wm.shell.pip.PipAnimationController.isInPipDirection; 24 25 import android.annotation.Nullable; 26 import android.app.PictureInPictureParams; 27 import android.app.TaskInfo; 28 import android.content.ComponentName; 29 import android.content.pm.ActivityInfo; 30 import android.graphics.Rect; 31 import android.os.IBinder; 32 import android.view.SurfaceControl; 33 import android.view.WindowManager; 34 import android.window.TransitionInfo; 35 import android.window.TransitionRequestInfo; 36 import android.window.WindowContainerTransaction; 37 38 import androidx.annotation.NonNull; 39 40 import com.android.wm.shell.ShellTaskOrganizer; 41 import com.android.wm.shell.common.pip.PipBoundsAlgorithm; 42 import com.android.wm.shell.common.pip.PipBoundsState; 43 import com.android.wm.shell.common.split.SplitScreenUtils; 44 import com.android.wm.shell.sysui.ShellInit; 45 import com.android.wm.shell.transition.Transitions; 46 47 import java.io.PrintWriter; 48 import java.util.ArrayList; 49 import java.util.List; 50 51 /** 52 * Responsible supplying PiP Transitions. 53 */ 54 public abstract class PipTransitionController implements Transitions.TransitionHandler { 55 56 protected final PipBoundsAlgorithm mPipBoundsAlgorithm; 57 protected final PipBoundsState mPipBoundsState; 58 protected final ShellTaskOrganizer mShellTaskOrganizer; 59 protected final PipMenuController mPipMenuController; 60 protected final Transitions mTransitions; 61 private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); 62 protected PipTaskOrganizer mPipOrganizer; 63 64 protected final PipAnimationController.PipAnimationCallback mPipAnimationCallback = 65 new PipAnimationController.PipAnimationCallback() { 66 @Override 67 public void onPipAnimationStart(TaskInfo taskInfo, 68 PipAnimationController.PipTransitionAnimator animator) { 69 final int direction = animator.getTransitionDirection(); 70 sendOnPipTransitionStarted(direction); 71 } 72 73 @Override 74 public void onPipAnimationEnd(TaskInfo taskInfo, SurfaceControl.Transaction tx, 75 PipAnimationController.PipTransitionAnimator animator) { 76 final int direction = animator.getTransitionDirection(); 77 mPipBoundsState.setBounds(animator.getDestinationBounds()); 78 if (direction == TRANSITION_DIRECTION_REMOVE_STACK) { 79 return; 80 } 81 if (isInPipDirection(direction) && animator.getContentOverlayLeash() != null) { 82 mPipOrganizer.fadeOutAndRemoveOverlay(animator.getContentOverlayLeash(), 83 animator::clearContentOverlay, true /* withStartDelay*/); 84 } 85 onFinishResize(taskInfo, animator.getDestinationBounds(), direction, tx); 86 sendOnPipTransitionFinished(direction); 87 } 88 89 @Override 90 public void onPipAnimationCancel(TaskInfo taskInfo, 91 PipAnimationController.PipTransitionAnimator animator) { 92 final int direction = animator.getTransitionDirection(); 93 if (isInPipDirection(direction) && animator.getContentOverlayLeash() != null) { 94 mPipOrganizer.fadeOutAndRemoveOverlay(animator.getContentOverlayLeash(), 95 animator::clearContentOverlay, true /* withStartDelay */); 96 } 97 sendOnPipTransitionCancelled(animator.getTransitionDirection()); 98 } 99 }; 100 101 /** 102 * Called when transition is about to finish. This is usually for performing tasks such as 103 * applying WindowContainerTransaction to finalize the PiP bounds and send to the framework. 104 */ onFinishResize(TaskInfo taskInfo, Rect destinationBounds, @PipAnimationController.TransitionDirection int direction, SurfaceControl.Transaction tx)105 public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds, 106 @PipAnimationController.TransitionDirection int direction, 107 SurfaceControl.Transaction tx) { 108 } 109 110 /** 111 * Called when the Shell wants to start an exit Pip transition/animation. 112 */ startExitTransition(int type, WindowContainerTransaction out, @Nullable Rect destinationBounds)113 public void startExitTransition(int type, WindowContainerTransaction out, 114 @Nullable Rect destinationBounds) { 115 // Default implementation does nothing. 116 } 117 118 /** 119 * Called when the transition animation can't continue (eg. task is removed during 120 * animation) 121 */ forceFinishTransition()122 public void forceFinishTransition() { 123 } 124 125 /** Called when the fixed rotation started. */ onFixedRotationStarted()126 public void onFixedRotationStarted() { 127 } 128 129 /** Called when the fixed rotation finished. */ onFixedRotationFinished()130 public void onFixedRotationFinished() { 131 } 132 PipTransitionController( @onNull ShellInit shellInit, @NonNull ShellTaskOrganizer shellTaskOrganizer, @NonNull Transitions transitions, PipBoundsState pipBoundsState, PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm)133 public PipTransitionController( 134 @NonNull ShellInit shellInit, 135 @NonNull ShellTaskOrganizer shellTaskOrganizer, 136 @NonNull Transitions transitions, 137 PipBoundsState pipBoundsState, 138 PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm) { 139 mPipBoundsState = pipBoundsState; 140 mPipMenuController = pipMenuController; 141 mShellTaskOrganizer = shellTaskOrganizer; 142 mPipBoundsAlgorithm = pipBoundsAlgorithm; 143 mTransitions = transitions; 144 if (Transitions.ENABLE_SHELL_TRANSITIONS) { 145 shellInit.addInitCallback(this::onInit, this); 146 } 147 } 148 onInit()149 protected void onInit() { 150 mTransitions.addHandler(this); 151 } 152 setPipOrganizer(PipTaskOrganizer pto)153 void setPipOrganizer(PipTaskOrganizer pto) { 154 mPipOrganizer = pto; 155 } 156 157 /** 158 * Registers {@link PipTransitionCallback} to receive transition callbacks. 159 */ registerPipTransitionCallback(PipTransitionCallback callback)160 public void registerPipTransitionCallback(PipTransitionCallback callback) { 161 mPipTransitionCallbacks.add(callback); 162 } 163 sendOnPipTransitionStarted( @ipAnimationController.TransitionDirection int direction)164 protected void sendOnPipTransitionStarted( 165 @PipAnimationController.TransitionDirection int direction) { 166 final Rect pipBounds = mPipBoundsState.getBounds(); 167 for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { 168 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); 169 callback.onPipTransitionStarted(direction, pipBounds); 170 } 171 } 172 sendOnPipTransitionFinished( @ipAnimationController.TransitionDirection int direction)173 protected void sendOnPipTransitionFinished( 174 @PipAnimationController.TransitionDirection int direction) { 175 for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { 176 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); 177 callback.onPipTransitionFinished(direction); 178 } 179 } 180 sendOnPipTransitionCancelled( @ipAnimationController.TransitionDirection int direction)181 protected void sendOnPipTransitionCancelled( 182 @PipAnimationController.TransitionDirection int direction) { 183 for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { 184 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); 185 callback.onPipTransitionCanceled(direction); 186 } 187 } 188 189 /** 190 * The windowing mode to restore to when resizing out of PIP direction. Defaults to undefined 191 * and can be overridden to restore to an alternate windowing mode. 192 */ getOutPipWindowingMode()193 public int getOutPipWindowingMode() { 194 // By default, simply reset the windowing mode to undefined. 195 return WINDOWING_MODE_UNDEFINED; 196 } 197 setBoundsStateForEntry(ComponentName componentName, PictureInPictureParams params, ActivityInfo activityInfo)198 protected void setBoundsStateForEntry(ComponentName componentName, 199 PictureInPictureParams params, 200 ActivityInfo activityInfo) { 201 mPipBoundsState.setBoundsStateForEntry(componentName, activityInfo, params, 202 mPipBoundsAlgorithm); 203 } 204 205 /** 206 * Called when the display is going to rotate. 207 * 208 * @return {@code true} if it was handled, otherwise the existing pip logic 209 * will deal with rotation. 210 */ handleRotateDisplay(int startRotation, int endRotation, WindowContainerTransaction wct)211 public boolean handleRotateDisplay(int startRotation, int endRotation, 212 WindowContainerTransaction wct) { 213 return false; 214 } 215 216 /** @return whether the transition-request represents a pip-entry. */ requestHasPipEnter(@onNull TransitionRequestInfo request)217 public boolean requestHasPipEnter(@NonNull TransitionRequestInfo request) { 218 return request.getType() == TRANSIT_PIP; 219 } 220 221 /** Whether a particular change is a window that is entering pip. */ isEnteringPip(@onNull TransitionInfo.Change change, @WindowManager.TransitionType int transitType)222 public boolean isEnteringPip(@NonNull TransitionInfo.Change change, 223 @WindowManager.TransitionType int transitType) { 224 return false; 225 } 226 227 /** Whether a particular package is same as current pip package. */ isInPipPackage(String packageName)228 public boolean isInPipPackage(String packageName) { 229 final TaskInfo inPipTask = mPipOrganizer.getTaskInfo(); 230 return packageName != null && inPipTask != null 231 && packageName.equals(SplitScreenUtils.getPackageName(inPipTask.baseIntent)); 232 } 233 234 /** Add PiP-related changes to `outWCT` for the given request. */ augmentRequest(@onNull IBinder transition, @NonNull TransitionRequestInfo request, @NonNull WindowContainerTransaction outWCT)235 public void augmentRequest(@NonNull IBinder transition, 236 @NonNull TransitionRequestInfo request, @NonNull WindowContainerTransaction outWCT) { 237 throw new IllegalStateException("Request isn't entering PiP"); 238 } 239 240 /** Sets the type of animation when a PiP task appears. */ setEnterAnimationType(@ipAnimationController.AnimationType int type)241 public void setEnterAnimationType(@PipAnimationController.AnimationType int type) { 242 } 243 244 /** Play a transition animation for entering PiP on a specific PiP change. */ startEnterAnimation(@onNull final TransitionInfo.Change pipChange, @NonNull final SurfaceControl.Transaction startTransaction, @NonNull final SurfaceControl.Transaction finishTransaction, @NonNull final Transitions.TransitionFinishCallback finishCallback)245 public void startEnterAnimation(@NonNull final TransitionInfo.Change pipChange, 246 @NonNull final SurfaceControl.Transaction startTransaction, 247 @NonNull final SurfaceControl.Transaction finishTransaction, 248 @NonNull final Transitions.TransitionFinishCallback finishCallback) { 249 } 250 251 /** 252 * Applies the proper surface states (rounded corners/shadows) to pip surfaces in `info`. 253 * This is intended to be used when PiP is part of another animation but isn't, itself, 254 * animating (eg. unlocking). 255 * @return `true` if there was a pip in `info`. 256 */ syncPipSurfaceState(@onNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull SurfaceControl.Transaction finishTransaction)257 public boolean syncPipSurfaceState(@NonNull TransitionInfo info, 258 @NonNull SurfaceControl.Transaction startTransaction, 259 @NonNull SurfaceControl.Transaction finishTransaction) { 260 return false; 261 } 262 263 /** End the currently-playing PiP animation. */ end()264 public void end() { 265 } 266 267 /** 268 * Callback interface for PiP transitions (both from and to PiP mode) 269 */ 270 public interface PipTransitionCallback { 271 /** 272 * Callback when the pip transition is started. 273 */ onPipTransitionStarted(int direction, Rect pipBounds)274 void onPipTransitionStarted(int direction, Rect pipBounds); 275 276 /** 277 * Callback when the pip transition is finished. 278 */ onPipTransitionFinished(int direction)279 void onPipTransitionFinished(int direction); 280 281 /** 282 * Callback when the pip transition is cancelled. 283 */ onPipTransitionCanceled(int direction)284 void onPipTransitionCanceled(int direction); 285 } 286 287 /** 288 * Dumps internal states. 289 */ dump(PrintWriter pw, String prefix)290 public void dump(PrintWriter pw, String prefix) {} 291 } 292