blob: d9243467835780ae35f2912b9d21ccd7a238ba6f [file] [log] [blame]
Hongwei Wang85cf41f2020-01-15 15:14:47 -08001/*
2 * Copyright (C) 2020 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
17package com.android.systemui.pip;
18
Winson Chungc4d4ee82020-05-05 12:51:06 -070019import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
20import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
21import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
Hongwei Wangd39583a2020-03-04 11:14:32 -080022import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
23
Hongwei Wang85cf41f2020-01-15 15:14:47 -080024import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA;
25import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
Hongwei Wangdf8bb002020-03-03 17:41:02 -080026import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_NONE;
27import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_SAME;
28import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_FULLSCREEN;
29import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
Hongwei Wang5c52ff82020-04-20 16:02:30 -070030import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_SPLIT_SCREEN;
Winson Chungc4d4ee82020-05-05 12:51:06 -070031import static com.android.systemui.pip.PipAnimationController.isInPipDirection;
Hongwei Wang5c52ff82020-04-20 16:02:30 -070032import static com.android.systemui.pip.PipAnimationController.isOutPipDirection;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080033
34import android.annotation.NonNull;
35import android.annotation.Nullable;
36import android.app.ActivityManager;
Winson Chungc4d4ee82020-05-05 12:51:06 -070037import android.app.ActivityTaskManager;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080038import android.app.PictureInPictureParams;
Hongwei Wang221fe3d2020-03-26 13:13:04 -070039import android.content.ComponentName;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080040import android.content.Context;
Hongwei Wang2e725be2020-03-10 11:01:28 -070041import android.content.pm.ActivityInfo;
Winson Chungc4d4ee82020-05-05 12:51:06 -070042import android.content.res.Configuration;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080043import android.graphics.Rect;
44import android.os.Handler;
Hongwei Wangfbc25fe2020-03-16 11:59:28 -070045import android.os.IBinder;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080046import android.os.Looper;
Winson Chungc4d4ee82020-05-05 12:51:06 -070047import android.os.RemoteException;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080048import android.util.Log;
Hongwei Wang2e725be2020-03-10 11:01:28 -070049import android.util.Size;
Hongwei Wangd39583a2020-03-04 11:14:32 -080050import android.view.SurfaceControl;
Wale Ogunwaleadf116e2020-03-27 16:36:01 -070051import android.window.TaskOrganizer;
52import android.window.WindowContainerToken;
Wale Ogunwale57946582020-03-21 14:29:07 -070053import android.window.WindowContainerTransaction;
Winson Chungc4d4ee82020-05-05 12:51:06 -070054import android.window.WindowContainerTransactionCallback;
Wale Ogunwale568f9f412020-03-21 22:27:35 -070055import android.window.WindowOrganizer;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080056
Winson Chung55701472020-03-04 19:30:30 -080057import com.android.internal.os.SomeArgs;
Hongwei Wangdf8bb002020-03-03 17:41:02 -080058import com.android.systemui.R;
Winson Chung55701472020-03-04 19:30:30 -080059import com.android.systemui.pip.phone.PipUpdateThread;
Hongwei Wang5c52ff82020-04-20 16:02:30 -070060import com.android.systemui.stackdivider.Divider;
Hongwei Wangf4e4bab2020-05-19 11:28:32 -070061import com.android.systemui.wm.DisplayController;
Winson Chung55701472020-03-04 19:30:30 -080062
Winson Chungc4d4ee82020-05-05 12:51:06 -070063import java.io.PrintWriter;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080064import java.util.ArrayList;
Hongwei Wangfbc25fe2020-03-16 11:59:28 -070065import java.util.HashMap;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080066import java.util.List;
Hongwei Wangfbc25fe2020-03-16 11:59:28 -070067import java.util.Map;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080068import java.util.Objects;
Winson Chung55701472020-03-04 19:30:30 -080069import java.util.function.Consumer;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080070
Ben Lin6189fa42020-04-29 14:59:16 -070071import javax.inject.Inject;
72import javax.inject.Singleton;
73
Hongwei Wang85cf41f2020-01-15 15:14:47 -080074/**
75 * Manages PiP tasks such as resize and offset.
76 *
Wale Ogunwaleadf116e2020-03-27 16:36:01 -070077 * This class listens on {@link TaskOrganizer} callbacks for windowing mode change
Hongwei Wang85cf41f2020-01-15 15:14:47 -080078 * both to and from PiP and issues corresponding animation if applicable.
79 * Normally, we apply series of {@link SurfaceControl.Transaction} when the animator is running
80 * and files a final {@link WindowContainerTransaction} at the end of the transition.
81 *
82 * This class is also responsible for general resize/offset PiP operations within SysUI component,
83 * see also {@link com.android.systemui.pip.phone.PipMotionHelper}.
84 */
Ben Lin6189fa42020-04-29 14:59:16 -070085@Singleton
Hongwei Wangf4e4bab2020-05-19 11:28:32 -070086public class PipTaskOrganizer extends TaskOrganizer implements
87 DisplayController.OnDisplaysChangedListener {
Hongwei Wang85cf41f2020-01-15 15:14:47 -080088 private static final String TAG = PipTaskOrganizer.class.getSimpleName();
Hongwei Wangf4e4bab2020-05-19 11:28:32 -070089 private static final boolean DEBUG = false;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080090
Winson Chung55701472020-03-04 19:30:30 -080091 private static final int MSG_RESIZE_IMMEDIATE = 1;
92 private static final int MSG_RESIZE_ANIMATE = 2;
93 private static final int MSG_OFFSET_ANIMATE = 3;
94 private static final int MSG_FINISH_RESIZE = 4;
Ben Lin75ba9c32020-03-19 17:55:12 -070095 private static final int MSG_RESIZE_USER = 5;
Winson Chung55701472020-03-04 19:30:30 -080096
Hongwei Wang85cf41f2020-01-15 15:14:47 -080097 private final Handler mMainHandler;
Winson Chung55701472020-03-04 19:30:30 -080098 private final Handler mUpdateHandler;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080099 private final PipBoundsHandler mPipBoundsHandler;
100 private final PipAnimationController mPipAnimationController;
101 private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800102 private final Rect mLastReportedBounds = new Rect();
Ben Linede9a602020-02-26 12:16:09 -0800103 private final int mEnterExitAnimationDuration;
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700104 private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
Winson Chungc4d4ee82020-05-05 12:51:06 -0700105 private final Map<IBinder, Configuration> mInitialState = new HashMap<>();
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700106 private final Divider mSplitDivider;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800107
Winson Chung55701472020-03-04 19:30:30 -0800108 // These callbacks are called on the update thread
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800109 private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
110 new PipAnimationController.PipAnimationCallback() {
111 @Override
Winson Chung55701472020-03-04 19:30:30 -0800112 public void onPipAnimationStart(PipAnimationController.PipTransitionAnimator animator) {
Hongwei Wang3d11e1f2020-05-28 15:48:06 -0700113 sendOnPipTransitionStarted(animator.getTransitionDirection());
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800114 }
115
116 @Override
Winson Chung55701472020-03-04 19:30:30 -0800117 public void onPipAnimationEnd(SurfaceControl.Transaction tx,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800118 PipAnimationController.PipTransitionAnimator animator) {
Winson Chungc4d4ee82020-05-05 12:51:06 -0700119 finishResize(tx, animator.getDestinationBounds(), animator.getTransitionDirection(),
120 animator.getAnimationType());
Hongwei Wang3d11e1f2020-05-28 15:48:06 -0700121 sendOnPipTransitionFinished(animator.getTransitionDirection());
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800122 }
123
124 @Override
Winson Chung55701472020-03-04 19:30:30 -0800125 public void onPipAnimationCancel(PipAnimationController.PipTransitionAnimator animator) {
Hongwei Wang3d11e1f2020-05-28 15:48:06 -0700126 sendOnPipTransitionCancelled(animator.getTransitionDirection());
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800127 }
128 };
129
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800130 @SuppressWarnings("unchecked")
Hongwei Wange8e32862020-04-08 13:23:45 -0700131 private final Handler.Callback mUpdateCallbacks = (msg) -> {
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800132 SomeArgs args = (SomeArgs) msg.obj;
133 Consumer<Rect> updateBoundsCallback = (Consumer<Rect>) args.arg1;
134 switch (msg.what) {
135 case MSG_RESIZE_IMMEDIATE: {
136 Rect toBounds = (Rect) args.arg2;
137 resizePip(toBounds);
138 if (updateBoundsCallback != null) {
139 updateBoundsCallback.accept(toBounds);
Winson Chung55701472020-03-04 19:30:30 -0800140 }
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800141 break;
Winson Chung55701472020-03-04 19:30:30 -0800142 }
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800143 case MSG_RESIZE_ANIMATE: {
144 Rect currentBounds = (Rect) args.arg2;
145 Rect toBounds = (Rect) args.arg3;
146 int duration = args.argi2;
Winson Chungd7302542020-06-08 17:09:45 +0000147 animateResizePip(currentBounds, toBounds, args.argi1 /* direction */, duration);
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800148 if (updateBoundsCallback != null) {
149 updateBoundsCallback.accept(toBounds);
150 }
151 break;
152 }
153 case MSG_OFFSET_ANIMATE: {
154 Rect originalBounds = (Rect) args.arg2;
155 final int offset = args.argi1;
156 final int duration = args.argi2;
157 offsetPip(originalBounds, 0 /* xOffset */, offset, duration);
158 Rect toBounds = new Rect(originalBounds);
159 toBounds.offset(0, offset);
160 if (updateBoundsCallback != null) {
161 updateBoundsCallback.accept(toBounds);
162 }
163 break;
164 }
165 case MSG_FINISH_RESIZE: {
166 SurfaceControl.Transaction tx = (SurfaceControl.Transaction) args.arg2;
167 Rect toBounds = (Rect) args.arg3;
Winson Chungc4d4ee82020-05-05 12:51:06 -0700168 finishResize(tx, toBounds, args.argi1 /* direction */, -1);
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800169 if (updateBoundsCallback != null) {
170 updateBoundsCallback.accept(toBounds);
171 }
172 break;
173 }
Ben Lin75ba9c32020-03-19 17:55:12 -0700174 case MSG_RESIZE_USER: {
175 Rect startBounds = (Rect) args.arg2;
176 Rect toBounds = (Rect) args.arg3;
177 userResizePip(startBounds, toBounds);
178 break;
179 }
Winson Chung55701472020-03-04 19:30:30 -0800180 }
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800181 args.recycle();
182 return true;
Winson Chung55701472020-03-04 19:30:30 -0800183 };
184
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800185 private ActivityManager.RunningTaskInfo mTaskInfo;
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700186 private WindowContainerToken mToken;
Winson Chung55701472020-03-04 19:30:30 -0800187 private SurfaceControl mLeash;
188 private boolean mInPip;
Winson Chungc46cbae2020-05-27 21:36:09 -0700189 private boolean mExitingPip;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800190 private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700191 private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
192 mSurfaceControlTransactionFactory;
Hongwei Wang8c95ce52020-04-30 15:06:12 -0700193 private PictureInPictureParams mPictureInPictureParams;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800194
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700195 /**
196 * If set to {@code true}, the entering animation will be skipped and we will wait for
197 * {@link #onFixedRotationFinished(int)} callback to actually enter PiP.
198 */
199 private boolean mShouldDeferEnteringPip;
200
Ben Lin6189fa42020-04-29 14:59:16 -0700201 @Inject
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700202 public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler,
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700203 @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700204 @Nullable Divider divider,
jorgegil@google.com0c449ec2020-06-16 16:30:07 -0700205 @NonNull DisplayController displayController,
206 @NonNull PipAnimationController pipAnimationController) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800207 mMainHandler = new Handler(Looper.getMainLooper());
Winson Chung55701472020-03-04 19:30:30 -0800208 mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800209 mPipBoundsHandler = boundsHandler;
Ben Linede9a602020-02-26 12:16:09 -0800210 mEnterExitAnimationDuration = context.getResources()
211 .getInteger(R.integer.config_pipResizeAnimationDuration);
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700212 mSurfaceTransactionHelper = surfaceTransactionHelper;
jorgegil@google.com0c449ec2020-06-16 16:30:07 -0700213 mPipAnimationController = pipAnimationController;
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700214 mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700215 mSplitDivider = divider;
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700216 displayController.addDisplayWindowListener(this);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800217 }
218
Winson Chung55701472020-03-04 19:30:30 -0800219 public Handler getUpdateHandler() {
220 return mUpdateHandler;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800221 }
222
Hongwei Wang221fe3d2020-03-26 13:13:04 -0700223 public Rect getLastReportedBounds() {
224 return new Rect(mLastReportedBounds);
225 }
226
Joshua Tsuji0d4cbeb2020-05-01 12:45:41 -0400227 public boolean isInPip() {
228 return mInPip;
229 }
230
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800231 /**
232 * Registers {@link PipTransitionCallback} to receive transition callbacks.
233 */
234 public void registerPipTransitionCallback(PipTransitionCallback callback) {
235 mPipTransitionCallbacks.add(callback);
236 }
237
238 /**
239 * Sets the preferred animation type for one time.
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800240 * This is typically used to set the animation type to
241 * {@link PipAnimationController#ANIM_TYPE_ALPHA}.
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800242 */
243 public void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) {
244 mOneShotAnimationType = animationType;
245 }
246
Hongwei Wangd39583a2020-03-04 11:14:32 -0800247 /**
Winson Chungc4d4ee82020-05-05 12:51:06 -0700248 * Expands PiP to the previous bounds, this is done in two phases using
249 * {@link WindowContainerTransaction}
250 * - setActivityWindowingMode to either fullscreen or split-secondary at beginning of the
251 * transaction. without changing the windowing mode of the Task itself. This makes sure the
252 * activity render it's final configuration while the Task is still in PiP.
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700253 * - setWindowingMode to undefined at the end of transition
Hongwei Wangd39583a2020-03-04 11:14:32 -0800254 * @param animationDurationMs duration in millisecond for the exiting PiP transition
255 */
Winson Chungc4d4ee82020-05-05 12:51:06 -0700256 public void exitPip(int animationDurationMs) {
Winson Chungc46cbae2020-05-27 21:36:09 -0700257 if (!mInPip || mExitingPip || mToken == null) {
Winson Chungc4d4ee82020-05-05 12:51:06 -0700258 Log.wtf(TAG, "Not allowed to exitPip in current state"
Winson Chungc46cbae2020-05-27 21:36:09 -0700259 + " mInPip=" + mInPip + " mExitingPip=" + mExitingPip + " mToken=" + mToken);
Hongwei Wang1273c952020-04-30 13:28:29 -0700260 return;
261 }
Winson Chungc4d4ee82020-05-05 12:51:06 -0700262
263 final Configuration initialConfig = mInitialState.remove(mToken.asBinder());
264 final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation()
265 != mPipBoundsHandler.getDisplayRotation();
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700266 final WindowContainerTransaction wct = new WindowContainerTransaction();
Hongwei Wang3d11e1f2020-05-28 15:48:06 -0700267 final Rect destinationBounds = initialConfig.windowConfiguration.getBounds();
268 final int direction = syncWithSplitScreenBounds(destinationBounds)
269 ? TRANSITION_DIRECTION_TO_SPLIT_SCREEN
270 : TRANSITION_DIRECTION_TO_FULLSCREEN;
Winson Chungc4d4ee82020-05-05 12:51:06 -0700271 if (orientationDiffers) {
Hongwei Wang3d11e1f2020-05-28 15:48:06 -0700272 // Send started callback though animation is ignored.
273 sendOnPipTransitionStarted(direction);
Winson Chungc4d4ee82020-05-05 12:51:06 -0700274 // Don't bother doing an animation if the display rotation differs or if it's in
275 // a non-supported windowing mode
276 wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
277 wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
278 WindowOrganizer.applyTransaction(wct);
Hongwei Wang3d11e1f2020-05-28 15:48:06 -0700279 // Send finished callback though animation is ignored.
280 sendOnPipTransitionFinished(direction);
Winson Chungc4d4ee82020-05-05 12:51:06 -0700281 mInPip = false;
282 } else {
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700283 final SurfaceControl.Transaction tx =
284 mSurfaceControlTransactionFactory.getTransaction();
Winson Chungc4d4ee82020-05-05 12:51:06 -0700285 mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds,
286 mLastReportedBounds);
287 tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
288 wct.setActivityWindowingMode(mToken, direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN
289 ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
290 : WINDOWING_MODE_FULLSCREEN);
291 wct.setBounds(mToken, destinationBounds);
292 wct.setBoundsChangeTransaction(mToken, tx);
293 applySyncTransaction(wct, new WindowContainerTransactionCallback() {
294 @Override
295 public void onTransactionReady(int id, SurfaceControl.Transaction t) {
296 t.apply();
297 scheduleAnimateResizePip(mLastReportedBounds, destinationBounds,
Winson Chungd7302542020-06-08 17:09:45 +0000298 direction, animationDurationMs, null /* updateBoundsCallback */);
Winson Chungc4d4ee82020-05-05 12:51:06 -0700299 mInPip = false;
300 }
301 });
302 }
Winson Chungc46cbae2020-05-27 21:36:09 -0700303 mExitingPip = true;
Winson Chungc4d4ee82020-05-05 12:51:06 -0700304 }
305
306 /**
307 * Removes PiP immediately.
308 */
309 public void removePip() {
Winson Chungc46cbae2020-05-27 21:36:09 -0700310 if (!mInPip || mExitingPip || mToken == null) {
Winson Chungc4d4ee82020-05-05 12:51:06 -0700311 Log.wtf(TAG, "Not allowed to removePip in current state"
Winson Chungc46cbae2020-05-27 21:36:09 -0700312 + " mInPip=" + mInPip + " mExitingPip=" + mExitingPip + " mToken=" + mToken);
Winson Chungc4d4ee82020-05-05 12:51:06 -0700313 return;
314 }
315 getUpdateHandler().post(() -> {
316 try {
Winson Chung9bd523d2020-05-29 11:09:52 -0700317 // Reset the task bounds first to ensure the activity configuration is reset as well
318 final WindowContainerTransaction wct = new WindowContainerTransaction();
319 wct.setBounds(mToken, null);
320 WindowOrganizer.applyTransaction(wct);
321
Winson Chungc4d4ee82020-05-05 12:51:06 -0700322 ActivityTaskManager.getService().removeStacksInWindowingModes(
323 new int[]{ WINDOWING_MODE_PINNED });
324 } catch (RemoteException e) {
325 Log.e(TAG, "Failed to remove PiP", e);
326 }
327 });
328 mInitialState.remove(mToken.asBinder());
Winson Chungc46cbae2020-05-27 21:36:09 -0700329 mExitingPip = true;
Hongwei Wangd39583a2020-03-04 11:14:32 -0800330 }
331
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800332 @Override
chaviw7de50002020-04-27 12:33:30 -0700333 public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800334 Objects.requireNonNull(info, "Requires RunningTaskInfo");
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800335 mTaskInfo = info;
Winson Chung55701472020-03-04 19:30:30 -0800336 mToken = mTaskInfo.token;
337 mInPip = true;
Winson Chungc46cbae2020-05-27 21:36:09 -0700338 mExitingPip = false;
chaviw7de50002020-04-27 12:33:30 -0700339 mLeash = leash;
Winson Chungc4d4ee82020-05-05 12:51:06 -0700340 mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration));
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700341 mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
342
343 if (mShouldDeferEnteringPip) {
344 if (DEBUG) Log.d(TAG, "Defer entering PiP animation, fixed rotation is ongoing");
345 // if deferred, hide the surface till fixed rotation is completed
346 final SurfaceControl.Transaction tx =
347 mSurfaceControlTransactionFactory.getTransaction();
348 tx.setAlpha(mLeash, 0f);
Winson Chung14a657c2020-05-28 10:47:40 -0700349 tx.show(mLeash);
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700350 tx.apply();
351 return;
352 }
353
354 final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
355 mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams),
356 null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
357 Objects.requireNonNull(destinationBounds, "Missing destination bounds");
358 final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
Winson Chungc4d4ee82020-05-05 12:51:06 -0700359
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800360 if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
Winson Chungd7302542020-06-08 17:09:45 +0000361 scheduleAnimateResizePip(currentBounds, destinationBounds,
Hongwei Wangd39583a2020-03-04 11:14:32 -0800362 TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration,
363 null /* updateBoundsCallback */);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800364 } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700365 enterPipWithAlphaAnimation(destinationBounds, mEnterExitAnimationDuration);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800366 mOneShotAnimationType = ANIM_TYPE_BOUNDS;
367 } else {
368 throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType);
369 }
370 }
371
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700372 private void enterPipWithAlphaAnimation(Rect destinationBounds, long durationMs) {
373 // If we are fading the PIP in, then we should move the pip to the final location as
374 // soon as possible, but set the alpha immediately since the transaction can take a
375 // while to process
376 final SurfaceControl.Transaction tx =
377 mSurfaceControlTransactionFactory.getTransaction();
378 tx.setAlpha(mLeash, 0f);
379 tx.apply();
380 final WindowContainerTransaction wct = new WindowContainerTransaction();
381 wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
382 wct.setBounds(mToken, destinationBounds);
383 wct.scheduleFinishEnterPip(mToken, destinationBounds);
384 applySyncTransaction(wct, new WindowContainerTransactionCallback() {
385 @Override
386 public void onTransactionReady(int id, SurfaceControl.Transaction t) {
387 t.apply();
388 mUpdateHandler.post(() -> mPipAnimationController
389 .getAnimator(mLeash, destinationBounds, 0f, 1f)
390 .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
391 .setPipAnimationCallback(mPipAnimationCallback)
392 .setDuration(durationMs)
393 .start());
394 }
395 });
396 }
397
Hongwei Wang3d11e1f2020-05-28 15:48:06 -0700398 private void sendOnPipTransitionStarted(
399 @PipAnimationController.TransitionDirection int direction) {
400 mMainHandler.post(() -> {
401 for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
402 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
403 callback.onPipTransitionStarted(mTaskInfo.baseActivity, direction);
404 }
405 });
406 }
407
408 private void sendOnPipTransitionFinished(
409 @PipAnimationController.TransitionDirection int direction) {
410 mMainHandler.post(() -> {
411 for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
412 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
413 callback.onPipTransitionFinished(mTaskInfo.baseActivity, direction);
414 }
415 });
416 }
417
418 private void sendOnPipTransitionCancelled(
419 @PipAnimationController.TransitionDirection int direction) {
420 mMainHandler.post(() -> {
421 for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
422 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
423 callback.onPipTransitionCanceled(mTaskInfo.baseActivity, direction);
424 }
425 });
426 }
427
Hongwei Wangd39583a2020-03-04 11:14:32 -0800428 /**
Winson Chungc4d4ee82020-05-05 12:51:06 -0700429 * Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int)}.
Hongwei Wangd39583a2020-03-04 11:14:32 -0800430 * Meanwhile this callback is invoked whenever the task is removed. For instance:
431 * - as a result of removeStacksInWindowingModes from WM
432 * - activity itself is died
Hongwei Wang180162e2020-04-22 14:40:32 -0700433 * Nevertheless, we simply update the internal state here as all the heavy lifting should
434 * have been done in WM.
Hongwei Wangd39583a2020-03-04 11:14:32 -0800435 */
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800436 @Override
Wale Ogunwaledec34082020-03-22 09:45:00 -0700437 public void onTaskVanished(ActivityManager.RunningTaskInfo info) {
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700438 if (!mInPip) {
439 return;
440 }
Hongwei Wange8e32862020-04-08 13:23:45 -0700441 final WindowContainerToken token = info.token;
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700442 Objects.requireNonNull(token, "Requires valid WindowContainerToken");
Winson Chung55701472020-03-04 19:30:30 -0800443 if (token.asBinder() != mToken.asBinder()) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800444 Log.wtf(TAG, "Unrecognized token: " + token);
445 return;
446 }
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700447 mShouldDeferEnteringPip = false;
Hongwei Wang8c95ce52020-04-30 15:06:12 -0700448 mPictureInPictureParams = null;
Winson Chung55701472020-03-04 19:30:30 -0800449 mInPip = false;
Winson Chungc46cbae2020-05-27 21:36:09 -0700450 mExitingPip = false;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800451 }
452
453 @Override
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800454 public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
Hongwei Wange8e32862020-04-08 13:23:45 -0700455 Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken");
Hongwei Wang8e8a8ae2020-03-03 11:06:56 -0800456 final PictureInPictureParams newParams = info.pictureInPictureParams;
Winson Chung6d8a19562020-05-21 11:27:28 -0700457 if (newParams == null || !applyPictureInPictureParams(newParams)) {
Hongwei Wang8e8a8ae2020-03-03 11:06:56 -0800458 Log.d(TAG, "Ignored onTaskInfoChanged with PiP param: " + newParams);
459 return;
460 }
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800461 final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
Hongwei Wang309cffa2020-04-06 11:11:01 -0700462 info.topActivity, getAspectRatioOrDefault(newParams),
Hongwei Wangb5333e92020-05-29 12:51:52 -0700463 mLastReportedBounds, getMinimalSize(info.topActivityInfo),
464 true /* userCurrentMinEdgeSize */);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800465 Objects.requireNonNull(destinationBounds, "Missing destination bounds");
Hongwei Wangd39583a2020-03-04 11:14:32 -0800466 scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration,
467 null /* updateBoundsCallback */);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800468 }
469
Winson Chunga1f869d2020-03-21 23:02:48 -0700470 @Override
471 public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
472 // Do nothing
473 }
474
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700475 @Override
476 public void onFixedRotationStarted(int displayId, int newRotation) {
477 mShouldDeferEnteringPip = true;
478 }
479
480 @Override
481 public void onFixedRotationFinished(int displayId) {
482 if (mShouldDeferEnteringPip && mInPip) {
483 final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
484 mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams),
485 null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
486 // schedule a regular animation to ensure all the callbacks are still being sent
487 enterPipWithAlphaAnimation(destinationBounds, 0 /* durationMs */);
488 }
489 mShouldDeferEnteringPip = false;
490 }
491
Hongwei Wang8e8a8ae2020-03-03 11:06:56 -0800492 /**
Hongwei Wang951dc022020-03-30 16:16:16 -0700493 * TODO(b/152809058): consolidate the display info handling logic in SysUI
Hongwei Wang3c981f62020-04-07 16:16:26 -0700494 *
495 * @param destinationBoundsOut the current destination bounds will be populated to this param
Hongwei Wang951dc022020-03-30 16:16:16 -0700496 */
497 @SuppressWarnings("unchecked")
Hongwei Wange0412d12020-04-13 18:27:25 -0700498 public void onMovementBoundsChanged(Rect destinationBoundsOut, boolean fromRotation,
Hongwei Wang3c981f62020-04-07 16:16:26 -0700499 boolean fromImeAdjustment, boolean fromShelfAdjustment) {
Hongwei Wang951dc022020-03-30 16:16:16 -0700500 final PipAnimationController.PipTransitionAnimator animator =
501 mPipAnimationController.getCurrentAnimator();
Hongwei Wang6e68af52020-04-06 12:34:06 -0700502 if (animator == null || !animator.isRunning()
503 || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
Hongwei Wange0412d12020-04-13 18:27:25 -0700504 if (mInPip && fromRotation) {
505 // this could happen if rotation finishes before the animation
506 mLastReportedBounds.set(destinationBoundsOut);
507 scheduleFinishResizePip(mLastReportedBounds);
Ben Linba4b76b2020-06-05 16:39:17 -0700508 } else {
509 // There could be an animation on-going. If there is one on-going, last-reported
510 // bounds isn't yet updated. We'll use the animator's bounds instead.
511 if (animator != null && animator.isRunning()) {
512 if (!animator.getDestinationBounds().isEmpty()) {
513 destinationBoundsOut.set(animator.getDestinationBounds());
514 }
515 } else {
516 if (!mLastReportedBounds.isEmpty()) {
517 destinationBoundsOut.set(mLastReportedBounds);
518 }
519 }
Hongwei Wange0412d12020-04-13 18:27:25 -0700520 }
Hongwei Wang6e68af52020-04-06 12:34:06 -0700521 return;
Hongwei Wang951dc022020-03-30 16:16:16 -0700522 }
Hongwei Wang6e68af52020-04-06 12:34:06 -0700523
524 final Rect currentDestinationBounds = animator.getDestinationBounds();
Hongwei Wang3c981f62020-04-07 16:16:26 -0700525 destinationBoundsOut.set(currentDestinationBounds);
Hongwei Wang6e68af52020-04-06 12:34:06 -0700526 if (!fromImeAdjustment && !fromShelfAdjustment
527 && mPipBoundsHandler.getDisplayBounds().contains(currentDestinationBounds)) {
528 // no need to update the destination bounds, bail early
529 return;
530 }
531
532 final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds(
Hongwei Wang8c95ce52020-04-30 15:06:12 -0700533 mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams),
Hongwei Wang6e68af52020-04-06 12:34:06 -0700534 null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
535 if (newDestinationBounds.equals(currentDestinationBounds)) return;
536 if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) {
537 animator.updateEndValue(newDestinationBounds);
538 }
539 animator.setDestinationBounds(newDestinationBounds);
Hongwei Wang3c981f62020-04-07 16:16:26 -0700540 destinationBoundsOut.set(newDestinationBounds);
Hongwei Wang951dc022020-03-30 16:16:16 -0700541 }
542
543 /**
Hongwei Wang8e8a8ae2020-03-03 11:06:56 -0800544 * @return {@code true} if the aspect ratio is changed since no other parameters within
545 * {@link PictureInPictureParams} would affect the bounds.
546 */
Hongwei Wang8c95ce52020-04-30 15:06:12 -0700547 private boolean applyPictureInPictureParams(@NonNull PictureInPictureParams params) {
548 final boolean changed = (mPictureInPictureParams == null) ? true : !Objects.equals(
549 mPictureInPictureParams.getAspectRatioRational(), params.getAspectRatioRational());
550 if (changed) {
551 mPictureInPictureParams = params;
552 mPipBoundsHandler.onAspectRatioChanged(params.getAspectRatio());
Hongwei Wang8e8a8ae2020-03-03 11:06:56 -0800553 }
Hongwei Wang8c95ce52020-04-30 15:06:12 -0700554 return changed;
Hongwei Wang8e8a8ae2020-03-03 11:06:56 -0800555 }
Ben Lin7d6b8e72020-02-27 17:48:16 -0800556
557 /**
Winson Chung55701472020-03-04 19:30:30 -0800558 * Animates resizing of the pinned stack given the duration.
Ben Lin7d6b8e72020-02-27 17:48:16 -0800559 */
Winson Chung55701472020-03-04 19:30:30 -0800560 public void scheduleAnimateResizePip(Rect toBounds, int duration,
561 Consumer<Rect> updateBoundsCallback) {
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700562 if (mShouldDeferEnteringPip) {
563 Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred");
564 return;
565 }
Winson Chungd7302542020-06-08 17:09:45 +0000566 scheduleAnimateResizePip(mLastReportedBounds, toBounds,
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800567 TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback);
Ben Lin7d6b8e72020-02-27 17:48:16 -0800568 }
569
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800570 private void scheduleAnimateResizePip(Rect currentBounds, Rect destinationBounds,
Winson Chungd7302542020-06-08 17:09:45 +0000571 @PipAnimationController.TransitionDirection int direction, int durationMs,
572 Consumer<Rect> updateBoundsCallback) {
Winson Chung55701472020-03-04 19:30:30 -0800573 if (!mInPip) {
Hongwei Wange8e32862020-04-08 13:23:45 -0700574 // can be initiated in other component, ignore if we are no longer in PIP
Winson Chung55701472020-03-04 19:30:30 -0800575 return;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800576 }
Winson Chungc4d4ee82020-05-05 12:51:06 -0700577
Winson Chung55701472020-03-04 19:30:30 -0800578 SomeArgs args = SomeArgs.obtain();
579 args.arg1 = updateBoundsCallback;
580 args.arg2 = currentBounds;
581 args.arg3 = destinationBounds;
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800582 args.argi1 = direction;
Winson Chung55701472020-03-04 19:30:30 -0800583 args.argi2 = durationMs;
584 mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_ANIMATE, args));
585 }
586
587 /**
588 * Directly perform manipulation/resize on the leash. This will not perform any
589 * {@link WindowContainerTransaction} until {@link #scheduleFinishResizePip} is called.
590 */
591 public void scheduleResizePip(Rect toBounds, Consumer<Rect> updateBoundsCallback) {
Winson Chung55701472020-03-04 19:30:30 -0800592 SomeArgs args = SomeArgs.obtain();
593 args.arg1 = updateBoundsCallback;
594 args.arg2 = toBounds;
595 mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_IMMEDIATE, args));
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800596 }
597
Ben Lin7d6b8e72020-02-27 17:48:16 -0800598 /**
Ben Lin75ba9c32020-03-19 17:55:12 -0700599 * Directly perform a scaled matrix transformation on the leash. This will not perform any
600 * {@link WindowContainerTransaction} until {@link #scheduleFinishResizePip} is called.
601 */
602 public void scheduleUserResizePip(Rect startBounds, Rect toBounds,
603 Consumer<Rect> updateBoundsCallback) {
604 SomeArgs args = SomeArgs.obtain();
605 args.arg1 = updateBoundsCallback;
606 args.arg2 = startBounds;
607 args.arg3 = toBounds;
608 mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_USER, args));
609 }
610
611 /**
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700612 * Finish an intermediate resize operation. This is expected to be called after
Winson Chung55701472020-03-04 19:30:30 -0800613 * {@link #scheduleResizePip}.
Ben Lin7d6b8e72020-02-27 17:48:16 -0800614 */
Winson Chung55701472020-03-04 19:30:30 -0800615 public void scheduleFinishResizePip(Rect destinationBounds) {
Ben Lin4a8e2572020-05-08 15:55:15 -0700616 scheduleFinishResizePip(destinationBounds, null);
617 }
618
619 /**
620 * Same as {@link #scheduleFinishResizePip} but with a callback.
621 */
622 public void scheduleFinishResizePip(Rect destinationBounds,
623 Consumer<Rect> updateBoundsCallback) {
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700624 final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
625 mSurfaceTransactionHelper
626 .crop(tx, mLeash, destinationBounds)
Ben Linf603b272020-03-23 15:39:07 -0700627 .resetScale(tx, mLeash, destinationBounds)
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700628 .round(tx, mLeash, mInPip);
Ben Lin4a8e2572020-05-08 15:55:15 -0700629 scheduleFinishResizePip(tx, destinationBounds, TRANSITION_DIRECTION_NONE,
630 updateBoundsCallback);
Ben Lin7d6b8e72020-02-27 17:48:16 -0800631 }
632
Winson Chung55701472020-03-04 19:30:30 -0800633 private void scheduleFinishResizePip(SurfaceControl.Transaction tx,
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800634 Rect destinationBounds, @PipAnimationController.TransitionDirection int direction,
Winson Chung55701472020-03-04 19:30:30 -0800635 Consumer<Rect> updateBoundsCallback) {
Hongwei Wange8e32862020-04-08 13:23:45 -0700636 if (!mInPip) {
637 // can be initiated in other component, ignore if we are no longer in PIP
638 return;
639 }
Winson Chung55701472020-03-04 19:30:30 -0800640 SomeArgs args = SomeArgs.obtain();
641 args.arg1 = updateBoundsCallback;
642 args.arg2 = tx;
643 args.arg3 = destinationBounds;
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800644 args.argi1 = direction;
Winson Chung55701472020-03-04 19:30:30 -0800645 mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_FINISH_RESIZE, args));
646 }
647
648 /**
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800649 * Offset the PiP window by a given offset on Y-axis, triggered also from screen rotation.
Winson Chung55701472020-03-04 19:30:30 -0800650 */
651 public void scheduleOffsetPip(Rect originalBounds, int offset, int duration,
652 Consumer<Rect> updateBoundsCallback) {
653 if (!mInPip) {
Hongwei Wange8e32862020-04-08 13:23:45 -0700654 // can be initiated in other component, ignore if we are no longer in PIP
Winson Chung55701472020-03-04 19:30:30 -0800655 return;
656 }
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700657 if (mShouldDeferEnteringPip) {
658 Log.d(TAG, "skip scheduleOffsetPip, entering pip deferred");
659 return;
660 }
Winson Chung55701472020-03-04 19:30:30 -0800661 SomeArgs args = SomeArgs.obtain();
662 args.arg1 = updateBoundsCallback;
663 args.arg2 = originalBounds;
664 // offset would be zero if triggered from screen rotation.
665 args.argi1 = offset;
666 args.argi2 = duration;
667 mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_ANIMATE, args));
668 }
669
670 private void offsetPip(Rect originalBounds, int xOffset, int yOffset, int durationMs) {
671 if (Looper.myLooper() != mUpdateHandler.getLooper()) {
672 throw new RuntimeException("Callers should call scheduleOffsetPip() instead of this "
673 + "directly");
674 }
675 if (mTaskInfo == null) {
676 Log.w(TAG, "mTaskInfo is not set");
677 return;
678 }
679 final Rect destinationBounds = new Rect(originalBounds);
680 destinationBounds.offset(xOffset, yOffset);
Winson Chungd7302542020-06-08 17:09:45 +0000681 animateResizePip(originalBounds, destinationBounds, TRANSITION_DIRECTION_SAME, durationMs);
Winson Chung55701472020-03-04 19:30:30 -0800682 }
683
684 private void resizePip(Rect destinationBounds) {
685 if (Looper.myLooper() != mUpdateHandler.getLooper()) {
686 throw new RuntimeException("Callers should call scheduleResizePip() instead of this "
687 + "directly");
688 }
Winson Chungc4d4ee82020-05-05 12:51:06 -0700689 // Could happen when exitPip
Winson Chung55701472020-03-04 19:30:30 -0800690 if (mToken == null || mLeash == null) {
691 Log.w(TAG, "Abort animation, invalid leash");
692 return;
693 }
Ben Lin5400de32020-05-06 19:28:09 -0700694 mLastReportedBounds.set(destinationBounds);
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700695 final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
696 mSurfaceTransactionHelper
697 .crop(tx, mLeash, destinationBounds)
698 .round(tx, mLeash, mInPip);
699 tx.apply();
Winson Chung55701472020-03-04 19:30:30 -0800700 }
701
Ben Lin75ba9c32020-03-19 17:55:12 -0700702 private void userResizePip(Rect startBounds, Rect destinationBounds) {
703 if (Looper.myLooper() != mUpdateHandler.getLooper()) {
704 throw new RuntimeException("Callers should call scheduleUserResizePip() instead of "
705 + "this directly");
706 }
Winson Chungc4d4ee82020-05-05 12:51:06 -0700707 // Could happen when exitPip
Ben Lin75ba9c32020-03-19 17:55:12 -0700708 if (mToken == null || mLeash == null) {
709 Log.w(TAG, "Abort animation, invalid leash");
710 return;
711 }
Josh Tsujia8ed97e2020-06-16 17:00:59 -0400712
713 if (startBounds.isEmpty() || destinationBounds.isEmpty()) {
714 Log.w(TAG, "Attempted to user resize PIP to or from empty bounds, aborting.");
715 return;
716 }
717
Ben Lin75ba9c32020-03-19 17:55:12 -0700718 final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
719 mSurfaceTransactionHelper.scale(tx, mLeash, startBounds, destinationBounds);
720 tx.apply();
721 }
722
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800723 private void finishResize(SurfaceControl.Transaction tx, Rect destinationBounds,
Winson Chungc4d4ee82020-05-05 12:51:06 -0700724 @PipAnimationController.TransitionDirection int direction,
725 @PipAnimationController.AnimationType int type) {
Winson Chung55701472020-03-04 19:30:30 -0800726 if (Looper.myLooper() != mUpdateHandler.getLooper()) {
727 throw new RuntimeException("Callers should call scheduleResizePip() instead of this "
728 + "directly");
729 }
Ben Lin7d6b8e72020-02-27 17:48:16 -0800730 mLastReportedBounds.set(destinationBounds);
Winson Chungc4d4ee82020-05-05 12:51:06 -0700731 if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA) {
732 return;
733 }
734
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700735 final WindowContainerTransaction wct = new WindowContainerTransaction();
736 final Rect taskBounds;
Winson Chungc4d4ee82020-05-05 12:51:06 -0700737 if (isInPipDirection(direction)) {
738 // If we are animating from fullscreen using a bounds animation, then reset the
739 // activity windowing mode set by WM, and set the task bounds to the final bounds
740 taskBounds = destinationBounds;
741 wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
742 wct.scheduleFinishEnterPip(mToken, destinationBounds);
743 } else if (isOutPipDirection(direction)) {
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700744 // If we are animating to fullscreen, then we need to reset the override bounds
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700745 // on the task to ensure that the task "matches" the parent's bounds.
746 taskBounds = (direction == TRANSITION_DIRECTION_TO_FULLSCREEN)
747 ? null : destinationBounds;
jorgegil@google.com79aae3c2020-06-11 10:37:52 -0700748 // Reset the final windowing mode.
749 wct.setWindowingMode(mToken, getOutPipWindowingMode());
750 // Simply reset the activity mode set prior to the animation running.
Winson Chungc4d4ee82020-05-05 12:51:06 -0700751 wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
Evan Roskydc1d29f2020-05-04 13:46:14 -0700752 if (mSplitDivider != null && direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN) {
753 wct.reparent(mToken, mSplitDivider.getSecondaryRoot(), true /* onTop */);
754 }
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700755 } else {
Winson Chungc4d4ee82020-05-05 12:51:06 -0700756 // Just a resize in PIP
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700757 taskBounds = destinationBounds;
Ben Lin7d6b8e72020-02-27 17:48:16 -0800758 }
Winson Chungc4d4ee82020-05-05 12:51:06 -0700759
760 wct.setBounds(mToken, taskBounds);
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700761 wct.setBoundsChangeTransaction(mToken, tx);
jorgegil@google.com79aae3c2020-06-11 10:37:52 -0700762 applyFinishBoundsResize(wct, direction);
763 }
764
765 /**
766 * Applies the window container transaction to finish a bounds resize.
767 *
768 * Called by {@link #finishResize(SurfaceControl.Transaction, Rect, int, int)}} once it has
769 * finished preparing the transaction. It allows subclasses to modify the transaction before
770 * applying it.
771 */
772 public void applyFinishBoundsResize(@NonNull WindowContainerTransaction wct,
773 @PipAnimationController.TransitionDirection int direction) {
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700774 WindowOrganizer.applyTransaction(wct);
Ben Lin7d6b8e72020-02-27 17:48:16 -0800775 }
776
jorgegil@google.com79aae3c2020-06-11 10:37:52 -0700777 /**
778 * The windowing mode to restore to when resizing out of PIP direction. Defaults to undefined
779 * and can be overridden to restore to an alternate windowing mode.
780 */
781 public int getOutPipWindowingMode() {
782 // By default, simply reset the windowing mode to undefined.
783 return WINDOWING_MODE_UNDEFINED;
784 }
785
Winson Chungd7302542020-06-08 17:09:45 +0000786 private void animateResizePip(Rect currentBounds, Rect destinationBounds,
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800787 @PipAnimationController.TransitionDirection int direction, int durationMs) {
Winson Chung55701472020-03-04 19:30:30 -0800788 if (Looper.myLooper() != mUpdateHandler.getLooper()) {
789 throw new RuntimeException("Callers should call scheduleAnimateResizePip() instead of "
790 + "this directly");
Ben Lin7d6b8e72020-02-27 17:48:16 -0800791 }
Winson Chungc4d4ee82020-05-05 12:51:06 -0700792 // Could happen when exitPip
Winson Chung55701472020-03-04 19:30:30 -0800793 if (mToken == null || mLeash == null) {
794 Log.w(TAG, "Abort animation, invalid leash");
795 return;
796 }
Winson Chungc4d4ee82020-05-05 12:51:06 -0700797 mPipAnimationController
Winson Chungd7302542020-06-08 17:09:45 +0000798 .getAnimator(mLeash, currentBounds, destinationBounds)
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800799 .setTransitionDirection(direction)
Winson Chung55701472020-03-04 19:30:30 -0800800 .setPipAnimationCallback(mPipAnimationCallback)
801 .setDuration(durationMs)
Winson Chungc4d4ee82020-05-05 12:51:06 -0700802 .start();
Ben Lin7d6b8e72020-02-27 17:48:16 -0800803 }
804
Hongwei Wang2e725be2020-03-10 11:01:28 -0700805 private Size getMinimalSize(ActivityInfo activityInfo) {
806 if (activityInfo == null || activityInfo.windowLayout == null) {
807 return null;
808 }
809 final ActivityInfo.WindowLayout windowLayout = activityInfo.windowLayout;
Hongwei Wang907fd632020-04-02 17:13:08 -0700810 // -1 will be populated if an activity specifies defaultWidth/defaultHeight in <layout>
811 // without minWidth/minHeight
812 if (windowLayout.minWidth > 0 && windowLayout.minHeight > 0) {
813 return new Size(windowLayout.minWidth, windowLayout.minHeight);
814 }
815 return null;
Hongwei Wang2e725be2020-03-10 11:01:28 -0700816 }
817
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800818 private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) {
819 return params == null
820 ? mPipBoundsHandler.getDefaultAspectRatio()
821 : params.getAspectRatio();
822 }
823
824 /**
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700825 * Sync with {@link #mSplitDivider} on destination bounds if PiP is going to split screen.
826 *
827 * @param destinationBoundsOut contain the updated destination bounds if applicable
828 * @return {@code true} if destinationBounds is altered for split screen
829 */
830 private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut) {
Evan Rosky90d5b6d2020-05-13 19:39:05 -0700831 if (mSplitDivider == null || !mSplitDivider.isDividerVisible()) {
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700832 // bail early if system is not in split screen mode
833 return false;
834 }
835 // PiP window will go to split-secondary mode instead of fullscreen, populates the
836 // split screen bounds here.
837 destinationBoundsOut.set(
838 mSplitDivider.getView().getNonMinimizedSplitScreenSecondaryBounds());
839 return true;
840 }
841
842 /**
Winson Chungc4d4ee82020-05-05 12:51:06 -0700843 * Dumps internal states.
844 */
845 public void dump(PrintWriter pw, String prefix) {
846 final String innerPrefix = prefix + " ";
847 pw.println(prefix + TAG);
848 pw.println(innerPrefix + "mTaskInfo=" + mTaskInfo);
849 pw.println(innerPrefix + "mToken=" + mToken
850 + " binder=" + (mToken != null ? mToken.asBinder() : null));
851 pw.println(innerPrefix + "mLeash=" + mLeash);
852 pw.println(innerPrefix + "mInPip=" + mInPip);
853 pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
854 pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
855 pw.println(innerPrefix + "mLastReportedBounds=" + mLastReportedBounds);
856 pw.println(innerPrefix + "mInitialState:");
857 for (Map.Entry<IBinder, Configuration> e : mInitialState.entrySet()) {
858 pw.println(innerPrefix + " binder=" + e.getKey()
859 + " winConfig=" + e.getValue().windowConfiguration);
860 }
861 }
862
863 /**
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800864 * Callback interface for PiP transitions (both from and to PiP mode)
865 */
866 public interface PipTransitionCallback {
867 /**
868 * Callback when the pip transition is started.
869 */
Hongwei Wang221fe3d2020-03-26 13:13:04 -0700870 void onPipTransitionStarted(ComponentName activity, int direction);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800871
872 /**
873 * Callback when the pip transition is finished.
874 */
Hongwei Wang221fe3d2020-03-26 13:13:04 -0700875 void onPipTransitionFinished(ComponentName activity, int direction);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800876
877 /**
878 * Callback when the pip transition is cancelled.
879 */
Hongwei Wang221fe3d2020-03-26 13:13:04 -0700880 void onPipTransitionCanceled(ComponentName activity, int direction);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800881 }
882}