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.sysui.ShellInit; 42 import com.android.wm.shell.transition.Transitions; 43 44 import java.util.ArrayList; 45 import java.util.List; 46 47 /** 48 * Responsible supplying PiP Transitions. 49 */ 50 public abstract class PipTransitionController implements Transitions.TransitionHandler { 51 52 protected final PipAnimationController mPipAnimationController; 53 protected final PipBoundsAlgorithm mPipBoundsAlgorithm; 54 protected final PipBoundsState mPipBoundsState; 55 protected final ShellTaskOrganizer mShellTaskOrganizer; 56 protected final PipMenuController mPipMenuController; 57 protected final Transitions mTransitions; 58 private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); 59 protected PipTaskOrganizer mPipOrganizer; 60 61 protected final PipAnimationController.PipAnimationCallback mPipAnimationCallback = 62 new PipAnimationController.PipAnimationCallback() { 63 @Override 64 public void onPipAnimationStart(TaskInfo taskInfo, 65 PipAnimationController.PipTransitionAnimator animator) { 66 final int direction = animator.getTransitionDirection(); 67 sendOnPipTransitionStarted(direction); 68 } 69 70 @Override 71 public void onPipAnimationEnd(TaskInfo taskInfo, SurfaceControl.Transaction tx, 72 PipAnimationController.PipTransitionAnimator animator) { 73 final int direction = animator.getTransitionDirection(); 74 mPipBoundsState.setBounds(animator.getDestinationBounds()); 75 if (direction == TRANSITION_DIRECTION_REMOVE_STACK) { 76 return; 77 } 78 if (isInPipDirection(direction) && animator.getContentOverlayLeash() != null) { 79 mPipOrganizer.fadeOutAndRemoveOverlay(animator.getContentOverlayLeash(), 80 animator::clearContentOverlay, true /* withStartDelay*/); 81 } 82 onFinishResize(taskInfo, animator.getDestinationBounds(), direction, tx); 83 sendOnPipTransitionFinished(direction); 84 } 85 86 @Override 87 public void onPipAnimationCancel(TaskInfo taskInfo, 88 PipAnimationController.PipTransitionAnimator animator) { 89 final int direction = animator.getTransitionDirection(); 90 if (isInPipDirection(direction) && animator.getContentOverlayLeash() != null) { 91 mPipOrganizer.fadeOutAndRemoveOverlay(animator.getContentOverlayLeash(), 92 animator::clearContentOverlay, true /* withStartDelay */); 93 } 94 sendOnPipTransitionCancelled(animator.getTransitionDirection()); 95 } 96 }; 97 98 /** 99 * Called when transition is about to finish. This is usually for performing tasks such as 100 * applying WindowContainerTransaction to finalize the PiP bounds and send to the framework. 101 */ onFinishResize(TaskInfo taskInfo, Rect destinationBounds, @PipAnimationController.TransitionDirection int direction, SurfaceControl.Transaction tx)102 public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds, 103 @PipAnimationController.TransitionDirection int direction, 104 SurfaceControl.Transaction tx) { 105 } 106 107 /** 108 * Called to inform the transition that the animation should start with the assumption that 109 * PiP is not animating from its original bounds, but rather a continuation of another 110 * animation. For example, gesture navigation would first fade out the PiP activity, and the 111 * transition should be responsible to animate in (such as fade in) the PiP. 112 */ setIsFullAnimation(boolean isFullAnimation)113 public void setIsFullAnimation(boolean isFullAnimation) { 114 } 115 116 /** 117 * Called when the Shell wants to start an exit Pip transition/animation. 118 */ startExitTransition(int type, WindowContainerTransaction out, @Nullable Rect destinationBounds)119 public void startExitTransition(int type, WindowContainerTransaction out, 120 @Nullable Rect destinationBounds) { 121 // Default implementation does nothing. 122 } 123 124 /** 125 * Called when the transition animation can't continue (eg. task is removed during 126 * animation) 127 */ forceFinishTransition()128 public void forceFinishTransition() { 129 } 130 131 /** Called when the fixed rotation started. */ onFixedRotationStarted()132 public void onFixedRotationStarted() { 133 } 134 PipTransitionController( @onNull ShellInit shellInit, @NonNull ShellTaskOrganizer shellTaskOrganizer, @NonNull Transitions transitions, PipBoundsState pipBoundsState, PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm, PipAnimationController pipAnimationController)135 public PipTransitionController( 136 @NonNull ShellInit shellInit, 137 @NonNull ShellTaskOrganizer shellTaskOrganizer, 138 @NonNull Transitions transitions, 139 PipBoundsState pipBoundsState, 140 PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm, 141 PipAnimationController pipAnimationController) { 142 mPipBoundsState = pipBoundsState; 143 mPipMenuController = pipMenuController; 144 mShellTaskOrganizer = shellTaskOrganizer; 145 mPipBoundsAlgorithm = pipBoundsAlgorithm; 146 mPipAnimationController = pipAnimationController; 147 mTransitions = transitions; 148 if (Transitions.ENABLE_SHELL_TRANSITIONS) { 149 shellInit.addInitCallback(this::onInit, this); 150 } 151 } 152 onInit()153 private void onInit() { 154 mTransitions.addHandler(this); 155 } 156 setPipOrganizer(PipTaskOrganizer pto)157 void setPipOrganizer(PipTaskOrganizer pto) { 158 mPipOrganizer = pto; 159 } 160 161 /** 162 * Registers {@link PipTransitionCallback} to receive transition callbacks. 163 */ registerPipTransitionCallback(PipTransitionCallback callback)164 public void registerPipTransitionCallback(PipTransitionCallback callback) { 165 mPipTransitionCallbacks.add(callback); 166 } 167 sendOnPipTransitionStarted( @ipAnimationController.TransitionDirection int direction)168 protected void sendOnPipTransitionStarted( 169 @PipAnimationController.TransitionDirection int direction) { 170 final Rect pipBounds = mPipBoundsState.getBounds(); 171 for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { 172 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); 173 callback.onPipTransitionStarted(direction, pipBounds); 174 } 175 } 176 sendOnPipTransitionFinished( @ipAnimationController.TransitionDirection int direction)177 protected void sendOnPipTransitionFinished( 178 @PipAnimationController.TransitionDirection int direction) { 179 for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { 180 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); 181 callback.onPipTransitionFinished(direction); 182 } 183 } 184 sendOnPipTransitionCancelled( @ipAnimationController.TransitionDirection int direction)185 protected void sendOnPipTransitionCancelled( 186 @PipAnimationController.TransitionDirection int direction) { 187 for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { 188 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); 189 callback.onPipTransitionCanceled(direction); 190 } 191 } 192 193 /** 194 * The windowing mode to restore to when resizing out of PIP direction. Defaults to undefined 195 * and can be overridden to restore to an alternate windowing mode. 196 */ getOutPipWindowingMode()197 public int getOutPipWindowingMode() { 198 // By default, simply reset the windowing mode to undefined. 199 return WINDOWING_MODE_UNDEFINED; 200 } 201 setBoundsStateForEntry(ComponentName componentName, PictureInPictureParams params, ActivityInfo activityInfo)202 protected void setBoundsStateForEntry(ComponentName componentName, 203 PictureInPictureParams params, 204 ActivityInfo activityInfo) { 205 mPipBoundsState.setBoundsStateForEntry(componentName, activityInfo, params, 206 mPipBoundsAlgorithm); 207 } 208 209 /** 210 * Called when the display is going to rotate. 211 * 212 * @return {@code true} if it was handled, otherwise the existing pip logic 213 * will deal with rotation. 214 */ handleRotateDisplay(int startRotation, int endRotation, WindowContainerTransaction wct)215 public boolean handleRotateDisplay(int startRotation, int endRotation, 216 WindowContainerTransaction wct) { 217 return false; 218 } 219 220 /** @return whether the transition-request represents a pip-entry. */ requestHasPipEnter(@onNull TransitionRequestInfo request)221 public boolean requestHasPipEnter(@NonNull TransitionRequestInfo request) { 222 return request.getType() == TRANSIT_PIP; 223 } 224 225 /** Whether a particular change is a window that is entering pip. */ isEnteringPip(@onNull TransitionInfo.Change change, @WindowManager.TransitionType int transitType)226 public boolean isEnteringPip(@NonNull TransitionInfo.Change change, 227 @WindowManager.TransitionType int transitType) { 228 return false; 229 } 230 231 /** Add PiP-related changes to `outWCT` for the given request. */ augmentRequest(@onNull IBinder transition, @NonNull TransitionRequestInfo request, @NonNull WindowContainerTransaction outWCT)232 public void augmentRequest(@NonNull IBinder transition, 233 @NonNull TransitionRequestInfo request, @NonNull WindowContainerTransaction outWCT) { 234 throw new IllegalStateException("Request isn't entering PiP"); 235 } 236 237 /** 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)238 public void startEnterAnimation(@NonNull final TransitionInfo.Change pipChange, 239 @NonNull final SurfaceControl.Transaction startTransaction, 240 @NonNull final SurfaceControl.Transaction finishTransaction, 241 @NonNull final Transitions.TransitionFinishCallback finishCallback) { 242 } 243 244 /** End the currently-playing PiP animation. */ end()245 public void end() { 246 } 247 248 /** 249 * Callback interface for PiP transitions (both from and to PiP mode) 250 */ 251 public interface PipTransitionCallback { 252 /** 253 * Callback when the pip transition is started. 254 */ onPipTransitionStarted(int direction, Rect pipBounds)255 void onPipTransitionStarted(int direction, Rect pipBounds); 256 257 /** 258 * Callback when the pip transition is finished. 259 */ onPipTransitionFinished(int direction)260 void onPipTransitionFinished(int direction); 261 262 /** 263 * Callback when the pip transition is cancelled. 264 */ onPipTransitionCanceled(int direction)265 void onPipTransitionCanceled(int direction); 266 } 267 } 268