blob: e2feb71735fff7617a896f790636d6e530e37cbf [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
Hongwei Wang6ef225e2020-06-23 11:16:05 -0700276 applyWindowingModeChangeOnExit(wct, direction);
Winson Chungc4d4ee82020-05-05 12:51:06 -0700277 WindowOrganizer.applyTransaction(wct);
Hongwei Wang3d11e1f2020-05-28 15:48:06 -0700278 // Send finished callback though animation is ignored.
279 sendOnPipTransitionFinished(direction);
Winson Chungc4d4ee82020-05-05 12:51:06 -0700280 mInPip = false;
281 } else {
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700282 final SurfaceControl.Transaction tx =
283 mSurfaceControlTransactionFactory.getTransaction();
Winson Chungc4d4ee82020-05-05 12:51:06 -0700284 mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds,
285 mLastReportedBounds);
286 tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
287 wct.setActivityWindowingMode(mToken, direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN
288 ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
289 : WINDOWING_MODE_FULLSCREEN);
290 wct.setBounds(mToken, destinationBounds);
291 wct.setBoundsChangeTransaction(mToken, tx);
292 applySyncTransaction(wct, new WindowContainerTransactionCallback() {
293 @Override
294 public void onTransactionReady(int id, SurfaceControl.Transaction t) {
295 t.apply();
296 scheduleAnimateResizePip(mLastReportedBounds, destinationBounds,
Winson Chungd7302542020-06-08 17:09:45 +0000297 direction, animationDurationMs, null /* updateBoundsCallback */);
Winson Chungc4d4ee82020-05-05 12:51:06 -0700298 mInPip = false;
299 }
300 });
301 }
Winson Chungc46cbae2020-05-27 21:36:09 -0700302 mExitingPip = true;
Winson Chungc4d4ee82020-05-05 12:51:06 -0700303 }
304
Hongwei Wang6ef225e2020-06-23 11:16:05 -0700305 private void applyWindowingModeChangeOnExit(WindowContainerTransaction wct, int direction) {
306 // Reset the final windowing mode.
307 wct.setWindowingMode(mToken, getOutPipWindowingMode());
308 // Simply reset the activity mode set prior to the animation running.
309 wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
310 if (mSplitDivider != null && direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN) {
311 wct.reparent(mToken, mSplitDivider.getSecondaryRoot(), true /* onTop */);
312 }
313 }
314
Winson Chungc4d4ee82020-05-05 12:51:06 -0700315 /**
316 * Removes PiP immediately.
317 */
318 public void removePip() {
Winson Chungc46cbae2020-05-27 21:36:09 -0700319 if (!mInPip || mExitingPip || mToken == null) {
Winson Chungc4d4ee82020-05-05 12:51:06 -0700320 Log.wtf(TAG, "Not allowed to removePip in current state"
Winson Chungc46cbae2020-05-27 21:36:09 -0700321 + " mInPip=" + mInPip + " mExitingPip=" + mExitingPip + " mToken=" + mToken);
Winson Chungc4d4ee82020-05-05 12:51:06 -0700322 return;
323 }
324 getUpdateHandler().post(() -> {
325 try {
Winson Chung9bd523d2020-05-29 11:09:52 -0700326 // Reset the task bounds first to ensure the activity configuration is reset as well
327 final WindowContainerTransaction wct = new WindowContainerTransaction();
328 wct.setBounds(mToken, null);
329 WindowOrganizer.applyTransaction(wct);
330
Winson Chungc4d4ee82020-05-05 12:51:06 -0700331 ActivityTaskManager.getService().removeStacksInWindowingModes(
332 new int[]{ WINDOWING_MODE_PINNED });
333 } catch (RemoteException e) {
334 Log.e(TAG, "Failed to remove PiP", e);
335 }
336 });
337 mInitialState.remove(mToken.asBinder());
Winson Chungc46cbae2020-05-27 21:36:09 -0700338 mExitingPip = true;
Hongwei Wangd39583a2020-03-04 11:14:32 -0800339 }
340
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800341 @Override
chaviw7de50002020-04-27 12:33:30 -0700342 public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800343 Objects.requireNonNull(info, "Requires RunningTaskInfo");
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800344 mTaskInfo = info;
Winson Chung55701472020-03-04 19:30:30 -0800345 mToken = mTaskInfo.token;
346 mInPip = true;
Winson Chungc46cbae2020-05-27 21:36:09 -0700347 mExitingPip = false;
chaviw7de50002020-04-27 12:33:30 -0700348 mLeash = leash;
Winson Chungc4d4ee82020-05-05 12:51:06 -0700349 mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration));
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700350 mPictureInPictureParams = mTaskInfo.pictureInPictureParams;
351
352 if (mShouldDeferEnteringPip) {
353 if (DEBUG) Log.d(TAG, "Defer entering PiP animation, fixed rotation is ongoing");
354 // if deferred, hide the surface till fixed rotation is completed
355 final SurfaceControl.Transaction tx =
356 mSurfaceControlTransactionFactory.getTransaction();
357 tx.setAlpha(mLeash, 0f);
Winson Chung14a657c2020-05-28 10:47:40 -0700358 tx.show(mLeash);
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700359 tx.apply();
360 return;
361 }
362
363 final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
364 mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams),
365 null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
366 Objects.requireNonNull(destinationBounds, "Missing destination bounds");
367 final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
Winson Chungc4d4ee82020-05-05 12:51:06 -0700368
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800369 if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
Winson Chungd7302542020-06-08 17:09:45 +0000370 scheduleAnimateResizePip(currentBounds, destinationBounds,
Hongwei Wangd39583a2020-03-04 11:14:32 -0800371 TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration,
372 null /* updateBoundsCallback */);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800373 } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700374 enterPipWithAlphaAnimation(destinationBounds, mEnterExitAnimationDuration);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800375 mOneShotAnimationType = ANIM_TYPE_BOUNDS;
376 } else {
377 throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType);
378 }
379 }
380
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700381 private void enterPipWithAlphaAnimation(Rect destinationBounds, long durationMs) {
382 // If we are fading the PIP in, then we should move the pip to the final location as
383 // soon as possible, but set the alpha immediately since the transaction can take a
384 // while to process
385 final SurfaceControl.Transaction tx =
386 mSurfaceControlTransactionFactory.getTransaction();
387 tx.setAlpha(mLeash, 0f);
388 tx.apply();
389 final WindowContainerTransaction wct = new WindowContainerTransaction();
390 wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
391 wct.setBounds(mToken, destinationBounds);
392 wct.scheduleFinishEnterPip(mToken, destinationBounds);
393 applySyncTransaction(wct, new WindowContainerTransactionCallback() {
394 @Override
395 public void onTransactionReady(int id, SurfaceControl.Transaction t) {
396 t.apply();
397 mUpdateHandler.post(() -> mPipAnimationController
398 .getAnimator(mLeash, destinationBounds, 0f, 1f)
399 .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
400 .setPipAnimationCallback(mPipAnimationCallback)
401 .setDuration(durationMs)
402 .start());
403 }
404 });
405 }
406
Hongwei Wang3d11e1f2020-05-28 15:48:06 -0700407 private void sendOnPipTransitionStarted(
408 @PipAnimationController.TransitionDirection int direction) {
409 mMainHandler.post(() -> {
410 for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
411 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
412 callback.onPipTransitionStarted(mTaskInfo.baseActivity, direction);
413 }
414 });
415 }
416
417 private void sendOnPipTransitionFinished(
418 @PipAnimationController.TransitionDirection int direction) {
419 mMainHandler.post(() -> {
420 for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
421 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
422 callback.onPipTransitionFinished(mTaskInfo.baseActivity, direction);
423 }
424 });
425 }
426
427 private void sendOnPipTransitionCancelled(
428 @PipAnimationController.TransitionDirection int direction) {
429 mMainHandler.post(() -> {
430 for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
431 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
432 callback.onPipTransitionCanceled(mTaskInfo.baseActivity, direction);
433 }
434 });
435 }
436
Hongwei Wangd39583a2020-03-04 11:14:32 -0800437 /**
Winson Chungc4d4ee82020-05-05 12:51:06 -0700438 * Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int)}.
Hongwei Wangd39583a2020-03-04 11:14:32 -0800439 * Meanwhile this callback is invoked whenever the task is removed. For instance:
440 * - as a result of removeStacksInWindowingModes from WM
441 * - activity itself is died
Hongwei Wang180162e2020-04-22 14:40:32 -0700442 * Nevertheless, we simply update the internal state here as all the heavy lifting should
443 * have been done in WM.
Hongwei Wangd39583a2020-03-04 11:14:32 -0800444 */
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800445 @Override
Wale Ogunwaledec34082020-03-22 09:45:00 -0700446 public void onTaskVanished(ActivityManager.RunningTaskInfo info) {
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700447 if (!mInPip) {
448 return;
449 }
Hongwei Wange8e32862020-04-08 13:23:45 -0700450 final WindowContainerToken token = info.token;
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700451 Objects.requireNonNull(token, "Requires valid WindowContainerToken");
Winson Chung55701472020-03-04 19:30:30 -0800452 if (token.asBinder() != mToken.asBinder()) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800453 Log.wtf(TAG, "Unrecognized token: " + token);
454 return;
455 }
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700456 mShouldDeferEnteringPip = false;
Hongwei Wang8c95ce52020-04-30 15:06:12 -0700457 mPictureInPictureParams = null;
Winson Chung55701472020-03-04 19:30:30 -0800458 mInPip = false;
Winson Chungc46cbae2020-05-27 21:36:09 -0700459 mExitingPip = false;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800460 }
461
462 @Override
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800463 public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
Hongwei Wange8e32862020-04-08 13:23:45 -0700464 Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken");
Hongwei Wang8e8a8ae2020-03-03 11:06:56 -0800465 final PictureInPictureParams newParams = info.pictureInPictureParams;
Winson Chung6d8a19562020-05-21 11:27:28 -0700466 if (newParams == null || !applyPictureInPictureParams(newParams)) {
Hongwei Wang8e8a8ae2020-03-03 11:06:56 -0800467 Log.d(TAG, "Ignored onTaskInfoChanged with PiP param: " + newParams);
468 return;
469 }
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800470 final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
Hongwei Wang309cffa2020-04-06 11:11:01 -0700471 info.topActivity, getAspectRatioOrDefault(newParams),
Hongwei Wangb5333e92020-05-29 12:51:52 -0700472 mLastReportedBounds, getMinimalSize(info.topActivityInfo),
473 true /* userCurrentMinEdgeSize */);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800474 Objects.requireNonNull(destinationBounds, "Missing destination bounds");
Hongwei Wangd39583a2020-03-04 11:14:32 -0800475 scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration,
476 null /* updateBoundsCallback */);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800477 }
478
Winson Chunga1f869d2020-03-21 23:02:48 -0700479 @Override
480 public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
481 // Do nothing
482 }
483
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700484 @Override
485 public void onFixedRotationStarted(int displayId, int newRotation) {
486 mShouldDeferEnteringPip = true;
487 }
488
489 @Override
490 public void onFixedRotationFinished(int displayId) {
491 if (mShouldDeferEnteringPip && mInPip) {
492 final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
493 mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams),
494 null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
495 // schedule a regular animation to ensure all the callbacks are still being sent
496 enterPipWithAlphaAnimation(destinationBounds, 0 /* durationMs */);
497 }
498 mShouldDeferEnteringPip = false;
499 }
500
Hongwei Wang8e8a8ae2020-03-03 11:06:56 -0800501 /**
Hongwei Wang951dc022020-03-30 16:16:16 -0700502 * TODO(b/152809058): consolidate the display info handling logic in SysUI
Hongwei Wang3c981f62020-04-07 16:16:26 -0700503 *
504 * @param destinationBoundsOut the current destination bounds will be populated to this param
Hongwei Wang951dc022020-03-30 16:16:16 -0700505 */
506 @SuppressWarnings("unchecked")
Hongwei Wange0412d12020-04-13 18:27:25 -0700507 public void onMovementBoundsChanged(Rect destinationBoundsOut, boolean fromRotation,
Hongwei Wang3c981f62020-04-07 16:16:26 -0700508 boolean fromImeAdjustment, boolean fromShelfAdjustment) {
Hongwei Wang951dc022020-03-30 16:16:16 -0700509 final PipAnimationController.PipTransitionAnimator animator =
510 mPipAnimationController.getCurrentAnimator();
Hongwei Wang6e68af52020-04-06 12:34:06 -0700511 if (animator == null || !animator.isRunning()
512 || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
Hongwei Wange0412d12020-04-13 18:27:25 -0700513 if (mInPip && fromRotation) {
514 // this could happen if rotation finishes before the animation
515 mLastReportedBounds.set(destinationBoundsOut);
516 scheduleFinishResizePip(mLastReportedBounds);
Ben Linba4b76b2020-06-05 16:39:17 -0700517 } else {
518 // There could be an animation on-going. If there is one on-going, last-reported
519 // bounds isn't yet updated. We'll use the animator's bounds instead.
520 if (animator != null && animator.isRunning()) {
521 if (!animator.getDestinationBounds().isEmpty()) {
522 destinationBoundsOut.set(animator.getDestinationBounds());
523 }
524 } else {
525 if (!mLastReportedBounds.isEmpty()) {
526 destinationBoundsOut.set(mLastReportedBounds);
527 }
528 }
Hongwei Wange0412d12020-04-13 18:27:25 -0700529 }
Hongwei Wang6e68af52020-04-06 12:34:06 -0700530 return;
Hongwei Wang951dc022020-03-30 16:16:16 -0700531 }
Hongwei Wang6e68af52020-04-06 12:34:06 -0700532
533 final Rect currentDestinationBounds = animator.getDestinationBounds();
Hongwei Wang3c981f62020-04-07 16:16:26 -0700534 destinationBoundsOut.set(currentDestinationBounds);
Hongwei Wang6e68af52020-04-06 12:34:06 -0700535 if (!fromImeAdjustment && !fromShelfAdjustment
536 && mPipBoundsHandler.getDisplayBounds().contains(currentDestinationBounds)) {
537 // no need to update the destination bounds, bail early
538 return;
539 }
540
541 final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds(
Hongwei Wang8c95ce52020-04-30 15:06:12 -0700542 mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams),
Hongwei Wang6e68af52020-04-06 12:34:06 -0700543 null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
544 if (newDestinationBounds.equals(currentDestinationBounds)) return;
545 if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) {
546 animator.updateEndValue(newDestinationBounds);
547 }
548 animator.setDestinationBounds(newDestinationBounds);
Hongwei Wang3c981f62020-04-07 16:16:26 -0700549 destinationBoundsOut.set(newDestinationBounds);
Hongwei Wang951dc022020-03-30 16:16:16 -0700550 }
551
552 /**
Hongwei Wang8e8a8ae2020-03-03 11:06:56 -0800553 * @return {@code true} if the aspect ratio is changed since no other parameters within
554 * {@link PictureInPictureParams} would affect the bounds.
555 */
Hongwei Wang8c95ce52020-04-30 15:06:12 -0700556 private boolean applyPictureInPictureParams(@NonNull PictureInPictureParams params) {
557 final boolean changed = (mPictureInPictureParams == null) ? true : !Objects.equals(
558 mPictureInPictureParams.getAspectRatioRational(), params.getAspectRatioRational());
559 if (changed) {
560 mPictureInPictureParams = params;
561 mPipBoundsHandler.onAspectRatioChanged(params.getAspectRatio());
Hongwei Wang8e8a8ae2020-03-03 11:06:56 -0800562 }
Hongwei Wang8c95ce52020-04-30 15:06:12 -0700563 return changed;
Hongwei Wang8e8a8ae2020-03-03 11:06:56 -0800564 }
Ben Lin7d6b8e72020-02-27 17:48:16 -0800565
566 /**
Winson Chung55701472020-03-04 19:30:30 -0800567 * Animates resizing of the pinned stack given the duration.
Ben Lin7d6b8e72020-02-27 17:48:16 -0800568 */
Winson Chung55701472020-03-04 19:30:30 -0800569 public void scheduleAnimateResizePip(Rect toBounds, int duration,
570 Consumer<Rect> updateBoundsCallback) {
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700571 if (mShouldDeferEnteringPip) {
572 Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred");
573 return;
574 }
Winson Chungd7302542020-06-08 17:09:45 +0000575 scheduleAnimateResizePip(mLastReportedBounds, toBounds,
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800576 TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback);
Ben Lin7d6b8e72020-02-27 17:48:16 -0800577 }
578
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800579 private void scheduleAnimateResizePip(Rect currentBounds, Rect destinationBounds,
Winson Chungd7302542020-06-08 17:09:45 +0000580 @PipAnimationController.TransitionDirection int direction, int durationMs,
581 Consumer<Rect> updateBoundsCallback) {
Winson Chung55701472020-03-04 19:30:30 -0800582 if (!mInPip) {
Hongwei Wange8e32862020-04-08 13:23:45 -0700583 // can be initiated in other component, ignore if we are no longer in PIP
Winson Chung55701472020-03-04 19:30:30 -0800584 return;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800585 }
Winson Chungc4d4ee82020-05-05 12:51:06 -0700586
Winson Chung55701472020-03-04 19:30:30 -0800587 SomeArgs args = SomeArgs.obtain();
588 args.arg1 = updateBoundsCallback;
589 args.arg2 = currentBounds;
590 args.arg3 = destinationBounds;
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800591 args.argi1 = direction;
Winson Chung55701472020-03-04 19:30:30 -0800592 args.argi2 = durationMs;
593 mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_ANIMATE, args));
594 }
595
596 /**
597 * Directly perform manipulation/resize on the leash. This will not perform any
598 * {@link WindowContainerTransaction} until {@link #scheduleFinishResizePip} is called.
599 */
600 public void scheduleResizePip(Rect toBounds, Consumer<Rect> updateBoundsCallback) {
Winson Chung55701472020-03-04 19:30:30 -0800601 SomeArgs args = SomeArgs.obtain();
602 args.arg1 = updateBoundsCallback;
603 args.arg2 = toBounds;
604 mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_IMMEDIATE, args));
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800605 }
606
Ben Lin7d6b8e72020-02-27 17:48:16 -0800607 /**
Ben Lin75ba9c32020-03-19 17:55:12 -0700608 * Directly perform a scaled matrix transformation on the leash. This will not perform any
609 * {@link WindowContainerTransaction} until {@link #scheduleFinishResizePip} is called.
610 */
611 public void scheduleUserResizePip(Rect startBounds, Rect toBounds,
612 Consumer<Rect> updateBoundsCallback) {
613 SomeArgs args = SomeArgs.obtain();
614 args.arg1 = updateBoundsCallback;
615 args.arg2 = startBounds;
616 args.arg3 = toBounds;
617 mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_USER, args));
618 }
619
620 /**
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700621 * Finish an intermediate resize operation. This is expected to be called after
Winson Chung55701472020-03-04 19:30:30 -0800622 * {@link #scheduleResizePip}.
Ben Lin7d6b8e72020-02-27 17:48:16 -0800623 */
Winson Chung55701472020-03-04 19:30:30 -0800624 public void scheduleFinishResizePip(Rect destinationBounds) {
Ben Lin4a8e2572020-05-08 15:55:15 -0700625 scheduleFinishResizePip(destinationBounds, null);
626 }
627
628 /**
629 * Same as {@link #scheduleFinishResizePip} but with a callback.
630 */
631 public void scheduleFinishResizePip(Rect destinationBounds,
632 Consumer<Rect> updateBoundsCallback) {
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700633 final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
634 mSurfaceTransactionHelper
635 .crop(tx, mLeash, destinationBounds)
Ben Linf603b272020-03-23 15:39:07 -0700636 .resetScale(tx, mLeash, destinationBounds)
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700637 .round(tx, mLeash, mInPip);
Ben Lin4a8e2572020-05-08 15:55:15 -0700638 scheduleFinishResizePip(tx, destinationBounds, TRANSITION_DIRECTION_NONE,
639 updateBoundsCallback);
Ben Lin7d6b8e72020-02-27 17:48:16 -0800640 }
641
Winson Chung55701472020-03-04 19:30:30 -0800642 private void scheduleFinishResizePip(SurfaceControl.Transaction tx,
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800643 Rect destinationBounds, @PipAnimationController.TransitionDirection int direction,
Winson Chung55701472020-03-04 19:30:30 -0800644 Consumer<Rect> updateBoundsCallback) {
Hongwei Wange8e32862020-04-08 13:23:45 -0700645 if (!mInPip) {
646 // can be initiated in other component, ignore if we are no longer in PIP
647 return;
648 }
Winson Chung55701472020-03-04 19:30:30 -0800649 SomeArgs args = SomeArgs.obtain();
650 args.arg1 = updateBoundsCallback;
651 args.arg2 = tx;
652 args.arg3 = destinationBounds;
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800653 args.argi1 = direction;
Winson Chung55701472020-03-04 19:30:30 -0800654 mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_FINISH_RESIZE, args));
655 }
656
657 /**
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800658 * Offset the PiP window by a given offset on Y-axis, triggered also from screen rotation.
Winson Chung55701472020-03-04 19:30:30 -0800659 */
660 public void scheduleOffsetPip(Rect originalBounds, int offset, int duration,
661 Consumer<Rect> updateBoundsCallback) {
662 if (!mInPip) {
Hongwei Wange8e32862020-04-08 13:23:45 -0700663 // can be initiated in other component, ignore if we are no longer in PIP
Winson Chung55701472020-03-04 19:30:30 -0800664 return;
665 }
Hongwei Wangf4e4bab2020-05-19 11:28:32 -0700666 if (mShouldDeferEnteringPip) {
667 Log.d(TAG, "skip scheduleOffsetPip, entering pip deferred");
668 return;
669 }
Winson Chung55701472020-03-04 19:30:30 -0800670 SomeArgs args = SomeArgs.obtain();
671 args.arg1 = updateBoundsCallback;
672 args.arg2 = originalBounds;
673 // offset would be zero if triggered from screen rotation.
674 args.argi1 = offset;
675 args.argi2 = duration;
676 mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_ANIMATE, args));
677 }
678
679 private void offsetPip(Rect originalBounds, int xOffset, int yOffset, int durationMs) {
680 if (Looper.myLooper() != mUpdateHandler.getLooper()) {
681 throw new RuntimeException("Callers should call scheduleOffsetPip() instead of this "
682 + "directly");
683 }
684 if (mTaskInfo == null) {
685 Log.w(TAG, "mTaskInfo is not set");
686 return;
687 }
688 final Rect destinationBounds = new Rect(originalBounds);
689 destinationBounds.offset(xOffset, yOffset);
Winson Chungd7302542020-06-08 17:09:45 +0000690 animateResizePip(originalBounds, destinationBounds, TRANSITION_DIRECTION_SAME, durationMs);
Winson Chung55701472020-03-04 19:30:30 -0800691 }
692
693 private void resizePip(Rect destinationBounds) {
694 if (Looper.myLooper() != mUpdateHandler.getLooper()) {
695 throw new RuntimeException("Callers should call scheduleResizePip() instead of this "
696 + "directly");
697 }
Winson Chungc4d4ee82020-05-05 12:51:06 -0700698 // Could happen when exitPip
Winson Chung55701472020-03-04 19:30:30 -0800699 if (mToken == null || mLeash == null) {
700 Log.w(TAG, "Abort animation, invalid leash");
701 return;
702 }
Ben Lin5400de32020-05-06 19:28:09 -0700703 mLastReportedBounds.set(destinationBounds);
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700704 final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
705 mSurfaceTransactionHelper
706 .crop(tx, mLeash, destinationBounds)
707 .round(tx, mLeash, mInPip);
708 tx.apply();
Winson Chung55701472020-03-04 19:30:30 -0800709 }
710
Ben Lin75ba9c32020-03-19 17:55:12 -0700711 private void userResizePip(Rect startBounds, Rect destinationBounds) {
712 if (Looper.myLooper() != mUpdateHandler.getLooper()) {
713 throw new RuntimeException("Callers should call scheduleUserResizePip() instead of "
714 + "this directly");
715 }
Winson Chungc4d4ee82020-05-05 12:51:06 -0700716 // Could happen when exitPip
Ben Lin75ba9c32020-03-19 17:55:12 -0700717 if (mToken == null || mLeash == null) {
718 Log.w(TAG, "Abort animation, invalid leash");
719 return;
720 }
Josh Tsujia8ed97e2020-06-16 17:00:59 -0400721
722 if (startBounds.isEmpty() || destinationBounds.isEmpty()) {
723 Log.w(TAG, "Attempted to user resize PIP to or from empty bounds, aborting.");
724 return;
725 }
726
Ben Lin75ba9c32020-03-19 17:55:12 -0700727 final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
728 mSurfaceTransactionHelper.scale(tx, mLeash, startBounds, destinationBounds);
729 tx.apply();
730 }
731
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800732 private void finishResize(SurfaceControl.Transaction tx, Rect destinationBounds,
Winson Chungc4d4ee82020-05-05 12:51:06 -0700733 @PipAnimationController.TransitionDirection int direction,
734 @PipAnimationController.AnimationType int type) {
Winson Chung55701472020-03-04 19:30:30 -0800735 if (Looper.myLooper() != mUpdateHandler.getLooper()) {
736 throw new RuntimeException("Callers should call scheduleResizePip() instead of this "
737 + "directly");
738 }
Ben Lin7d6b8e72020-02-27 17:48:16 -0800739 mLastReportedBounds.set(destinationBounds);
Winson Chungc4d4ee82020-05-05 12:51:06 -0700740 if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA) {
741 return;
742 }
743
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700744 final WindowContainerTransaction wct = new WindowContainerTransaction();
745 final Rect taskBounds;
Winson Chungc4d4ee82020-05-05 12:51:06 -0700746 if (isInPipDirection(direction)) {
747 // If we are animating from fullscreen using a bounds animation, then reset the
748 // activity windowing mode set by WM, and set the task bounds to the final bounds
749 taskBounds = destinationBounds;
750 wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
751 wct.scheduleFinishEnterPip(mToken, destinationBounds);
752 } else if (isOutPipDirection(direction)) {
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700753 // If we are animating to fullscreen, then we need to reset the override bounds
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700754 // on the task to ensure that the task "matches" the parent's bounds.
755 taskBounds = (direction == TRANSITION_DIRECTION_TO_FULLSCREEN)
756 ? null : destinationBounds;
Hongwei Wang6ef225e2020-06-23 11:16:05 -0700757 applyWindowingModeChangeOnExit(wct, direction);
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700758 } else {
Winson Chungc4d4ee82020-05-05 12:51:06 -0700759 // Just a resize in PIP
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700760 taskBounds = destinationBounds;
Ben Lin7d6b8e72020-02-27 17:48:16 -0800761 }
Winson Chungc4d4ee82020-05-05 12:51:06 -0700762
763 wct.setBounds(mToken, taskBounds);
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700764 wct.setBoundsChangeTransaction(mToken, tx);
jorgegil@google.com79aae3c2020-06-11 10:37:52 -0700765 applyFinishBoundsResize(wct, direction);
766 }
767
768 /**
769 * Applies the window container transaction to finish a bounds resize.
770 *
771 * Called by {@link #finishResize(SurfaceControl.Transaction, Rect, int, int)}} once it has
772 * finished preparing the transaction. It allows subclasses to modify the transaction before
773 * applying it.
774 */
775 public void applyFinishBoundsResize(@NonNull WindowContainerTransaction wct,
776 @PipAnimationController.TransitionDirection int direction) {
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700777 WindowOrganizer.applyTransaction(wct);
Ben Lin7d6b8e72020-02-27 17:48:16 -0800778 }
779
jorgegil@google.com79aae3c2020-06-11 10:37:52 -0700780 /**
781 * The windowing mode to restore to when resizing out of PIP direction. Defaults to undefined
782 * and can be overridden to restore to an alternate windowing mode.
783 */
784 public int getOutPipWindowingMode() {
785 // By default, simply reset the windowing mode to undefined.
786 return WINDOWING_MODE_UNDEFINED;
787 }
788
Winson Chungd7302542020-06-08 17:09:45 +0000789 private void animateResizePip(Rect currentBounds, Rect destinationBounds,
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800790 @PipAnimationController.TransitionDirection int direction, int durationMs) {
Winson Chung55701472020-03-04 19:30:30 -0800791 if (Looper.myLooper() != mUpdateHandler.getLooper()) {
792 throw new RuntimeException("Callers should call scheduleAnimateResizePip() instead of "
793 + "this directly");
Ben Lin7d6b8e72020-02-27 17:48:16 -0800794 }
Winson Chungc4d4ee82020-05-05 12:51:06 -0700795 // Could happen when exitPip
Winson Chung55701472020-03-04 19:30:30 -0800796 if (mToken == null || mLeash == null) {
797 Log.w(TAG, "Abort animation, invalid leash");
798 return;
799 }
Winson Chungc4d4ee82020-05-05 12:51:06 -0700800 mPipAnimationController
Winson Chungd7302542020-06-08 17:09:45 +0000801 .getAnimator(mLeash, currentBounds, destinationBounds)
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800802 .setTransitionDirection(direction)
Winson Chung55701472020-03-04 19:30:30 -0800803 .setPipAnimationCallback(mPipAnimationCallback)
804 .setDuration(durationMs)
Winson Chungc4d4ee82020-05-05 12:51:06 -0700805 .start();
Ben Lin7d6b8e72020-02-27 17:48:16 -0800806 }
807
Hongwei Wang2e725be2020-03-10 11:01:28 -0700808 private Size getMinimalSize(ActivityInfo activityInfo) {
809 if (activityInfo == null || activityInfo.windowLayout == null) {
810 return null;
811 }
812 final ActivityInfo.WindowLayout windowLayout = activityInfo.windowLayout;
Hongwei Wang907fd632020-04-02 17:13:08 -0700813 // -1 will be populated if an activity specifies defaultWidth/defaultHeight in <layout>
814 // without minWidth/minHeight
815 if (windowLayout.minWidth > 0 && windowLayout.minHeight > 0) {
816 return new Size(windowLayout.minWidth, windowLayout.minHeight);
817 }
818 return null;
Hongwei Wang2e725be2020-03-10 11:01:28 -0700819 }
820
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800821 private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) {
822 return params == null
823 ? mPipBoundsHandler.getDefaultAspectRatio()
824 : params.getAspectRatio();
825 }
826
827 /**
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700828 * Sync with {@link #mSplitDivider} on destination bounds if PiP is going to split screen.
829 *
830 * @param destinationBoundsOut contain the updated destination bounds if applicable
831 * @return {@code true} if destinationBounds is altered for split screen
832 */
833 private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut) {
Evan Rosky90d5b6d2020-05-13 19:39:05 -0700834 if (mSplitDivider == null || !mSplitDivider.isDividerVisible()) {
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700835 // bail early if system is not in split screen mode
836 return false;
837 }
838 // PiP window will go to split-secondary mode instead of fullscreen, populates the
839 // split screen bounds here.
840 destinationBoundsOut.set(
841 mSplitDivider.getView().getNonMinimizedSplitScreenSecondaryBounds());
842 return true;
843 }
844
845 /**
Winson Chungc4d4ee82020-05-05 12:51:06 -0700846 * Dumps internal states.
847 */
848 public void dump(PrintWriter pw, String prefix) {
849 final String innerPrefix = prefix + " ";
850 pw.println(prefix + TAG);
851 pw.println(innerPrefix + "mTaskInfo=" + mTaskInfo);
852 pw.println(innerPrefix + "mToken=" + mToken
853 + " binder=" + (mToken != null ? mToken.asBinder() : null));
854 pw.println(innerPrefix + "mLeash=" + mLeash);
855 pw.println(innerPrefix + "mInPip=" + mInPip);
856 pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType);
857 pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams);
858 pw.println(innerPrefix + "mLastReportedBounds=" + mLastReportedBounds);
859 pw.println(innerPrefix + "mInitialState:");
860 for (Map.Entry<IBinder, Configuration> e : mInitialState.entrySet()) {
861 pw.println(innerPrefix + " binder=" + e.getKey()
862 + " winConfig=" + e.getValue().windowConfiguration);
863 }
864 }
865
866 /**
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800867 * Callback interface for PiP transitions (both from and to PiP mode)
868 */
869 public interface PipTransitionCallback {
870 /**
871 * Callback when the pip transition is started.
872 */
Hongwei Wang221fe3d2020-03-26 13:13:04 -0700873 void onPipTransitionStarted(ComponentName activity, int direction);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800874
875 /**
876 * Callback when the pip transition is finished.
877 */
Hongwei Wang221fe3d2020-03-26 13:13:04 -0700878 void onPipTransitionFinished(ComponentName activity, int direction);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800879
880 /**
881 * Callback when the pip transition is cancelled.
882 */
Hongwei Wang221fe3d2020-03-26 13:13:04 -0700883 void onPipTransitionCanceled(ComponentName activity, int direction);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800884 }
885}