Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 1 | /* |
| 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 | |
| 17 | package com.android.systemui.pip; |
| 18 | |
Hongwei Wang | ebf1808 | 2019-09-26 14:25:11 -0700 | [diff] [blame] | 19 | import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; |
| 20 | import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 21 | import static android.util.TypedValue.COMPLEX_UNIT_DIP; |
Hongwei Wang | ebf1808 | 2019-09-26 14:25:11 -0700 | [diff] [blame] | 22 | import static android.view.Surface.ROTATION_0; |
| 23 | import static android.view.Surface.ROTATION_180; |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 24 | |
Hongwei Wang | ebf1808 | 2019-09-26 14:25:11 -0700 | [diff] [blame] | 25 | import android.app.ActivityManager; |
| 26 | import android.app.ActivityTaskManager; |
Hongwei Wang | 43a752b | 2019-09-17 20:20:30 +0000 | [diff] [blame] | 27 | import android.content.ComponentName; |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 28 | import android.content.Context; |
| 29 | import android.content.res.Resources; |
| 30 | import android.graphics.Point; |
| 31 | import android.graphics.Rect; |
| 32 | import android.os.RemoteException; |
| 33 | import android.util.DisplayMetrics; |
| 34 | import android.util.Log; |
| 35 | import android.util.Size; |
| 36 | import android.util.TypedValue; |
| 37 | import android.view.DisplayInfo; |
| 38 | import android.view.Gravity; |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 39 | import android.view.IWindowManager; |
| 40 | import android.view.WindowManagerGlobal; |
Wale Ogunwale | 5794658 | 2020-03-21 14:29:07 -0700 | [diff] [blame] | 41 | import android.window.WindowContainerTransaction; |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 42 | |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 43 | import java.io.PrintWriter; |
| 44 | |
jorgegil@google.com | 5bdec5f | 2020-02-28 13:14:39 -0800 | [diff] [blame] | 45 | import javax.inject.Inject; |
Ben Lin | 6189fa4 | 2020-04-29 14:59:16 -0700 | [diff] [blame] | 46 | import javax.inject.Singleton; |
jorgegil@google.com | 5bdec5f | 2020-02-28 13:14:39 -0800 | [diff] [blame] | 47 | |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 48 | /** |
| 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 Lin | 6189fa4 | 2020-04-29 14:59:16 -0700 | [diff] [blame] | 52 | @Singleton |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 53 | public class PipBoundsHandler { |
| 54 | |
| 55 | private static final String TAG = PipBoundsHandler.class.getSimpleName(); |
| 56 | private static final float INVALID_SNAP_FRACTION = -1f; |
| 57 | |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 58 | private final Context mContext; |
| 59 | private final IWindowManager mWindowManager; |
| 60 | private final PipSnapAlgorithm mSnapAlgorithm; |
| 61 | private final DisplayInfo mDisplayInfo = new DisplayInfo(); |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 62 | private final Rect mTmpInsets = new Rect(); |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 63 | |
Hongwei Wang | 9b75180 | 2019-09-11 12:13:21 -0700 | [diff] [blame] | 64 | /** |
| 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 Wang | 43a752b | 2019-09-17 20:20:30 +0000 | [diff] [blame] | 70 | private ComponentName mLastPipComponentName; |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 71 | private float mReentrySnapFraction = INVALID_SNAP_FRACTION; |
Hongwei Wang | 2e725be | 2020-03-10 11:01:28 -0700 | [diff] [blame] | 72 | private Size mReentrySize; |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 73 | |
| 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 Wang | 2e725be | 2020-03-10 11:01:28 -0700 | [diff] [blame] | 82 | private Size mOverrideMinimalSize; |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 83 | |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 84 | private boolean mIsImeShowing; |
| 85 | private int mImeHeight; |
| 86 | private boolean mIsShelfShowing; |
| 87 | private int mShelfHeight; |
| 88 | |
jorgegil@google.com | 5bdec5f | 2020-02-28 13:14:39 -0800 | [diff] [blame] | 89 | @Inject |
| 90 | public PipBoundsHandler(Context context, PipSnapAlgorithm pipSnapAlgorithm) { |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 91 | mContext = context; |
jorgegil@google.com | 5bdec5f | 2020-02-28 13:14:39 -0800 | [diff] [blame] | 92 | mSnapAlgorithm = pipSnapAlgorithm; |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 93 | mWindowManager = WindowManagerGlobal.getWindowManagerService(); |
Hongwei Wang | 7759895 | 2019-09-17 16:42:37 +0000 | [diff] [blame] | 94 | reloadResources(); |
Hongwei Wang | 43a752b | 2019-09-17 20:20:30 +0000 | [diff] [blame] | 95 | // 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 Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 99 | } |
| 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 Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 127 | public void setMinEdgeSize(int minEdgeSize) { |
| 128 | mCurrentMinSize = minEdgeSize; |
| 129 | } |
| 130 | |
Ben Lin | 3afa6b0 | 2020-04-28 14:22:56 -0700 | [diff] [blame] | 131 | protected float getAspectRatio() { |
| 132 | return mAspectRatio; |
| 133 | } |
| 134 | |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 135 | /** |
Hongwei Wang | 9b75180 | 2019-09-11 12:13:21 -0700 | [diff] [blame] | 136 | * 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 Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 151 | * 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 Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 159 | * 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.com | 62f735e | 2019-10-30 14:37:19 -0700 | [diff] [blame] | 165 | final Rect defaultBounds = getDefaultBounds(INVALID_SNAP_FRACTION, null); |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 166 | 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.com | 62f735e | 2019-10-30 14:37:19 -0700 | [diff] [blame] | 178 | * Responds to IPinnedStackListener on saving reentry snap fraction and size |
Hongwei Wang | 43a752b | 2019-09-17 20:20:30 +0000 | [diff] [blame] | 179 | * for a given {@link ComponentName}. |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 180 | */ |
jorgegil@google.com | 62f735e | 2019-10-30 14:37:19 -0700 | [diff] [blame] | 181 | public void onSaveReentryBounds(ComponentName componentName, Rect bounds) { |
Hongwei Wang | 43a752b | 2019-09-17 20:20:30 +0000 | [diff] [blame] | 182 | mReentrySnapFraction = getSnapFraction(bounds); |
jorgegil@google.com | 62f735e | 2019-10-30 14:37:19 -0700 | [diff] [blame] | 183 | mReentrySize = new Size(bounds.width(), bounds.height()); |
Hongwei Wang | 43a752b | 2019-09-17 20:20:30 +0000 | [diff] [blame] | 184 | mLastPipComponentName = componentName; |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 185 | } |
| 186 | |
| 187 | /** |
jorgegil@google.com | 62f735e | 2019-10-30 14:37:19 -0700 | [diff] [blame] | 188 | * Responds to IPinnedStackListener on resetting reentry snap fraction and size |
Hongwei Wang | 43a752b | 2019-09-17 20:20:30 +0000 | [diff] [blame] | 189 | * for a given {@link ComponentName}. |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 190 | */ |
jorgegil@google.com | 62f735e | 2019-10-30 14:37:19 -0700 | [diff] [blame] | 191 | public void onResetReentryBounds(ComponentName componentName) { |
Hongwei Wang | 43a752b | 2019-09-17 20:20:30 +0000 | [diff] [blame] | 192 | if (componentName.equals(mLastPipComponentName)) { |
jorgegil@google.com | 62f735e | 2019-10-30 14:37:19 -0700 | [diff] [blame] | 193 | onResetReentryBoundsUnchecked(); |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 194 | } |
| 195 | } |
| 196 | |
jorgegil@google.com | 62f735e | 2019-10-30 14:37:19 -0700 | [diff] [blame] | 197 | private void onResetReentryBoundsUnchecked() { |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 198 | mReentrySnapFraction = INVALID_SNAP_FRACTION; |
jorgegil@google.com | 62f735e | 2019-10-30 14:37:19 -0700 | [diff] [blame] | 199 | mReentrySize = null; |
Hongwei Wang | 43a752b | 2019-09-17 20:20:30 +0000 | [diff] [blame] | 200 | mLastPipComponentName = null; |
Hongwei Wang | ebf1808 | 2019-09-26 14:25:11 -0700 | [diff] [blame] | 201 | mLastDestinationBounds.setEmpty(); |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 202 | } |
| 203 | |
Hongwei Wang | 9b75180 | 2019-09-11 12:13:21 -0700 | [diff] [blame] | 204 | public Rect getLastDestinationBounds() { |
| 205 | return mLastDestinationBounds; |
| 206 | } |
| 207 | |
Hongwei Wang | 951dc02 | 2020-03-30 16:16:16 -0700 | [diff] [blame] | 208 | public Rect getDisplayBounds() { |
| 209 | return new Rect(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); |
| 210 | } |
| 211 | |
Winson Chung | c4d4ee8 | 2020-05-05 12:51:06 -0700 | [diff] [blame] | 212 | public int getDisplayRotation() { |
| 213 | return mDisplayInfo.rotation; |
| 214 | } |
| 215 | |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 216 | /** |
| 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 Wang | 85cf41f | 2020-01-15 15:14:47 -0800 | [diff] [blame] | 242 | * @return {@link Rect} of the destination PiP window bounds. |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 243 | */ |
Hongwei Wang | 309cffa | 2020-04-06 11:11:01 -0700 | [diff] [blame] | 244 | Rect getDestinationBounds(ComponentName componentName, float aspectRatio, Rect bounds, |
| 245 | Size minimalSize) { |
| 246 | if (!componentName.equals(mLastPipComponentName)) { |
| 247 | onResetReentryBoundsUnchecked(); |
| 248 | mLastPipComponentName = componentName; |
| 249 | } |
Hongwei Wang | 43a752b | 2019-09-17 20:20:30 +0000 | [diff] [blame] | 250 | final Rect destinationBounds; |
| 251 | if (bounds == null) { |
Ben Lin | 094c775 | 2020-03-11 12:10:51 -0700 | [diff] [blame] | 252 | final Rect defaultBounds = getDefaultBounds(mReentrySnapFraction, mReentrySize); |
Hongwei Wang | ebf1808 | 2019-09-26 14:25:11 -0700 | [diff] [blame] | 253 | destinationBounds = new Rect(defaultBounds); |
Hongwei Wang | 2e725be | 2020-03-10 11:01:28 -0700 | [diff] [blame] | 254 | if (mReentrySnapFraction == INVALID_SNAP_FRACTION && mReentrySize == null) { |
| 255 | mOverrideMinimalSize = minimalSize; |
| 256 | } |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 257 | } else { |
Hongwei Wang | 43a752b | 2019-09-17 20:20:30 +0000 | [diff] [blame] | 258 | destinationBounds = new Rect(bounds); |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 259 | } |
| 260 | if (isValidPictureInPictureAspectRatio(aspectRatio)) { |
Hongwei Wang | 43a752b | 2019-09-17 20:20:30 +0000 | [diff] [blame] | 261 | transformBoundsToAspectRatio(destinationBounds, aspectRatio, |
| 262 | false /* useCurrentMinEdgeSize */); |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 263 | } |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 264 | mAspectRatio = aspectRatio; |
Hongwei Wang | 85cf41f | 2020-01-15 15:14:47 -0800 | [diff] [blame] | 265 | mLastDestinationBounds.set(destinationBounds); |
| 266 | return destinationBounds; |
| 267 | } |
| 268 | |
| 269 | float getDefaultAspectRatio() { |
| 270 | return mDefaultAspectRatio; |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 271 | } |
| 272 | |
| 273 | /** |
Hongwei Wang | ebf1808 | 2019-09-26 14:25:11 -0700 | [diff] [blame] | 274 | * 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 Wang | ebf1808 | 2019-09-26 14:25:11 -0700 | [diff] [blame] | 314 | |
Hongwei Wang | 85cf41f | 2020-01-15 15:14:47 -0800 | [diff] [blame] | 315 | outBounds.set(postChangeStackBounds); |
| 316 | mLastDestinationBounds.set(outBounds); |
| 317 | t.setBounds(pinnedStackInfo.stackToken, outBounds); |
Hongwei Wang | ebf1808 | 2019-09-26 14:25:11 -0700 | [diff] [blame] | 318 | 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 Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 336 | * @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 Lin | 04c83f6 | 2019-12-20 10:56:45 -0800 | [diff] [blame] | 344 | * 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 Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 352 | * 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.com | 62f735e | 2019-10-30 14:37:19 -0700 | [diff] [blame] | 357 | // Save the snap fraction and adjust the size based on the new aspect ratio. |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 358 | final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds, |
| 359 | getMovementBounds(stackBounds)); |
Hongwei Wang | 610d228 | 2020-01-31 15:09:37 -0800 | [diff] [blame] | 360 | 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.com | 62f735e | 2019-10-30 14:37:19 -0700 | [diff] [blame] | 371 | |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 372 | 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 Wang | 2e725be | 2020-03-10 11:01:28 -0700 | [diff] [blame] | 375 | // 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 Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 379 | mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction); |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 380 | } |
| 381 | |
| 382 | /** |
Hongwei Wang | 2e725be | 2020-03-10 11:01:28 -0700 | [diff] [blame] | 383 | * 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.com | 62f735e | 2019-10-30 14:37:19 -0700 | [diff] [blame] | 407 | * @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 Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 409 | */ |
jorgegil@google.com | 62f735e | 2019-10-30 14:37:19 -0700 | [diff] [blame] | 410 | private Rect getDefaultBounds(float snapFraction, Size size) { |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 411 | final Rect defaultBounds = new Rect(); |
jorgegil@google.com | 62f735e | 2019-10-30 14:37:19 -0700 | [diff] [blame] | 412 | if (snapFraction != INVALID_SNAP_FRACTION && size != null) { |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 413 | defaultBounds.set(0, 0, size.getWidth(), size.getHeight()); |
| 414 | final Rect movementBounds = getMovementBounds(defaultBounds); |
| 415 | mSnapAlgorithm.applySnapFraction(defaultBounds, movementBounds, snapFraction); |
| 416 | } else { |
jorgegil@google.com | 62f735e | 2019-10-30 14:37:19 -0700 | [diff] [blame] | 417 | final Rect insetBounds = new Rect(); |
| 418 | getInsetBounds(insetBounds); |
| 419 | size = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio, |
| 420 | mDefaultMinSize, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 421 | 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 Lin | 3afa6b0 | 2020-04-28 14:22:56 -0700 | [diff] [blame] | 432 | protected void getInsetBounds(Rect outRect) { |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 433 | 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 Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 467 | * @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.com | 62f735e | 2019-10-30 14:37:19 -0700 | [diff] [blame] | 470 | public float getSnapFraction(Rect stackBounds) { |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 471 | return mSnapAlgorithm.getSnapFraction(stackBounds, getMovementBounds(stackBounds)); |
| 472 | } |
| 473 | |
| 474 | /** |
jorgegil@google.com | 62f735e | 2019-10-30 14:37:19 -0700 | [diff] [blame] | 475 | * 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 Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 483 | * @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 Wang | 43a752b | 2019-09-17 20:20:30 +0000 | [diff] [blame] | 495 | pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName); |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 496 | pw.println(innerPrefix + "mReentrySnapFraction=" + mReentrySnapFraction); |
Hongwei Wang | 309cffa | 2020-04-06 11:11:01 -0700 | [diff] [blame] | 497 | pw.println(innerPrefix + "mReentrySize=" + mReentrySize); |
Hongwei Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 498 | 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 Wang | 37568ad | 2019-09-04 10:33:51 -0700 | [diff] [blame] | 504 | 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 | } |