blob: 394f9975579db90b4771ea2f479652e5eb12e9cb [file] [log] [blame]
Hongwei Wang37568ad2019-09-04 10:33:51 -07001/*
2 * Copyright (C) 2019 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 Wangebf18082019-09-26 14:25:11 -070019import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
20import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
Hongwei Wang37568ad2019-09-04 10:33:51 -070021import static android.util.TypedValue.COMPLEX_UNIT_DIP;
Hongwei Wangebf18082019-09-26 14:25:11 -070022import static android.view.Surface.ROTATION_0;
23import static android.view.Surface.ROTATION_180;
Hongwei Wang37568ad2019-09-04 10:33:51 -070024
Hongwei Wangebf18082019-09-26 14:25:11 -070025import android.app.ActivityManager;
26import android.app.ActivityTaskManager;
Hongwei Wang43a752b2019-09-17 20:20:30 +000027import android.content.ComponentName;
Hongwei Wang37568ad2019-09-04 10:33:51 -070028import android.content.Context;
29import android.content.res.Resources;
30import android.graphics.Point;
31import android.graphics.Rect;
32import android.os.RemoteException;
33import android.util.DisplayMetrics;
34import android.util.Log;
35import android.util.Size;
36import android.util.TypedValue;
37import android.view.DisplayInfo;
38import android.view.Gravity;
Hongwei Wang37568ad2019-09-04 10:33:51 -070039import android.view.IWindowManager;
40import android.view.WindowManagerGlobal;
Wale Ogunwale57946582020-03-21 14:29:07 -070041import android.window.WindowContainerTransaction;
Hongwei Wang37568ad2019-09-04 10:33:51 -070042
Hongwei Wang37568ad2019-09-04 10:33:51 -070043import java.io.PrintWriter;
44
jorgegil@google.com5bdec5f2020-02-28 13:14:39 -080045import javax.inject.Inject;
Ben Lin6189fa42020-04-29 14:59:16 -070046import javax.inject.Singleton;
jorgegil@google.com5bdec5f2020-02-28 13:14:39 -080047
Hongwei Wang37568ad2019-09-04 10:33:51 -070048/**
49 * Handles bounds calculation for PIP on Phone and other form factors, it keeps tracking variant
50 * state changes originated from Window Manager and is the source of truth for PiP window bounds.
51 */
Ben Lin6189fa42020-04-29 14:59:16 -070052@Singleton
Hongwei Wang37568ad2019-09-04 10:33:51 -070053public class PipBoundsHandler {
54
55 private static final String TAG = PipBoundsHandler.class.getSimpleName();
56 private static final float INVALID_SNAP_FRACTION = -1f;
57
Hongwei Wang37568ad2019-09-04 10:33:51 -070058 private final Context mContext;
59 private final IWindowManager mWindowManager;
60 private final PipSnapAlgorithm mSnapAlgorithm;
61 private final DisplayInfo mDisplayInfo = new DisplayInfo();
Hongwei Wang37568ad2019-09-04 10:33:51 -070062 private final Rect mTmpInsets = new Rect();
Hongwei Wang37568ad2019-09-04 10:33:51 -070063
Hongwei Wang9b751802019-09-11 12:13:21 -070064 /**
65 * Tracks the destination bounds, used for any following
66 * {@link #onMovementBoundsChanged(Rect, Rect, Rect, DisplayInfo)} calculations.
67 */
68 private final Rect mLastDestinationBounds = new Rect();
69
Hongwei Wang43a752b2019-09-17 20:20:30 +000070 private ComponentName mLastPipComponentName;
Hongwei Wang37568ad2019-09-04 10:33:51 -070071 private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
Hongwei Wang2e725be2020-03-10 11:01:28 -070072 private Size mReentrySize;
Hongwei Wang37568ad2019-09-04 10:33:51 -070073
74 private float mDefaultAspectRatio;
75 private float mMinAspectRatio;
76 private float mMaxAspectRatio;
77 private float mAspectRatio;
78 private int mDefaultStackGravity;
79 private int mDefaultMinSize;
80 private Point mScreenEdgeInsets;
81 private int mCurrentMinSize;
Hongwei Wang2e725be2020-03-10 11:01:28 -070082 private Size mOverrideMinimalSize;
Hongwei Wang37568ad2019-09-04 10:33:51 -070083
Hongwei Wang37568ad2019-09-04 10:33:51 -070084 private boolean mIsImeShowing;
85 private int mImeHeight;
86 private boolean mIsShelfShowing;
87 private int mShelfHeight;
88
jorgegil@google.com5bdec5f2020-02-28 13:14:39 -080089 @Inject
90 public PipBoundsHandler(Context context, PipSnapAlgorithm pipSnapAlgorithm) {
Hongwei Wang37568ad2019-09-04 10:33:51 -070091 mContext = context;
jorgegil@google.com5bdec5f2020-02-28 13:14:39 -080092 mSnapAlgorithm = pipSnapAlgorithm;
Hongwei Wang37568ad2019-09-04 10:33:51 -070093 mWindowManager = WindowManagerGlobal.getWindowManagerService();
Hongwei Wang77598952019-09-17 16:42:37 +000094 reloadResources();
Hongwei Wang43a752b2019-09-17 20:20:30 +000095 // Initialize the aspect ratio to the default aspect ratio. Don't do this in reload
96 // resources as it would clobber mAspectRatio when entering PiP from fullscreen which
97 // triggers a configuration change and the resources to be reloaded.
98 mAspectRatio = mDefaultAspectRatio;
Hongwei Wang37568ad2019-09-04 10:33:51 -070099 }
100
101 /**
102 * TODO: move the resources to SysUI package.
103 */
104 private void reloadResources() {
105 final Resources res = mContext.getResources();
106 mDefaultAspectRatio = res.getFloat(
107 com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio);
108 mDefaultStackGravity = res.getInteger(
109 com.android.internal.R.integer.config_defaultPictureInPictureGravity);
110 mDefaultMinSize = res.getDimensionPixelSize(
111 com.android.internal.R.dimen.default_minimal_size_pip_resizable_task);
112 mCurrentMinSize = mDefaultMinSize;
113 final String screenEdgeInsetsDpString = res.getString(
114 com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets);
115 final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty()
116 ? Size.parseSize(screenEdgeInsetsDpString)
117 : null;
118 mScreenEdgeInsets = screenEdgeInsetsDp == null ? new Point()
119 : new Point(dpToPx(screenEdgeInsetsDp.getWidth(), res.getDisplayMetrics()),
120 dpToPx(screenEdgeInsetsDp.getHeight(), res.getDisplayMetrics()));
121 mMinAspectRatio = res.getFloat(
122 com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
123 mMaxAspectRatio = res.getFloat(
124 com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
125 }
126
Hongwei Wang37568ad2019-09-04 10:33:51 -0700127 public void setMinEdgeSize(int minEdgeSize) {
128 mCurrentMinSize = minEdgeSize;
129 }
130
Ben Lin3afa6b02020-04-28 14:22:56 -0700131 protected float getAspectRatio() {
132 return mAspectRatio;
133 }
134
Hongwei Wang37568ad2019-09-04 10:33:51 -0700135 /**
Hongwei Wang9b751802019-09-11 12:13:21 -0700136 * Sets both shelf visibility and its height if applicable.
137 * @return {@code true} if the internal shelf state is changed, {@code false} otherwise.
138 */
139 public boolean setShelfHeight(boolean shelfVisible, int shelfHeight) {
140 final boolean shelfShowing = shelfVisible && shelfHeight > 0;
141 if (shelfShowing == mIsShelfShowing && shelfHeight == mShelfHeight) {
142 return false;
143 }
144
145 mIsShelfShowing = shelfVisible;
146 mShelfHeight = shelfHeight;
147 return true;
148 }
149
150 /**
Hongwei Wang37568ad2019-09-04 10:33:51 -0700151 * Responds to IPinnedStackListener on IME visibility change.
152 */
153 public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
154 mIsImeShowing = imeVisible;
155 mImeHeight = imeHeight;
156 }
157
158 /**
Hongwei Wang37568ad2019-09-04 10:33:51 -0700159 * Responds to IPinnedStackListener on movement bounds change.
160 * Note that both inset and normal bounds will be calculated here rather than in the caller.
161 */
162 public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
163 Rect animatingBounds, DisplayInfo displayInfo) {
164 getInsetBounds(insetBounds);
jorgegil@google.com62f735e2019-10-30 14:37:19 -0700165 final Rect defaultBounds = getDefaultBounds(INVALID_SNAP_FRACTION, null);
Hongwei Wang37568ad2019-09-04 10:33:51 -0700166 normalBounds.set(defaultBounds);
167 if (animatingBounds.isEmpty()) {
168 animatingBounds.set(defaultBounds);
169 }
170 if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
171 transformBoundsToAspectRatio(normalBounds, mAspectRatio,
172 false /* useCurrentMinEdgeSize */);
173 }
174 displayInfo.copyFrom(mDisplayInfo);
175 }
176
177 /**
jorgegil@google.com62f735e2019-10-30 14:37:19 -0700178 * Responds to IPinnedStackListener on saving reentry snap fraction and size
Hongwei Wang43a752b2019-09-17 20:20:30 +0000179 * for a given {@link ComponentName}.
Hongwei Wang37568ad2019-09-04 10:33:51 -0700180 */
jorgegil@google.com62f735e2019-10-30 14:37:19 -0700181 public void onSaveReentryBounds(ComponentName componentName, Rect bounds) {
Hongwei Wang43a752b2019-09-17 20:20:30 +0000182 mReentrySnapFraction = getSnapFraction(bounds);
jorgegil@google.com62f735e2019-10-30 14:37:19 -0700183 mReentrySize = new Size(bounds.width(), bounds.height());
Hongwei Wang43a752b2019-09-17 20:20:30 +0000184 mLastPipComponentName = componentName;
Hongwei Wang37568ad2019-09-04 10:33:51 -0700185 }
186
187 /**
jorgegil@google.com62f735e2019-10-30 14:37:19 -0700188 * Responds to IPinnedStackListener on resetting reentry snap fraction and size
Hongwei Wang43a752b2019-09-17 20:20:30 +0000189 * for a given {@link ComponentName}.
Hongwei Wang37568ad2019-09-04 10:33:51 -0700190 */
jorgegil@google.com62f735e2019-10-30 14:37:19 -0700191 public void onResetReentryBounds(ComponentName componentName) {
Hongwei Wang43a752b2019-09-17 20:20:30 +0000192 if (componentName.equals(mLastPipComponentName)) {
jorgegil@google.com62f735e2019-10-30 14:37:19 -0700193 onResetReentryBoundsUnchecked();
Hongwei Wang37568ad2019-09-04 10:33:51 -0700194 }
195 }
196
jorgegil@google.com62f735e2019-10-30 14:37:19 -0700197 private void onResetReentryBoundsUnchecked() {
Hongwei Wang37568ad2019-09-04 10:33:51 -0700198 mReentrySnapFraction = INVALID_SNAP_FRACTION;
jorgegil@google.com62f735e2019-10-30 14:37:19 -0700199 mReentrySize = null;
Hongwei Wang43a752b2019-09-17 20:20:30 +0000200 mLastPipComponentName = null;
Hongwei Wangebf18082019-09-26 14:25:11 -0700201 mLastDestinationBounds.setEmpty();
Hongwei Wang37568ad2019-09-04 10:33:51 -0700202 }
203
Hongwei Wang9b751802019-09-11 12:13:21 -0700204 public Rect getLastDestinationBounds() {
205 return mLastDestinationBounds;
206 }
207
Hongwei Wang951dc022020-03-30 16:16:16 -0700208 public Rect getDisplayBounds() {
209 return new Rect(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
210 }
211
Winson Chungc4d4ee82020-05-05 12:51:06 -0700212 public int getDisplayRotation() {
213 return mDisplayInfo.rotation;
214 }
215
Hongwei Wang37568ad2019-09-04 10:33:51 -0700216 /**
217 * Responds to IPinnedStackListener on {@link DisplayInfo} change.
218 * It will normally follow up with a
219 * {@link #onMovementBoundsChanged(Rect, Rect, Rect, DisplayInfo)} callback.
220 */
221 public void onDisplayInfoChanged(DisplayInfo displayInfo) {
222 mDisplayInfo.copyFrom(displayInfo);
223 }
224
225 /**
226 * Responds to IPinnedStackListener on configuration change.
227 */
228 public void onConfigurationChanged() {
229 reloadResources();
230 }
231
232 /**
233 * Responds to IPinnedStackListener on resetting aspect ratio for the pinned window.
234 * It will normally follow up with a
235 * {@link #onMovementBoundsChanged(Rect, Rect, Rect, DisplayInfo)} callback.
236 */
237 public void onAspectRatioChanged(float aspectRatio) {
238 mAspectRatio = aspectRatio;
239 }
240
241 /**
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800242 * @return {@link Rect} of the destination PiP window bounds.
Hongwei Wang37568ad2019-09-04 10:33:51 -0700243 */
Hongwei Wang309cffa2020-04-06 11:11:01 -0700244 Rect getDestinationBounds(ComponentName componentName, float aspectRatio, Rect bounds,
245 Size minimalSize) {
246 if (!componentName.equals(mLastPipComponentName)) {
247 onResetReentryBoundsUnchecked();
248 mLastPipComponentName = componentName;
249 }
Hongwei Wang43a752b2019-09-17 20:20:30 +0000250 final Rect destinationBounds;
251 if (bounds == null) {
Ben Lin094c7752020-03-11 12:10:51 -0700252 final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize);
Hongwei Wangebf18082019-09-26 14:25:11 -0700253 destinationBounds = new Rect(defaultBounds);
Hongwei Wang2e725be2020-03-10 11:01:28 -0700254 if (mReentrySnapFraction == INVALID_SNAP_FRACTION && mReentrySize == null) {
255 mOverrideMinimalSize = minimalSize;
256 }
Hongwei Wang37568ad2019-09-04 10:33:51 -0700257 } else {
Hongwei Wang43a752b2019-09-17 20:20:30 +0000258 destinationBounds = new Rect(bounds);
Hongwei Wang37568ad2019-09-04 10:33:51 -0700259 }
260 if (isValidPictureInPictureAspectRatio(aspectRatio)) {
Hongwei Wang43a752b2019-09-17 20:20:30 +0000261 transformBoundsToAspectRatio(destinationBounds, aspectRatio,
262 false /* useCurrentMinEdgeSize */);
Hongwei Wang37568ad2019-09-04 10:33:51 -0700263 }
Hongwei Wang37568ad2019-09-04 10:33:51 -0700264 mAspectRatio = aspectRatio;
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800265 mLastDestinationBounds.set(destinationBounds);
266 return destinationBounds;
267 }
268
269 float getDefaultAspectRatio() {
270 return mDefaultAspectRatio;
Hongwei Wang37568ad2019-09-04 10:33:51 -0700271 }
272
273 /**
Hongwei Wangebf18082019-09-26 14:25:11 -0700274 * Updates the display info, calculating and returning the new stack and movement bounds in the
275 * new orientation of the device if necessary.
276 *
277 * @return {@code true} if internal {@link DisplayInfo} is rotated, {@code false} otherwise.
278 */
279 public boolean onDisplayRotationChanged(Rect outBounds, int displayId, int fromRotation,
280 int toRotation, WindowContainerTransaction t) {
281 // Bail early if the event is not sent to current {@link #mDisplayInfo}
282 if ((displayId != mDisplayInfo.displayId) || (fromRotation == toRotation)) {
283 return false;
284 }
285
286 // Bail early if the pinned stack is staled.
287 final ActivityManager.StackInfo pinnedStackInfo;
288 try {
289 pinnedStackInfo = ActivityTaskManager.getService()
290 .getStackInfo(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
291 if (pinnedStackInfo == null) return false;
292 } catch (RemoteException e) {
293 Log.e(TAG, "Failed to get StackInfo for pinned stack", e);
294 return false;
295 }
296
297 // Calculate the snap fraction of the current stack along the old movement bounds
298 final Rect postChangeStackBounds = new Rect(mLastDestinationBounds);
299 final float snapFraction = getSnapFraction(postChangeStackBounds);
300
301 // Populate the new {@link #mDisplayInfo}.
302 // The {@link DisplayInfo} queried from DisplayManager would be the one before rotation,
303 // therefore, the width/height may require a swap first.
304 // Moving forward, we should get the new dimensions after rotation from DisplayLayout.
305 mDisplayInfo.rotation = toRotation;
306 updateDisplayInfoIfNeeded();
307
308 // Calculate the stack bounds in the new orientation based on same fraction along the
309 // rotated movement bounds.
310 final Rect postChangeMovementBounds = getMovementBounds(postChangeStackBounds,
311 false /* adjustForIme */);
312 mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds,
313 snapFraction);
Hongwei Wangebf18082019-09-26 14:25:11 -0700314
Hongwei Wang85cf41f2020-01-15 15:14:47 -0800315 outBounds.set(postChangeStackBounds);
316 mLastDestinationBounds.set(outBounds);
317 t.setBounds(pinnedStackInfo.stackToken, outBounds);
Hongwei Wangebf18082019-09-26 14:25:11 -0700318 return true;
319 }
320
321 private void updateDisplayInfoIfNeeded() {
322 final boolean updateNeeded;
323 if ((mDisplayInfo.rotation == ROTATION_0) || (mDisplayInfo.rotation == ROTATION_180)) {
324 updateNeeded = (mDisplayInfo.logicalWidth > mDisplayInfo.logicalHeight);
325 } else {
326 updateNeeded = (mDisplayInfo.logicalWidth < mDisplayInfo.logicalHeight);
327 }
328 if (updateNeeded) {
329 final int newLogicalHeight = mDisplayInfo.logicalWidth;
330 mDisplayInfo.logicalWidth = mDisplayInfo.logicalHeight;
331 mDisplayInfo.logicalHeight = newLogicalHeight;
332 }
333 }
334
335 /**
Hongwei Wang37568ad2019-09-04 10:33:51 -0700336 * @return whether the given {@param aspectRatio} is valid.
337 */
338 private boolean isValidPictureInPictureAspectRatio(float aspectRatio) {
339 return Float.compare(mMinAspectRatio, aspectRatio) <= 0
340 && Float.compare(aspectRatio, mMaxAspectRatio) <= 0;
341 }
342
343 /**
Ben Lin04c83f62019-12-20 10:56:45 -0800344 * Sets the current bound with the currently store aspect ratio.
345 * @param stackBounds
346 */
347 public void transformBoundsToAspectRatio(Rect stackBounds) {
348 transformBoundsToAspectRatio(stackBounds, mAspectRatio, true);
349 }
350
351 /**
Hongwei Wang37568ad2019-09-04 10:33:51 -0700352 * Set the current bounds (or the default bounds if there are no current bounds) with the
353 * specified aspect ratio.
354 */
355 private void transformBoundsToAspectRatio(Rect stackBounds, float aspectRatio,
356 boolean useCurrentMinEdgeSize) {
jorgegil@google.com62f735e2019-10-30 14:37:19 -0700357 // Save the snap fraction and adjust the size based on the new aspect ratio.
Hongwei Wang37568ad2019-09-04 10:33:51 -0700358 final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
359 getMovementBounds(stackBounds));
Hongwei Wang610d2282020-01-31 15:09:37 -0800360 final int minEdgeSize;
361 final Size size;
362 if (useCurrentMinEdgeSize) {
363 minEdgeSize = mCurrentMinSize;
364 size = mSnapAlgorithm.getSizeForAspectRatio(
365 new Size(stackBounds.width(), stackBounds.height()), aspectRatio, minEdgeSize);
366 } else {
367 minEdgeSize = mDefaultMinSize;
368 size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, minEdgeSize,
369 mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
370 }
jorgegil@google.com62f735e2019-10-30 14:37:19 -0700371
Hongwei Wang37568ad2019-09-04 10:33:51 -0700372 final int left = (int) (stackBounds.centerX() - size.getWidth() / 2f);
373 final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f);
374 stackBounds.set(left, top, left + size.getWidth(), top + size.getHeight());
Hongwei Wang2e725be2020-03-10 11:01:28 -0700375 // apply the override minimal size if applicable, this minimal size is specified by app
376 if (mOverrideMinimalSize != null) {
377 transformBoundsToMinimalSize(stackBounds, aspectRatio, mOverrideMinimalSize);
378 }
Hongwei Wang37568ad2019-09-04 10:33:51 -0700379 mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction);
Hongwei Wang37568ad2019-09-04 10:33:51 -0700380 }
381
382 /**
Hongwei Wang2e725be2020-03-10 11:01:28 -0700383 * Transforms a given bounds to meet the minimal size constraints.
384 * This function assumes the given {@param stackBounds} qualifies {@param aspectRatio}.
385 */
386 private void transformBoundsToMinimalSize(Rect stackBounds, float aspectRatio,
387 Size minimalSize) {
388 if (minimalSize == null) return;
389 final Size adjustedMinimalSize;
390 final float minimalSizeAspectRatio =
391 minimalSize.getWidth() / (float) minimalSize.getHeight();
392 if (minimalSizeAspectRatio > aspectRatio) {
393 // minimal size is wider, fixed the width and increase the height
394 adjustedMinimalSize = new Size(
395 minimalSize.getWidth(), (int) (minimalSize.getWidth() / aspectRatio));
396 } else {
397 adjustedMinimalSize = new Size(
398 (int) (minimalSize.getHeight() * aspectRatio), minimalSize.getHeight());
399 }
400 final Rect containerBounds = new Rect(stackBounds);
401 Gravity.apply(mDefaultStackGravity,
402 adjustedMinimalSize.getWidth(), adjustedMinimalSize.getHeight(),
403 containerBounds, stackBounds);
404 }
405
406 /**
jorgegil@google.com62f735e2019-10-30 14:37:19 -0700407 * @return the default bounds to show the PIP, if a {@param snapFraction} and {@param size} are
408 * provided, then it will apply the default bounds to the provided snap fraction and size.
Hongwei Wang37568ad2019-09-04 10:33:51 -0700409 */
jorgegil@google.com62f735e2019-10-30 14:37:19 -0700410 private Rect getDefaultBounds(float snapFraction, Size size) {
Hongwei Wang37568ad2019-09-04 10:33:51 -0700411 final Rect defaultBounds = new Rect();
jorgegil@google.com62f735e2019-10-30 14:37:19 -0700412 if (snapFraction != INVALID_SNAP_FRACTION && size != null) {
Hongwei Wang37568ad2019-09-04 10:33:51 -0700413 defaultBounds.set(0, 0, size.getWidth(), size.getHeight());
414 final Rect movementBounds = getMovementBounds(defaultBounds);
415 mSnapAlgorithm.applySnapFraction(defaultBounds, movementBounds, snapFraction);
416 } else {
jorgegil@google.com62f735e2019-10-30 14:37:19 -0700417 final Rect insetBounds = new Rect();
418 getInsetBounds(insetBounds);
419 size = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio,
420 mDefaultMinSize, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
Hongwei Wang37568ad2019-09-04 10:33:51 -0700421 Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds,
422 0, Math.max(mIsImeShowing ? mImeHeight : 0,
423 mIsShelfShowing ? mShelfHeight : 0),
424 defaultBounds);
425 }
426 return defaultBounds;
427 }
428
429 /**
430 * Populates the bounds on the screen that the PIP can be visible in.
431 */
Ben Lin3afa6b02020-04-28 14:22:56 -0700432 protected void getInsetBounds(Rect outRect) {
Hongwei Wang37568ad2019-09-04 10:33:51 -0700433 try {
434 mWindowManager.getStableInsets(mContext.getDisplayId(), mTmpInsets);
435 outRect.set(mTmpInsets.left + mScreenEdgeInsets.x,
436 mTmpInsets.top + mScreenEdgeInsets.y,
437 mDisplayInfo.logicalWidth - mTmpInsets.right - mScreenEdgeInsets.x,
438 mDisplayInfo.logicalHeight - mTmpInsets.bottom - mScreenEdgeInsets.y);
439 } catch (RemoteException e) {
440 Log.e(TAG, "Failed to get stable insets from WM", e);
441 }
442 }
443
444 /**
445 * @return the movement bounds for the given {@param stackBounds} and the current state of the
446 * controller.
447 */
448 private Rect getMovementBounds(Rect stackBounds) {
449 return getMovementBounds(stackBounds, true /* adjustForIme */);
450 }
451
452 /**
453 * @return the movement bounds for the given {@param stackBounds} and the current state of the
454 * controller.
455 */
456 private Rect getMovementBounds(Rect stackBounds, boolean adjustForIme) {
457 final Rect movementBounds = new Rect();
458 getInsetBounds(movementBounds);
459
460 // Apply the movement bounds adjustments based on the current state.
461 mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds,
462 (adjustForIme && mIsImeShowing) ? mImeHeight : 0);
463 return movementBounds;
464 }
465
466 /**
Hongwei Wang37568ad2019-09-04 10:33:51 -0700467 * @return the default snap fraction to apply instead of the default gravity when calculating
468 * the default stack bounds when first entering PiP.
469 */
jorgegil@google.com62f735e2019-10-30 14:37:19 -0700470 public float getSnapFraction(Rect stackBounds) {
Hongwei Wang37568ad2019-09-04 10:33:51 -0700471 return mSnapAlgorithm.getSnapFraction(stackBounds, getMovementBounds(stackBounds));
472 }
473
474 /**
jorgegil@google.com62f735e2019-10-30 14:37:19 -0700475 * Applies the given snap fraction to the given stack bounds.
476 */
477 public void applySnapFraction(Rect stackBounds, float snapFraction) {
478 final Rect movementBounds = getMovementBounds(stackBounds);
479 mSnapAlgorithm.applySnapFraction(stackBounds, movementBounds, snapFraction);
480 }
481
482 /**
Hongwei Wang37568ad2019-09-04 10:33:51 -0700483 * @return the pixels for a given dp value.
484 */
485 private int dpToPx(float dpValue, DisplayMetrics dm) {
486 return (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, dpValue, dm);
487 }
488
489 /**
490 * Dumps internal states.
491 */
492 public void dump(PrintWriter pw, String prefix) {
493 final String innerPrefix = prefix + " ";
494 pw.println(prefix + TAG);
Hongwei Wang43a752b2019-09-17 20:20:30 +0000495 pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
Hongwei Wang37568ad2019-09-04 10:33:51 -0700496 pw.println(innerPrefix + "mReentrySnapFraction=" + mReentrySnapFraction);
Hongwei Wang309cffa2020-04-06 11:11:01 -0700497 pw.println(innerPrefix + "mReentrySize=" + mReentrySize);
Hongwei Wang37568ad2019-09-04 10:33:51 -0700498 pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo);
499 pw.println(innerPrefix + "mDefaultAspectRatio=" + mDefaultAspectRatio);
500 pw.println(innerPrefix + "mMinAspectRatio=" + mMinAspectRatio);
501 pw.println(innerPrefix + "mMaxAspectRatio=" + mMaxAspectRatio);
502 pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
503 pw.println(innerPrefix + "mDefaultStackGravity=" + mDefaultStackGravity);
Hongwei Wang37568ad2019-09-04 10:33:51 -0700504 pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
505 pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
506 pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
507 pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
508 mSnapAlgorithm.dump(pw, innerPrefix);
509 }
510}