blob: 8d6ce4718aefe7e04d82b297a65187cd58ac5e9f [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
Hongwei Wangd39583a2020-03-04 11:14:32 -080019import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
20
Hongwei Wang85cf41f2020-01-15 15:14:47 -080021import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA;
22import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_BOUNDS;
Hongwei Wangdf8bb002020-03-03 17:41:02 -080023import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_NONE;
24import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_SAME;
25import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_FULLSCREEN;
26import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP;
Hongwei Wang5c52ff82020-04-20 16:02:30 -070027import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_SPLIT_SCREEN;
28import static com.android.systemui.pip.PipAnimationController.isOutPipDirection;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080029
30import android.annotation.NonNull;
31import android.annotation.Nullable;
32import android.app.ActivityManager;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080033import android.app.PictureInPictureParams;
Hongwei Wang221fe3d2020-03-26 13:13:04 -070034import android.content.ComponentName;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080035import android.content.Context;
Hongwei Wang2e725be2020-03-10 11:01:28 -070036import android.content.pm.ActivityInfo;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080037import android.graphics.Rect;
38import android.os.Handler;
Hongwei Wangfbc25fe2020-03-16 11:59:28 -070039import android.os.IBinder;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080040import android.os.Looper;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080041import android.util.Log;
Hongwei Wang2e725be2020-03-10 11:01:28 -070042import android.util.Size;
Hongwei Wangd39583a2020-03-04 11:14:32 -080043import android.view.SurfaceControl;
Wale Ogunwaleadf116e2020-03-27 16:36:01 -070044import android.window.TaskOrganizer;
45import android.window.WindowContainerToken;
Wale Ogunwale57946582020-03-21 14:29:07 -070046import android.window.WindowContainerTransaction;
Wale Ogunwale568f9f412020-03-21 22:27:35 -070047import android.window.WindowOrganizer;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080048
Winson Chung55701472020-03-04 19:30:30 -080049import com.android.internal.os.SomeArgs;
Hongwei Wangdf8bb002020-03-03 17:41:02 -080050import com.android.systemui.R;
Winson Chung55701472020-03-04 19:30:30 -080051import com.android.systemui.pip.phone.PipUpdateThread;
Hongwei Wang5c52ff82020-04-20 16:02:30 -070052import com.android.systemui.stackdivider.Divider;
Winson Chung55701472020-03-04 19:30:30 -080053
Hongwei Wang85cf41f2020-01-15 15:14:47 -080054import java.util.ArrayList;
Hongwei Wangfbc25fe2020-03-16 11:59:28 -070055import java.util.HashMap;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080056import java.util.List;
Hongwei Wangfbc25fe2020-03-16 11:59:28 -070057import java.util.Map;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080058import java.util.Objects;
Winson Chung55701472020-03-04 19:30:30 -080059import java.util.function.Consumer;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080060
Ben Lin6189fa42020-04-29 14:59:16 -070061import javax.inject.Inject;
62import javax.inject.Singleton;
63
Hongwei Wang85cf41f2020-01-15 15:14:47 -080064/**
65 * Manages PiP tasks such as resize and offset.
66 *
Wale Ogunwaleadf116e2020-03-27 16:36:01 -070067 * This class listens on {@link TaskOrganizer} callbacks for windowing mode change
Hongwei Wang85cf41f2020-01-15 15:14:47 -080068 * both to and from PiP and issues corresponding animation if applicable.
69 * Normally, we apply series of {@link SurfaceControl.Transaction} when the animator is running
70 * and files a final {@link WindowContainerTransaction} at the end of the transition.
71 *
72 * This class is also responsible for general resize/offset PiP operations within SysUI component,
73 * see also {@link com.android.systemui.pip.phone.PipMotionHelper}.
74 */
Ben Lin6189fa42020-04-29 14:59:16 -070075@Singleton
Wale Ogunwaleadf116e2020-03-27 16:36:01 -070076public class PipTaskOrganizer extends TaskOrganizer {
Hongwei Wang85cf41f2020-01-15 15:14:47 -080077 private static final String TAG = PipTaskOrganizer.class.getSimpleName();
78
Winson Chung55701472020-03-04 19:30:30 -080079 private static final int MSG_RESIZE_IMMEDIATE = 1;
80 private static final int MSG_RESIZE_ANIMATE = 2;
81 private static final int MSG_OFFSET_ANIMATE = 3;
82 private static final int MSG_FINISH_RESIZE = 4;
Ben Lin75ba9c32020-03-19 17:55:12 -070083 private static final int MSG_RESIZE_USER = 5;
Winson Chung55701472020-03-04 19:30:30 -080084
Hongwei Wang85cf41f2020-01-15 15:14:47 -080085 private final Handler mMainHandler;
Winson Chung55701472020-03-04 19:30:30 -080086 private final Handler mUpdateHandler;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080087 private final PipBoundsHandler mPipBoundsHandler;
88 private final PipAnimationController mPipAnimationController;
89 private final List<PipTransitionCallback> mPipTransitionCallbacks = new ArrayList<>();
Hongwei Wang85cf41f2020-01-15 15:14:47 -080090 private final Rect mLastReportedBounds = new Rect();
Ben Linede9a602020-02-26 12:16:09 -080091 private final int mEnterExitAnimationDuration;
Hongwei Wangec3cb3c2020-03-09 10:43:21 -070092 private final PipSurfaceTransactionHelper mSurfaceTransactionHelper;
Hongwei Wangfbc25fe2020-03-16 11:59:28 -070093 private final Map<IBinder, Rect> mBoundsToRestore = new HashMap<>();
Hongwei Wang5c52ff82020-04-20 16:02:30 -070094 private final Divider mSplitDivider;
Hongwei Wang85cf41f2020-01-15 15:14:47 -080095
Winson Chung55701472020-03-04 19:30:30 -080096 // These callbacks are called on the update thread
Hongwei Wang85cf41f2020-01-15 15:14:47 -080097 private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
98 new PipAnimationController.PipAnimationCallback() {
99 @Override
Winson Chung55701472020-03-04 19:30:30 -0800100 public void onPipAnimationStart(PipAnimationController.PipTransitionAnimator animator) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800101 mMainHandler.post(() -> {
102 for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
103 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
Hongwei Wang221fe3d2020-03-26 13:13:04 -0700104 callback.onPipTransitionStarted(mTaskInfo.baseActivity,
105 animator.getTransitionDirection());
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800106 }
107 });
108 }
109
110 @Override
Winson Chung55701472020-03-04 19:30:30 -0800111 public void onPipAnimationEnd(SurfaceControl.Transaction tx,
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800112 PipAnimationController.PipTransitionAnimator animator) {
Ben Lin3cd7bc32020-04-07 17:15:21 -0700113 finishResize(tx, animator.getDestinationBounds(), animator.getTransitionDirection());
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800114 mMainHandler.post(() -> {
115 for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
116 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
Hongwei Wang221fe3d2020-03-26 13:13:04 -0700117 callback.onPipTransitionFinished(mTaskInfo.baseActivity,
118 animator.getTransitionDirection());
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800119 }
120 });
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800121 }
122
123 @Override
Winson Chung55701472020-03-04 19:30:30 -0800124 public void onPipAnimationCancel(PipAnimationController.PipTransitionAnimator animator) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800125 mMainHandler.post(() -> {
126 for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
127 final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
Hongwei Wang221fe3d2020-03-26 13:13:04 -0700128 callback.onPipTransitionCanceled(mTaskInfo.baseActivity,
129 animator.getTransitionDirection());
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800130 }
131 });
132 }
133 };
134
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800135 @SuppressWarnings("unchecked")
Hongwei Wange8e32862020-04-08 13:23:45 -0700136 private final Handler.Callback mUpdateCallbacks = (msg) -> {
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800137 SomeArgs args = (SomeArgs) msg.obj;
138 Consumer<Rect> updateBoundsCallback = (Consumer<Rect>) args.arg1;
139 switch (msg.what) {
140 case MSG_RESIZE_IMMEDIATE: {
141 Rect toBounds = (Rect) args.arg2;
142 resizePip(toBounds);
143 if (updateBoundsCallback != null) {
144 updateBoundsCallback.accept(toBounds);
Winson Chung55701472020-03-04 19:30:30 -0800145 }
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800146 break;
Winson Chung55701472020-03-04 19:30:30 -0800147 }
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800148 case MSG_RESIZE_ANIMATE: {
149 Rect currentBounds = (Rect) args.arg2;
150 Rect toBounds = (Rect) args.arg3;
151 int duration = args.argi2;
152 animateResizePip(currentBounds, toBounds, args.argi1 /* direction */, duration);
153 if (updateBoundsCallback != null) {
154 updateBoundsCallback.accept(toBounds);
155 }
156 break;
157 }
158 case MSG_OFFSET_ANIMATE: {
159 Rect originalBounds = (Rect) args.arg2;
160 final int offset = args.argi1;
161 final int duration = args.argi2;
162 offsetPip(originalBounds, 0 /* xOffset */, offset, duration);
163 Rect toBounds = new Rect(originalBounds);
164 toBounds.offset(0, offset);
165 if (updateBoundsCallback != null) {
166 updateBoundsCallback.accept(toBounds);
167 }
168 break;
169 }
170 case MSG_FINISH_RESIZE: {
171 SurfaceControl.Transaction tx = (SurfaceControl.Transaction) args.arg2;
172 Rect toBounds = (Rect) args.arg3;
173 finishResize(tx, toBounds, args.argi1 /* direction */);
174 if (updateBoundsCallback != null) {
175 updateBoundsCallback.accept(toBounds);
176 }
177 break;
178 }
Ben Lin75ba9c32020-03-19 17:55:12 -0700179 case MSG_RESIZE_USER: {
180 Rect startBounds = (Rect) args.arg2;
181 Rect toBounds = (Rect) args.arg3;
182 userResizePip(startBounds, toBounds);
183 break;
184 }
Winson Chung55701472020-03-04 19:30:30 -0800185 }
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800186 args.recycle();
187 return true;
Winson Chung55701472020-03-04 19:30:30 -0800188 };
189
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800190 private ActivityManager.RunningTaskInfo mTaskInfo;
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700191 private WindowContainerToken mToken;
Winson Chung55701472020-03-04 19:30:30 -0800192 private SurfaceControl mLeash;
193 private boolean mInPip;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800194 private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700195 private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
196 mSurfaceControlTransactionFactory;
Hongwei Wang8c95ce52020-04-30 15:06:12 -0700197 private PictureInPictureParams mPictureInPictureParams;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800198
Ben Lin6189fa42020-04-29 14:59:16 -0700199 @Inject
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700200 public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler,
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700201 @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper,
202 @Nullable Divider divider) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800203 mMainHandler = new Handler(Looper.getMainLooper());
Winson Chung55701472020-03-04 19:30:30 -0800204 mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800205 mPipBoundsHandler = boundsHandler;
Ben Linede9a602020-02-26 12:16:09 -0800206 mEnterExitAnimationDuration = context.getResources()
207 .getInteger(R.integer.config_pipResizeAnimationDuration);
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700208 mSurfaceTransactionHelper = surfaceTransactionHelper;
209 mPipAnimationController = new PipAnimationController(context, surfaceTransactionHelper);
210 mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700211 mSplitDivider = divider;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800212 }
213
Winson Chung55701472020-03-04 19:30:30 -0800214 public Handler getUpdateHandler() {
215 return mUpdateHandler;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800216 }
217
Hongwei Wang221fe3d2020-03-26 13:13:04 -0700218 public Rect getLastReportedBounds() {
219 return new Rect(mLastReportedBounds);
220 }
221
Joshua Tsuji0d4cbeb2020-05-01 12:45:41 -0400222 public boolean isInPip() {
223 return mInPip;
224 }
225
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800226 /**
227 * Registers {@link PipTransitionCallback} to receive transition callbacks.
228 */
229 public void registerPipTransitionCallback(PipTransitionCallback callback) {
230 mPipTransitionCallbacks.add(callback);
231 }
232
233 /**
234 * Sets the preferred animation type for one time.
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800235 * This is typically used to set the animation type to
236 * {@link PipAnimationController#ANIM_TYPE_ALPHA}.
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800237 */
238 public void setOneShotAnimationType(@PipAnimationController.AnimationType int animationType) {
239 mOneShotAnimationType = animationType;
240 }
241
Hongwei Wangd39583a2020-03-04 11:14:32 -0800242 /**
243 * Dismiss PiP, this is done in two phases using {@link WindowContainerTransaction}
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700244 * - setActivityWindowingMode to undefined at beginning of the transaction. without changing
245 * the windowing mode of the Task itself. This makes sure the activity render it's final
Hongwei Wangd39583a2020-03-04 11:14:32 -0800246 * configuration while the Task is still in PiP.
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700247 * - setWindowingMode to undefined at the end of transition
Hongwei Wangd39583a2020-03-04 11:14:32 -0800248 * @param animationDurationMs duration in millisecond for the exiting PiP transition
249 */
250 public void dismissPip(int animationDurationMs) {
Hongwei Wang1273c952020-04-30 13:28:29 -0700251 if (!mInPip || mToken == null) {
252 Log.wtf(TAG, "Not allowed to dismissPip in current state"
253 + " mInPip=" + mInPip + " mToken=" + mToken);
254 return;
255 }
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700256 final WindowContainerTransaction wct = new WindowContainerTransaction();
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700257 wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700258 WindowOrganizer.applyTransaction(wct);
Hongwei Wangd39583a2020-03-04 11:14:32 -0800259 final Rect destinationBounds = mBoundsToRestore.remove(mToken.asBinder());
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700260 final int direction = syncWithSplitScreenBounds(destinationBounds)
261 ? TRANSITION_DIRECTION_TO_SPLIT_SCREEN : TRANSITION_DIRECTION_TO_FULLSCREEN;
Hongwei Wangd39583a2020-03-04 11:14:32 -0800262 scheduleAnimateResizePip(mLastReportedBounds, destinationBounds,
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700263 direction, animationDurationMs, null /* updateBoundsCallback */);
Hongwei Wangd39583a2020-03-04 11:14:32 -0800264 mInPip = false;
265 }
266
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800267 @Override
chaviw7de50002020-04-27 12:33:30 -0700268 public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800269 Objects.requireNonNull(info, "Requires RunningTaskInfo");
Hongwei Wang8c95ce52020-04-30 15:06:12 -0700270 mPictureInPictureParams = info.pictureInPictureParams;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800271 final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
Hongwei Wang8c95ce52020-04-30 15:06:12 -0700272 info.topActivity, getAspectRatioOrDefault(mPictureInPictureParams),
Hongwei Wang2e725be2020-03-10 11:01:28 -0700273 null /* bounds */, getMinimalSize(info.topActivityInfo));
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800274 Objects.requireNonNull(destinationBounds, "Missing destination bounds");
275 mTaskInfo = info;
Winson Chung55701472020-03-04 19:30:30 -0800276 mToken = mTaskInfo.token;
277 mInPip = true;
chaviw7de50002020-04-27 12:33:30 -0700278 mLeash = leash;
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700279
Hongwei Wang89f18ff2020-03-10 10:24:35 -0700280 final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
Hongwei Wangfbc25fe2020-03-16 11:59:28 -0700281 mBoundsToRestore.put(mToken.asBinder(), currentBounds);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800282 if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800283 scheduleAnimateResizePip(currentBounds, destinationBounds,
Hongwei Wangd39583a2020-03-04 11:14:32 -0800284 TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration,
285 null /* updateBoundsCallback */);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800286 } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
Winson Chung55701472020-03-04 19:30:30 -0800287 mUpdateHandler.post(() -> mPipAnimationController
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800288 .getAnimator(mLeash, destinationBounds, 0f, 1f)
289 .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800290 .setPipAnimationCallback(mPipAnimationCallback)
Ben Linede9a602020-02-26 12:16:09 -0800291 .setDuration(mEnterExitAnimationDuration)
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800292 .start());
293 mOneShotAnimationType = ANIM_TYPE_BOUNDS;
294 } else {
295 throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType);
296 }
297 }
298
Hongwei Wangd39583a2020-03-04 11:14:32 -0800299 /**
300 * Note that dismissing PiP is now originated from SystemUI, see {@link #dismissPip(int)}.
301 * Meanwhile this callback is invoked whenever the task is removed. For instance:
302 * - as a result of removeStacksInWindowingModes from WM
303 * - activity itself is died
Hongwei Wang180162e2020-04-22 14:40:32 -0700304 * Nevertheless, we simply update the internal state here as all the heavy lifting should
305 * have been done in WM.
Hongwei Wangd39583a2020-03-04 11:14:32 -0800306 */
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800307 @Override
Wale Ogunwaledec34082020-03-22 09:45:00 -0700308 public void onTaskVanished(ActivityManager.RunningTaskInfo info) {
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700309 if (!mInPip) {
310 return;
311 }
Hongwei Wange8e32862020-04-08 13:23:45 -0700312 final WindowContainerToken token = info.token;
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700313 Objects.requireNonNull(token, "Requires valid WindowContainerToken");
Winson Chung55701472020-03-04 19:30:30 -0800314 if (token.asBinder() != mToken.asBinder()) {
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800315 Log.wtf(TAG, "Unrecognized token: " + token);
316 return;
317 }
Hongwei Wang8c95ce52020-04-30 15:06:12 -0700318 mPictureInPictureParams = null;
Winson Chung55701472020-03-04 19:30:30 -0800319 mInPip = false;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800320 }
321
322 @Override
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800323 public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
Hongwei Wange8e32862020-04-08 13:23:45 -0700324 Objects.requireNonNull(mToken, "onTaskInfoChanged requires valid existing mToken");
Hongwei Wang8e8a8ae2020-03-03 11:06:56 -0800325 final PictureInPictureParams newParams = info.pictureInPictureParams;
Hongwei Wang8c95ce52020-04-30 15:06:12 -0700326 if (!applyPictureInPictureParams(newParams)) {
Hongwei Wang8e8a8ae2020-03-03 11:06:56 -0800327 Log.d(TAG, "Ignored onTaskInfoChanged with PiP param: " + newParams);
328 return;
329 }
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800330 final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
Hongwei Wang309cffa2020-04-06 11:11:01 -0700331 info.topActivity, getAspectRatioOrDefault(newParams),
Hongwei Wang2e725be2020-03-10 11:01:28 -0700332 null /* bounds */, getMinimalSize(info.topActivityInfo));
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800333 Objects.requireNonNull(destinationBounds, "Missing destination bounds");
Hongwei Wangd39583a2020-03-04 11:14:32 -0800334 scheduleAnimateResizePip(destinationBounds, mEnterExitAnimationDuration,
335 null /* updateBoundsCallback */);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800336 }
337
Winson Chunga1f869d2020-03-21 23:02:48 -0700338 @Override
339 public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
340 // Do nothing
341 }
342
Hongwei Wang8e8a8ae2020-03-03 11:06:56 -0800343 /**
Hongwei Wang951dc022020-03-30 16:16:16 -0700344 * TODO(b/152809058): consolidate the display info handling logic in SysUI
Hongwei Wang3c981f62020-04-07 16:16:26 -0700345 *
346 * @param destinationBoundsOut the current destination bounds will be populated to this param
Hongwei Wang951dc022020-03-30 16:16:16 -0700347 */
348 @SuppressWarnings("unchecked")
Hongwei Wange0412d12020-04-13 18:27:25 -0700349 public void onMovementBoundsChanged(Rect destinationBoundsOut, boolean fromRotation,
Hongwei Wang3c981f62020-04-07 16:16:26 -0700350 boolean fromImeAdjustment, boolean fromShelfAdjustment) {
Hongwei Wang951dc022020-03-30 16:16:16 -0700351 final PipAnimationController.PipTransitionAnimator animator =
352 mPipAnimationController.getCurrentAnimator();
Hongwei Wang6e68af52020-04-06 12:34:06 -0700353 if (animator == null || !animator.isRunning()
354 || animator.getTransitionDirection() != TRANSITION_DIRECTION_TO_PIP) {
Hongwei Wange0412d12020-04-13 18:27:25 -0700355 if (mInPip && fromRotation) {
356 // this could happen if rotation finishes before the animation
357 mLastReportedBounds.set(destinationBoundsOut);
358 scheduleFinishResizePip(mLastReportedBounds);
359 } else if (!mLastReportedBounds.isEmpty()) {
360 destinationBoundsOut.set(mLastReportedBounds);
361 }
Hongwei Wang6e68af52020-04-06 12:34:06 -0700362 return;
Hongwei Wang951dc022020-03-30 16:16:16 -0700363 }
Hongwei Wang6e68af52020-04-06 12:34:06 -0700364
365 final Rect currentDestinationBounds = animator.getDestinationBounds();
Hongwei Wang3c981f62020-04-07 16:16:26 -0700366 destinationBoundsOut.set(currentDestinationBounds);
Hongwei Wang6e68af52020-04-06 12:34:06 -0700367 if (!fromImeAdjustment && !fromShelfAdjustment
368 && mPipBoundsHandler.getDisplayBounds().contains(currentDestinationBounds)) {
369 // no need to update the destination bounds, bail early
370 return;
371 }
372
373 final Rect newDestinationBounds = mPipBoundsHandler.getDestinationBounds(
Hongwei Wang8c95ce52020-04-30 15:06:12 -0700374 mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams),
Hongwei Wang6e68af52020-04-06 12:34:06 -0700375 null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo));
376 if (newDestinationBounds.equals(currentDestinationBounds)) return;
377 if (animator.getAnimationType() == ANIM_TYPE_BOUNDS) {
378 animator.updateEndValue(newDestinationBounds);
379 }
380 animator.setDestinationBounds(newDestinationBounds);
Hongwei Wang3c981f62020-04-07 16:16:26 -0700381 destinationBoundsOut.set(newDestinationBounds);
Hongwei Wang951dc022020-03-30 16:16:16 -0700382 }
383
384 /**
Hongwei Wang8e8a8ae2020-03-03 11:06:56 -0800385 * @return {@code true} if the aspect ratio is changed since no other parameters within
386 * {@link PictureInPictureParams} would affect the bounds.
387 */
Hongwei Wang8c95ce52020-04-30 15:06:12 -0700388 private boolean applyPictureInPictureParams(@NonNull PictureInPictureParams params) {
389 final boolean changed = (mPictureInPictureParams == null) ? true : !Objects.equals(
390 mPictureInPictureParams.getAspectRatioRational(), params.getAspectRatioRational());
391 if (changed) {
392 mPictureInPictureParams = params;
393 mPipBoundsHandler.onAspectRatioChanged(params.getAspectRatio());
Hongwei Wang8e8a8ae2020-03-03 11:06:56 -0800394 }
Hongwei Wang8c95ce52020-04-30 15:06:12 -0700395 return changed;
Hongwei Wang8e8a8ae2020-03-03 11:06:56 -0800396 }
Ben Lin7d6b8e72020-02-27 17:48:16 -0800397
398 /**
Winson Chung55701472020-03-04 19:30:30 -0800399 * Animates resizing of the pinned stack given the duration.
Ben Lin7d6b8e72020-02-27 17:48:16 -0800400 */
Winson Chung55701472020-03-04 19:30:30 -0800401 public void scheduleAnimateResizePip(Rect toBounds, int duration,
402 Consumer<Rect> updateBoundsCallback) {
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800403 scheduleAnimateResizePip(mLastReportedBounds, toBounds,
404 TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback);
Ben Lin7d6b8e72020-02-27 17:48:16 -0800405 }
406
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800407 private void scheduleAnimateResizePip(Rect currentBounds, Rect destinationBounds,
408 @PipAnimationController.TransitionDirection int direction, int durationMs,
Winson Chung55701472020-03-04 19:30:30 -0800409 Consumer<Rect> updateBoundsCallback) {
Winson Chung55701472020-03-04 19:30:30 -0800410 if (!mInPip) {
Hongwei Wange8e32862020-04-08 13:23:45 -0700411 // can be initiated in other component, ignore if we are no longer in PIP
Winson Chung55701472020-03-04 19:30:30 -0800412 return;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800413 }
Winson Chung55701472020-03-04 19:30:30 -0800414 SomeArgs args = SomeArgs.obtain();
415 args.arg1 = updateBoundsCallback;
416 args.arg2 = currentBounds;
417 args.arg3 = destinationBounds;
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800418 args.argi1 = direction;
Winson Chung55701472020-03-04 19:30:30 -0800419 args.argi2 = durationMs;
420 mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_ANIMATE, args));
421 }
422
423 /**
424 * Directly perform manipulation/resize on the leash. This will not perform any
425 * {@link WindowContainerTransaction} until {@link #scheduleFinishResizePip} is called.
426 */
427 public void scheduleResizePip(Rect toBounds, Consumer<Rect> updateBoundsCallback) {
Winson Chung55701472020-03-04 19:30:30 -0800428 SomeArgs args = SomeArgs.obtain();
429 args.arg1 = updateBoundsCallback;
430 args.arg2 = toBounds;
431 mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_IMMEDIATE, args));
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800432 }
433
Ben Lin7d6b8e72020-02-27 17:48:16 -0800434 /**
Ben Lin75ba9c32020-03-19 17:55:12 -0700435 * Directly perform a scaled matrix transformation on the leash. This will not perform any
436 * {@link WindowContainerTransaction} until {@link #scheduleFinishResizePip} is called.
437 */
438 public void scheduleUserResizePip(Rect startBounds, Rect toBounds,
439 Consumer<Rect> updateBoundsCallback) {
440 SomeArgs args = SomeArgs.obtain();
441 args.arg1 = updateBoundsCallback;
442 args.arg2 = startBounds;
443 args.arg3 = toBounds;
444 mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_USER, args));
445 }
446
447 /**
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700448 * Finish an intermediate resize operation. This is expected to be called after
Winson Chung55701472020-03-04 19:30:30 -0800449 * {@link #scheduleResizePip}.
Ben Lin7d6b8e72020-02-27 17:48:16 -0800450 */
Winson Chung55701472020-03-04 19:30:30 -0800451 public void scheduleFinishResizePip(Rect destinationBounds) {
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700452 final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
453 mSurfaceTransactionHelper
454 .crop(tx, mLeash, destinationBounds)
Ben Linf603b272020-03-23 15:39:07 -0700455 .resetScale(tx, mLeash, destinationBounds)
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700456 .round(tx, mLeash, mInPip);
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800457 scheduleFinishResizePip(tx, destinationBounds, TRANSITION_DIRECTION_NONE, null);
Ben Lin7d6b8e72020-02-27 17:48:16 -0800458 }
459
Winson Chung55701472020-03-04 19:30:30 -0800460 private void scheduleFinishResizePip(SurfaceControl.Transaction tx,
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800461 Rect destinationBounds, @PipAnimationController.TransitionDirection int direction,
Winson Chung55701472020-03-04 19:30:30 -0800462 Consumer<Rect> updateBoundsCallback) {
Hongwei Wange8e32862020-04-08 13:23:45 -0700463 if (!mInPip) {
464 // can be initiated in other component, ignore if we are no longer in PIP
465 return;
466 }
Winson Chung55701472020-03-04 19:30:30 -0800467 SomeArgs args = SomeArgs.obtain();
468 args.arg1 = updateBoundsCallback;
469 args.arg2 = tx;
470 args.arg3 = destinationBounds;
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800471 args.argi1 = direction;
Winson Chung55701472020-03-04 19:30:30 -0800472 mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_FINISH_RESIZE, args));
473 }
474
475 /**
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800476 * Offset the PiP window by a given offset on Y-axis, triggered also from screen rotation.
Winson Chung55701472020-03-04 19:30:30 -0800477 */
478 public void scheduleOffsetPip(Rect originalBounds, int offset, int duration,
479 Consumer<Rect> updateBoundsCallback) {
480 if (!mInPip) {
Hongwei Wange8e32862020-04-08 13:23:45 -0700481 // can be initiated in other component, ignore if we are no longer in PIP
Winson Chung55701472020-03-04 19:30:30 -0800482 return;
483 }
Winson Chung55701472020-03-04 19:30:30 -0800484 SomeArgs args = SomeArgs.obtain();
485 args.arg1 = updateBoundsCallback;
486 args.arg2 = originalBounds;
487 // offset would be zero if triggered from screen rotation.
488 args.argi1 = offset;
489 args.argi2 = duration;
490 mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_ANIMATE, args));
491 }
492
493 private void offsetPip(Rect originalBounds, int xOffset, int yOffset, int durationMs) {
494 if (Looper.myLooper() != mUpdateHandler.getLooper()) {
495 throw new RuntimeException("Callers should call scheduleOffsetPip() instead of this "
496 + "directly");
497 }
498 if (mTaskInfo == null) {
499 Log.w(TAG, "mTaskInfo is not set");
500 return;
501 }
502 final Rect destinationBounds = new Rect(originalBounds);
503 destinationBounds.offset(xOffset, yOffset);
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800504 animateResizePip(originalBounds, destinationBounds, TRANSITION_DIRECTION_SAME, durationMs);
Winson Chung55701472020-03-04 19:30:30 -0800505 }
506
507 private void resizePip(Rect destinationBounds) {
508 if (Looper.myLooper() != mUpdateHandler.getLooper()) {
509 throw new RuntimeException("Callers should call scheduleResizePip() instead of this "
510 + "directly");
511 }
Winson Chung55701472020-03-04 19:30:30 -0800512 // Could happen when dismissPip
513 if (mToken == null || mLeash == null) {
514 Log.w(TAG, "Abort animation, invalid leash");
515 return;
516 }
Hongwei Wangec3cb3c2020-03-09 10:43:21 -0700517 final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
518 mSurfaceTransactionHelper
519 .crop(tx, mLeash, destinationBounds)
520 .round(tx, mLeash, mInPip);
521 tx.apply();
Winson Chung55701472020-03-04 19:30:30 -0800522 }
523
Ben Lin75ba9c32020-03-19 17:55:12 -0700524 private void userResizePip(Rect startBounds, Rect destinationBounds) {
525 if (Looper.myLooper() != mUpdateHandler.getLooper()) {
526 throw new RuntimeException("Callers should call scheduleUserResizePip() instead of "
527 + "this directly");
528 }
529 // Could happen when dismissPip
530 if (mToken == null || mLeash == null) {
531 Log.w(TAG, "Abort animation, invalid leash");
532 return;
533 }
534 final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction();
535 mSurfaceTransactionHelper.scale(tx, mLeash, startBounds, destinationBounds);
536 tx.apply();
537 }
538
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800539 private void finishResize(SurfaceControl.Transaction tx, Rect destinationBounds,
540 @PipAnimationController.TransitionDirection int direction) {
Winson Chung55701472020-03-04 19:30:30 -0800541 if (Looper.myLooper() != mUpdateHandler.getLooper()) {
542 throw new RuntimeException("Callers should call scheduleResizePip() instead of this "
543 + "directly");
544 }
Ben Lin7d6b8e72020-02-27 17:48:16 -0800545 mLastReportedBounds.set(destinationBounds);
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700546 final WindowContainerTransaction wct = new WindowContainerTransaction();
547 final Rect taskBounds;
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700548 if (isOutPipDirection(direction)) {
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700549 // If we are animating to fullscreen, then we need to reset the override bounds
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700550 // on the task to ensure that the task "matches" the parent's bounds.
551 taskBounds = (direction == TRANSITION_DIRECTION_TO_FULLSCREEN)
552 ? null : destinationBounds;
553 // As for the final windowing mode, simply reset it to undefined.
554 wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED);
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700555 } else {
556 taskBounds = destinationBounds;
Ben Lin7d6b8e72020-02-27 17:48:16 -0800557 }
Wale Ogunwaleadf116e2020-03-27 16:36:01 -0700558 if (direction == TRANSITION_DIRECTION_TO_PIP) {
559 wct.scheduleFinishEnterPip(mToken, taskBounds);
560 } else {
561 wct.setBounds(mToken, taskBounds);
562 }
563 wct.setBoundsChangeTransaction(mToken, tx);
564 WindowOrganizer.applyTransaction(wct);
Ben Lin7d6b8e72020-02-27 17:48:16 -0800565 }
566
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800567 private void animateResizePip(Rect currentBounds, Rect destinationBounds,
568 @PipAnimationController.TransitionDirection int direction, int durationMs) {
Winson Chung55701472020-03-04 19:30:30 -0800569 if (Looper.myLooper() != mUpdateHandler.getLooper()) {
570 throw new RuntimeException("Callers should call scheduleAnimateResizePip() instead of "
571 + "this directly");
Ben Lin7d6b8e72020-02-27 17:48:16 -0800572 }
Winson Chung55701472020-03-04 19:30:30 -0800573 // Could happen when dismissPip
574 if (mToken == null || mLeash == null) {
575 Log.w(TAG, "Abort animation, invalid leash");
576 return;
577 }
578 mUpdateHandler.post(() -> mPipAnimationController
Hongwei Wangdf8bb002020-03-03 17:41:02 -0800579 .getAnimator(mLeash, currentBounds, destinationBounds)
580 .setTransitionDirection(direction)
Winson Chung55701472020-03-04 19:30:30 -0800581 .setPipAnimationCallback(mPipAnimationCallback)
582 .setDuration(durationMs)
583 .start());
Ben Lin7d6b8e72020-02-27 17:48:16 -0800584 }
585
Hongwei Wang2e725be2020-03-10 11:01:28 -0700586 private Size getMinimalSize(ActivityInfo activityInfo) {
587 if (activityInfo == null || activityInfo.windowLayout == null) {
588 return null;
589 }
590 final ActivityInfo.WindowLayout windowLayout = activityInfo.windowLayout;
Hongwei Wang907fd632020-04-02 17:13:08 -0700591 // -1 will be populated if an activity specifies defaultWidth/defaultHeight in <layout>
592 // without minWidth/minHeight
593 if (windowLayout.minWidth > 0 && windowLayout.minHeight > 0) {
594 return new Size(windowLayout.minWidth, windowLayout.minHeight);
595 }
596 return null;
Hongwei Wang2e725be2020-03-10 11:01:28 -0700597 }
598
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800599 private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) {
600 return params == null
601 ? mPipBoundsHandler.getDefaultAspectRatio()
602 : params.getAspectRatio();
603 }
604
605 /**
Hongwei Wang5c52ff82020-04-20 16:02:30 -0700606 * Sync with {@link #mSplitDivider} on destination bounds if PiP is going to split screen.
607 *
608 * @param destinationBoundsOut contain the updated destination bounds if applicable
609 * @return {@code true} if destinationBounds is altered for split screen
610 */
611 private boolean syncWithSplitScreenBounds(Rect destinationBoundsOut) {
612 if (mSplitDivider == null || !mSplitDivider.inSplitMode()) {
613 // bail early if system is not in split screen mode
614 return false;
615 }
616 // PiP window will go to split-secondary mode instead of fullscreen, populates the
617 // split screen bounds here.
618 destinationBoundsOut.set(
619 mSplitDivider.getView().getNonMinimizedSplitScreenSecondaryBounds());
620 return true;
621 }
622
623 /**
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800624 * Callback interface for PiP transitions (both from and to PiP mode)
625 */
626 public interface PipTransitionCallback {
627 /**
628 * Callback when the pip transition is started.
629 */
Hongwei Wang221fe3d2020-03-26 13:13:04 -0700630 void onPipTransitionStarted(ComponentName activity, int direction);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800631
632 /**
633 * Callback when the pip transition is finished.
634 */
Hongwei Wang221fe3d2020-03-26 13:13:04 -0700635 void onPipTransitionFinished(ComponentName activity, int direction);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800636
637 /**
638 * Callback when the pip transition is cancelled.
639 */
Hongwei Wang221fe3d2020-03-26 13:13:04 -0700640 void onPipTransitionCanceled(ComponentName activity, int direction);
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800641 }
642}