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 21 import static com.android.wm.shell.pip.PipAnimationController.TRANSITION_DIRECTION_REMOVE_STACK; 22 23 import android.app.PictureInPictureParams; 24 import android.app.TaskInfo; 25 import android.content.ComponentName; 26 import android.content.pm.ActivityInfo; 27 import android.graphics.Rect; 28 import android.os.Handler; 29 import android.os.Looper; 30 import android.view.SurfaceControl; 31 import android.window.WindowContainerTransaction; 32 33 import com.android.wm.shell.ShellTaskOrganizer; 34 import com.android.wm.shell.transition.Transitions; 35 36 import java.util.ArrayList; 37 import java.util.List; 38 39 /** 40 * Responsible supplying PiP Transitions. 41 */ 42 public abstract class PipTransitionController implements Transitions.TransitionHandler { 43 44 protected final PipAnimationController mPipAnimationController; 45 protected final PipBoundsAlgorithm mPipBoundsAlgorithm; 46 protected final PipBoundsState mPipBoundsState; 47 protected final ShellTaskOrganizer mShellTaskOrganizer; 48 protected final PipMenuController mPipMenuController; 49 protected final Transitions mTransitions; 50 private final Handler mMainHandler; 51 private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>(); 52 protected PipTaskOrganizer mPipOrganizer; 53 54 protected final PipAnimationController.PipAnimationCallback mPipAnimationCallback = 55 new PipAnimationController.PipAnimationCallback() { 56 @Override 57 public void onPipAnimationStart(TaskInfo taskInfo, 58 PipAnimationController.PipTransitionAnimator animator) { 59 final int direction = animator.getTransitionDirection(); 60 sendOnPipTransitionStarted(direction); 61 } 62 63 @Override 64 public void onPipAnimationEnd(TaskInfo taskInfo, SurfaceControl.Transaction tx, 65 PipAnimationController.PipTransitionAnimator animator) { 66 final int direction = animator.getTransitionDirection(); 67 mPipBoundsState.setBounds(animator.getDestinationBounds()); 68 if (direction == TRANSITION_DIRECTION_REMOVE_STACK) { 69 return; 70 } 71 onFinishResize(taskInfo, animator.getDestinationBounds(), direction, tx); 72 sendOnPipTransitionFinished(direction); 73 } 74 75 @Override 76 public void onPipAnimationCancel(TaskInfo taskInfo, 77 PipAnimationController.PipTransitionAnimator animator) { 78 sendOnPipTransitionCancelled(animator.getTransitionDirection()); 79 } 80 }; 81 82 /** 83 * Called when transition is about to finish. This is usually for performing tasks such as 84 * applying WindowContainerTransaction to finalize the PiP bounds and send to the framework. 85 */ onFinishResize(TaskInfo taskInfo, Rect destinationBounds, @PipAnimationController.TransitionDirection int direction, SurfaceControl.Transaction tx)86 public void onFinishResize(TaskInfo taskInfo, Rect destinationBounds, 87 @PipAnimationController.TransitionDirection int direction, 88 SurfaceControl.Transaction tx) { 89 } 90 91 /** 92 * Called to inform the transition that the animation should start with the assumption that 93 * PiP is not animating from its original bounds, but rather a continuation of another 94 * animation. For example, gesture navigation would first fade out the PiP activity, and the 95 * transition should be responsible to animate in (such as fade in) the PiP. 96 */ setIsFullAnimation(boolean isFullAnimation)97 public void setIsFullAnimation(boolean isFullAnimation) { 98 } 99 100 /** 101 * Called when the Shell wants to starts a transition/animation. 102 */ startTransition(Rect destinationBounds, WindowContainerTransaction out)103 public void startTransition(Rect destinationBounds, WindowContainerTransaction out) { 104 // Default implementation does nothing. 105 } 106 107 /** 108 * Called when the transition animation can't continue (eg. task is removed during 109 * animation) 110 */ forceFinishTransition()111 public void forceFinishTransition() { 112 } 113 PipTransitionController(PipBoundsState pipBoundsState, PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm, PipAnimationController pipAnimationController, Transitions transitions, @android.annotation.NonNull ShellTaskOrganizer shellTaskOrganizer)114 public PipTransitionController(PipBoundsState pipBoundsState, 115 PipMenuController pipMenuController, PipBoundsAlgorithm pipBoundsAlgorithm, 116 PipAnimationController pipAnimationController, Transitions transitions, 117 @android.annotation.NonNull ShellTaskOrganizer shellTaskOrganizer) { 118 mPipBoundsState = pipBoundsState; 119 mPipMenuController = pipMenuController; 120 mShellTaskOrganizer = shellTaskOrganizer; 121 mPipBoundsAlgorithm = pipBoundsAlgorithm; 122 mPipAnimationController = pipAnimationController; 123 mTransitions = transitions; 124 mMainHandler = new Handler(Looper.getMainLooper()); 125 if (Transitions.ENABLE_SHELL_TRANSITIONS) { 126 transitions.addHandler(this); 127 } 128 } 129 setPipOrganizer(PipTaskOrganizer pto)130 void setPipOrganizer(PipTaskOrganizer pto) { 131 mPipOrganizer = pto; 132 } 133 134 /** 135 * Registers {@link PipTransitionCallback} to receive transition callbacks. 136 */ registerPipTransitionCallback(PipTransitionCallback callback)137 public void registerPipTransitionCallback(PipTransitionCallback callback) { 138 mPipTransitionCallbacks.add(callback); 139 } 140 sendOnPipTransitionStarted( @ipAnimationController.TransitionDirection int direction)141 protected void sendOnPipTransitionStarted( 142 @PipAnimationController.TransitionDirection int direction) { 143 final Rect pipBounds = mPipBoundsState.getBounds(); 144 for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { 145 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); 146 callback.onPipTransitionStarted(direction, pipBounds); 147 } 148 } 149 sendOnPipTransitionFinished( @ipAnimationController.TransitionDirection int direction)150 protected void sendOnPipTransitionFinished( 151 @PipAnimationController.TransitionDirection int direction) { 152 for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { 153 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); 154 callback.onPipTransitionFinished(direction); 155 } 156 } 157 sendOnPipTransitionCancelled( @ipAnimationController.TransitionDirection int direction)158 protected void sendOnPipTransitionCancelled( 159 @PipAnimationController.TransitionDirection int direction) { 160 for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { 161 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); 162 callback.onPipTransitionCanceled(direction); 163 } 164 } 165 166 /** 167 * The windowing mode to restore to when resizing out of PIP direction. Defaults to undefined 168 * and can be overridden to restore to an alternate windowing mode. 169 */ getOutPipWindowingMode()170 public int getOutPipWindowingMode() { 171 // By default, simply reset the windowing mode to undefined. 172 return WINDOWING_MODE_UNDEFINED; 173 } 174 setBoundsStateForEntry(ComponentName componentName, PictureInPictureParams params, ActivityInfo activityInfo)175 protected void setBoundsStateForEntry(ComponentName componentName, 176 PictureInPictureParams params, 177 ActivityInfo activityInfo) { 178 mPipBoundsState.setBoundsStateForEntry(componentName, 179 mPipBoundsAlgorithm.getAspectRatioOrDefault(params), 180 mPipBoundsAlgorithm.getMinimalSize(activityInfo)); 181 } 182 183 /** 184 * Callback interface for PiP transitions (both from and to PiP mode) 185 */ 186 public interface PipTransitionCallback { 187 /** 188 * Callback when the pip transition is started. 189 */ onPipTransitionStarted(int direction, Rect pipBounds)190 void onPipTransitionStarted(int direction, Rect pipBounds); 191 192 /** 193 * Callback when the pip transition is finished. 194 */ onPipTransitionFinished(int direction)195 void onPipTransitionFinished(int direction); 196 197 /** 198 * Callback when the pip transition is cancelled. 199 */ onPipTransitionCanceled(int direction)200 void onPipTransitionCanceled(int direction); 201 } 202 } 203