1 /* 2 * Copyright (C) 2016 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.server.wm; 18 19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 21 import static android.util.TypedValue.COMPLEX_UNIT_DIP; 22 23 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 24 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 25 import static com.android.server.wm.PinnedStackControllerProto.DEFAULT_BOUNDS; 26 import static com.android.server.wm.PinnedStackControllerProto.MOVEMENT_BOUNDS; 27 28 import android.app.RemoteAction; 29 import android.content.pm.ParceledListSlice; 30 import android.content.res.Resources; 31 import android.graphics.Point; 32 import android.graphics.Rect; 33 import android.os.Handler; 34 import android.os.IBinder; 35 import android.os.RemoteException; 36 import android.util.DisplayMetrics; 37 import android.util.Log; 38 import android.util.Size; 39 import android.util.Slog; 40 import android.util.TypedValue; 41 import android.util.proto.ProtoOutputStream; 42 import android.view.DisplayInfo; 43 import android.view.Gravity; 44 import android.view.IPinnedStackController; 45 import android.view.IPinnedStackListener; 46 47 import com.android.internal.policy.PipSnapAlgorithm; 48 import com.android.server.UiThread; 49 50 import java.io.PrintWriter; 51 import java.lang.ref.WeakReference; 52 import java.util.ArrayList; 53 import java.util.List; 54 55 /** 56 * Holds the common state of the pinned stack between the system and SystemUI. If SystemUI ever 57 * needs to be restarted, it will be notified with the last known state. 58 * 59 * Changes to the pinned stack also flow through this controller, and generally, the system only 60 * changes the pinned stack bounds through this controller in two ways: 61 * 62 * 1) When first entering PiP: the controller returns the valid bounds given, taking aspect ratio 63 * and IME state into account. 64 * 2) When rotating the device: the controller calculates the new bounds in the new orientation, 65 * taking the minimized and IME state into account. In this case, we currently ignore the 66 * SystemUI adjustments (ie. expanded for menu, interaction, etc). 67 * 68 * Other changes in the system, including adjustment of IME, configuration change, and more are 69 * handled by SystemUI (similar to the docked stack divider). 70 */ 71 class PinnedStackController { 72 73 private static final String TAG = TAG_WITH_CLASS_NAME ? "PinnedStackController" : TAG_WM; 74 75 public static final float INVALID_SNAP_FRACTION = -1f; 76 private final WindowManagerService mService; 77 private final DisplayContent mDisplayContent; 78 private final Handler mHandler = UiThread.getHandler(); 79 80 private IPinnedStackListener mPinnedStackListener; 81 private final PinnedStackListenerDeathHandler mPinnedStackListenerDeathHandler = 82 new PinnedStackListenerDeathHandler(); 83 84 private final PinnedStackControllerCallback mCallbacks = new PinnedStackControllerCallback(); 85 private final PipSnapAlgorithm mSnapAlgorithm; 86 87 // States that affect how the PIP can be manipulated 88 private boolean mIsMinimized; 89 private boolean mIsImeShowing; 90 private int mImeHeight; 91 private boolean mIsShelfShowing; 92 private int mShelfHeight; 93 94 // The set of actions and aspect-ratio for the that are currently allowed on the PiP activity 95 private ArrayList<RemoteAction> mActions = new ArrayList<>(); 96 private float mAspectRatio = -1f; 97 98 // Used to calculate stack bounds across rotations 99 private final DisplayInfo mDisplayInfo = new DisplayInfo(); 100 private final Rect mStableInsets = new Rect(); 101 102 // The size and position information that describes where the pinned stack will go by default. 103 private int mDefaultMinSize; 104 private int mDefaultStackGravity; 105 private float mDefaultAspectRatio; 106 private Point mScreenEdgeInsets; 107 private int mCurrentMinSize; 108 private float mReentrySnapFraction = INVALID_SNAP_FRACTION; 109 private WeakReference<AppWindowToken> mLastPipActivity = null; 110 111 // The aspect ratio bounds of the PIP. 112 private float mMinAspectRatio; 113 private float mMaxAspectRatio; 114 115 // Temp vars for calculation 116 private final DisplayMetrics mTmpMetrics = new DisplayMetrics(); 117 private final Rect mTmpInsets = new Rect(); 118 private final Rect mTmpRect = new Rect(); 119 private final Rect mTmpAnimatingBoundsRect = new Rect(); 120 private final Point mTmpDisplaySize = new Point(); 121 122 123 /** 124 * The callback object passed to listeners for them to notify the controller of state changes. 125 */ 126 private class PinnedStackControllerCallback extends IPinnedStackController.Stub { 127 128 @Override setIsMinimized(final boolean isMinimized)129 public void setIsMinimized(final boolean isMinimized) { 130 mHandler.post(() -> { 131 mIsMinimized = isMinimized; 132 mSnapAlgorithm.setMinimized(isMinimized); 133 }); 134 } 135 136 @Override setMinEdgeSize(int minEdgeSize)137 public void setMinEdgeSize(int minEdgeSize) { 138 mHandler.post(() -> { 139 mCurrentMinSize = Math.max(mDefaultMinSize, minEdgeSize); 140 }); 141 } 142 143 @Override getDisplayRotation()144 public int getDisplayRotation() { 145 synchronized (mService.mWindowMap) { 146 return mDisplayInfo.rotation; 147 } 148 } 149 } 150 151 /** 152 * Handler for the case where the listener dies. 153 */ 154 private class PinnedStackListenerDeathHandler implements IBinder.DeathRecipient { 155 156 @Override binderDied()157 public void binderDied() { 158 // Clean up the state if the listener dies 159 if (mPinnedStackListener != null) { 160 mPinnedStackListener.asBinder().unlinkToDeath(mPinnedStackListenerDeathHandler, 0); 161 } 162 mPinnedStackListener = null; 163 } 164 } 165 PinnedStackController(WindowManagerService service, DisplayContent displayContent)166 PinnedStackController(WindowManagerService service, DisplayContent displayContent) { 167 mService = service; 168 mDisplayContent = displayContent; 169 mSnapAlgorithm = new PipSnapAlgorithm(service.mContext); 170 mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo()); 171 reloadResources(); 172 // Initialize the aspect ratio to the default aspect ratio. Don't do this in reload 173 // resources as it would clobber mAspectRatio when entering PiP from fullscreen which 174 // triggers a configuration change and the resources to be reloaded. 175 mAspectRatio = mDefaultAspectRatio; 176 } 177 onConfigurationChanged()178 void onConfigurationChanged() { 179 reloadResources(); 180 } 181 182 /** 183 * Reloads all the resources for the current configuration. 184 */ reloadResources()185 private void reloadResources() { 186 final Resources res = mService.mContext.getResources(); 187 mDefaultMinSize = res.getDimensionPixelSize( 188 com.android.internal.R.dimen.default_minimal_size_pip_resizable_task); 189 mCurrentMinSize = mDefaultMinSize; 190 mDefaultAspectRatio = res.getFloat( 191 com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio); 192 final String screenEdgeInsetsDpString = res.getString( 193 com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets); 194 final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty() 195 ? Size.parseSize(screenEdgeInsetsDpString) 196 : null; 197 mDefaultStackGravity = res.getInteger( 198 com.android.internal.R.integer.config_defaultPictureInPictureGravity); 199 mDisplayContent.getDisplay().getRealMetrics(mTmpMetrics); 200 mScreenEdgeInsets = screenEdgeInsetsDp == null ? new Point() 201 : new Point(dpToPx(screenEdgeInsetsDp.getWidth(), mTmpMetrics), 202 dpToPx(screenEdgeInsetsDp.getHeight(), mTmpMetrics)); 203 mMinAspectRatio = res.getFloat( 204 com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio); 205 mMaxAspectRatio = res.getFloat( 206 com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio); 207 } 208 209 /** 210 * Registers a pinned stack listener. 211 */ registerPinnedStackListener(IPinnedStackListener listener)212 void registerPinnedStackListener(IPinnedStackListener listener) { 213 try { 214 listener.asBinder().linkToDeath(mPinnedStackListenerDeathHandler, 0); 215 listener.onListenerRegistered(mCallbacks); 216 mPinnedStackListener = listener; 217 notifyImeVisibilityChanged(mIsImeShowing, mImeHeight); 218 notifyShelfVisibilityChanged(mIsShelfShowing, mShelfHeight); 219 // The movement bounds notification needs to be sent before the minimized state, since 220 // SystemUI may use the bounds to retore the minimized position 221 notifyMovementBoundsChanged(false /* fromImeAdjustment */, 222 false /* fromShelfAdjustment */); 223 notifyActionsChanged(mActions); 224 notifyMinimizeChanged(mIsMinimized); 225 } catch (RemoteException e) { 226 Log.e(TAG, "Failed to register pinned stack listener", e); 227 } 228 } 229 230 /** 231 * @return whether the given {@param aspectRatio} is valid. 232 */ isValidPictureInPictureAspectRatio(float aspectRatio)233 public boolean isValidPictureInPictureAspectRatio(float aspectRatio) { 234 return Float.compare(mMinAspectRatio, aspectRatio) <= 0 && 235 Float.compare(aspectRatio, mMaxAspectRatio) <= 0; 236 } 237 238 /** 239 * Returns the current bounds (or the default bounds if there are no current bounds) with the 240 * specified aspect ratio. 241 */ transformBoundsToAspectRatio(Rect stackBounds, float aspectRatio, boolean useCurrentMinEdgeSize)242 Rect transformBoundsToAspectRatio(Rect stackBounds, float aspectRatio, 243 boolean useCurrentMinEdgeSize) { 244 // Save the snap fraction, calculate the aspect ratio based on screen size 245 final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds, 246 getMovementBounds(stackBounds)); 247 248 final int minEdgeSize = useCurrentMinEdgeSize ? mCurrentMinSize : mDefaultMinSize; 249 final Size size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, minEdgeSize, 250 mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); 251 final int left = (int) (stackBounds.centerX() - size.getWidth() / 2f); 252 final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f); 253 stackBounds.set(left, top, left + size.getWidth(), top + size.getHeight()); 254 mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction); 255 if (mIsMinimized) { 256 applyMinimizedOffset(stackBounds, getMovementBounds(stackBounds)); 257 } 258 return stackBounds; 259 } 260 261 /** 262 * Saves the current snap fraction for re-entry of the current activity into PiP. 263 */ saveReentrySnapFraction(final AppWindowToken token, final Rect stackBounds)264 void saveReentrySnapFraction(final AppWindowToken token, final Rect stackBounds) { 265 mReentrySnapFraction = getSnapFraction(stackBounds); 266 mLastPipActivity = new WeakReference<>(token); 267 } 268 269 /** 270 * Resets the last saved snap fraction so that the default bounds will be returned. 271 */ resetReentrySnapFraction(AppWindowToken token)272 void resetReentrySnapFraction(AppWindowToken token) { 273 if (mLastPipActivity != null && mLastPipActivity.get() == token) { 274 mReentrySnapFraction = INVALID_SNAP_FRACTION; 275 mLastPipActivity = null; 276 } 277 } 278 279 /** 280 * @return the default bounds to show the PIP when there is no active PIP. 281 */ getDefaultOrLastSavedBounds()282 Rect getDefaultOrLastSavedBounds() { 283 return getDefaultBounds(mReentrySnapFraction); 284 } 285 286 /** 287 * @return the default bounds to show the PIP, if a {@param snapFraction} is provided, then it 288 * will apply the default bounds to the provided snap fraction. 289 */ getDefaultBounds(float snapFraction)290 Rect getDefaultBounds(float snapFraction) { 291 synchronized (mService.mWindowMap) { 292 final Rect insetBounds = new Rect(); 293 getInsetBounds(insetBounds); 294 295 final Rect defaultBounds = new Rect(); 296 final Size size = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio, 297 mDefaultMinSize, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); 298 if (snapFraction != INVALID_SNAP_FRACTION) { 299 defaultBounds.set(0, 0, size.getWidth(), size.getHeight()); 300 final Rect movementBounds = getMovementBounds(defaultBounds); 301 mSnapAlgorithm.applySnapFraction(defaultBounds, movementBounds, snapFraction); 302 } else { 303 Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds, 304 0, Math.max(mIsImeShowing ? mImeHeight : 0, 305 mIsShelfShowing ? mShelfHeight : 0), 306 defaultBounds); 307 } 308 return defaultBounds; 309 } 310 } 311 312 /** 313 * In the case where the display rotation is changed but there is no stack, we can't depend on 314 * onTaskStackBoundsChanged() to be called. But we still should update our known display info 315 * with the new state so that we can update SystemUI. 316 */ onDisplayInfoChanged()317 synchronized void onDisplayInfoChanged() { 318 mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo()); 319 notifyMovementBoundsChanged(false /* fromImeAdjustment */, false /* fromShelfAdjustment */); 320 } 321 322 /** 323 * Updates the display info, calculating and returning the new stack and movement bounds in the 324 * new orientation of the device if necessary. 325 */ onTaskStackBoundsChanged(Rect targetBounds, Rect outBounds)326 boolean onTaskStackBoundsChanged(Rect targetBounds, Rect outBounds) { 327 synchronized (mService.mWindowMap) { 328 final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo(); 329 if (mDisplayInfo.equals(displayInfo)) { 330 // We are already in the right orientation, ignore 331 outBounds.setEmpty(); 332 return false; 333 } else if (targetBounds.isEmpty()) { 334 // The stack is null, we are just initializing the stack, so just store the display 335 // info and ignore 336 mDisplayInfo.copyFrom(displayInfo); 337 outBounds.setEmpty(); 338 return false; 339 } 340 341 mTmpRect.set(targetBounds); 342 final Rect postChangeStackBounds = mTmpRect; 343 344 // Calculate the snap fraction of the current stack along the old movement bounds 345 final float snapFraction = getSnapFraction(postChangeStackBounds); 346 mDisplayInfo.copyFrom(displayInfo); 347 348 // Calculate the stack bounds in the new orientation to the same same fraction along the 349 // rotated movement bounds. 350 final Rect postChangeMovementBounds = getMovementBounds(postChangeStackBounds, 351 false /* adjustForIme */, false /* adjustForShelf */); 352 mSnapAlgorithm.applySnapFraction(postChangeStackBounds, postChangeMovementBounds, 353 snapFraction); 354 if (mIsMinimized) { 355 applyMinimizedOffset(postChangeStackBounds, postChangeMovementBounds); 356 } 357 358 notifyMovementBoundsChanged(false /* fromImeAdjustment */, 359 false /* fromShelfAdjustment */); 360 361 outBounds.set(postChangeStackBounds); 362 return true; 363 } 364 } 365 366 /** 367 * Sets the Ime state and height. 368 */ setAdjustedForIme(boolean adjustedForIme, int imeHeight)369 void setAdjustedForIme(boolean adjustedForIme, int imeHeight) { 370 // Due to the order of callbacks from the system, we may receive an ime height even when 371 // {@param adjustedForIme} is false, and also a zero height when {@param adjustedForIme} 372 // is true. Instead, ensure that the ime state changes with the height and if the ime is 373 // showing, then the height is non-zero. 374 final boolean imeShowing = adjustedForIme && imeHeight > 0; 375 imeHeight = imeShowing ? imeHeight : 0; 376 if (imeShowing == mIsImeShowing && imeHeight == mImeHeight) { 377 return; 378 } 379 380 mIsImeShowing = imeShowing; 381 mImeHeight = imeHeight; 382 notifyImeVisibilityChanged(imeShowing, imeHeight); 383 notifyMovementBoundsChanged(true /* fromImeAdjustment */, false /* fromShelfAdjustment */); 384 } 385 386 /** 387 * Sets the shelf state and height. 388 */ setAdjustedForShelf(boolean adjustedForShelf, int shelfHeight)389 void setAdjustedForShelf(boolean adjustedForShelf, int shelfHeight) { 390 final boolean shelfShowing = adjustedForShelf && shelfHeight > 0; 391 if (shelfShowing == mIsShelfShowing && shelfHeight == mShelfHeight) { 392 return; 393 } 394 395 mIsShelfShowing = shelfShowing; 396 mShelfHeight = shelfHeight; 397 notifyShelfVisibilityChanged(shelfShowing, shelfHeight); 398 notifyMovementBoundsChanged(false /* fromImeAdjustment */, true /* fromShelfAdjustment */); 399 } 400 401 /** 402 * Sets the current aspect ratio. 403 */ setAspectRatio(float aspectRatio)404 void setAspectRatio(float aspectRatio) { 405 if (Float.compare(mAspectRatio, aspectRatio) != 0) { 406 mAspectRatio = aspectRatio; 407 notifyMovementBoundsChanged(false /* fromImeAdjustment */, 408 false /* fromShelfAdjustment */); 409 } 410 } 411 412 /** 413 * @return the current aspect ratio. 414 */ getAspectRatio()415 float getAspectRatio() { 416 return mAspectRatio; 417 } 418 419 /** 420 * Sets the current set of actions. 421 */ setActions(List<RemoteAction> actions)422 void setActions(List<RemoteAction> actions) { 423 mActions.clear(); 424 if (actions != null) { 425 mActions.addAll(actions); 426 } 427 notifyActionsChanged(mActions); 428 } 429 430 /** 431 * Notifies listeners that the PIP needs to be adjusted for the IME. 432 */ notifyImeVisibilityChanged(boolean imeVisible, int imeHeight)433 private void notifyImeVisibilityChanged(boolean imeVisible, int imeHeight) { 434 if (mPinnedStackListener != null) { 435 try { 436 mPinnedStackListener.onImeVisibilityChanged(imeVisible, imeHeight); 437 } catch (RemoteException e) { 438 Slog.e(TAG_WM, "Error delivering bounds changed event.", e); 439 } 440 } 441 } 442 notifyShelfVisibilityChanged(boolean shelfVisible, int shelfHeight)443 private void notifyShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) { 444 if (mPinnedStackListener != null) { 445 try { 446 mPinnedStackListener.onShelfVisibilityChanged(shelfVisible, shelfHeight); 447 } catch (RemoteException e) { 448 Slog.e(TAG_WM, "Error delivering bounds changed event.", e); 449 } 450 } 451 } 452 453 /** 454 * Notifies listeners that the PIP minimized state has changed. 455 */ notifyMinimizeChanged(boolean isMinimized)456 private void notifyMinimizeChanged(boolean isMinimized) { 457 if (mPinnedStackListener != null) { 458 try { 459 mPinnedStackListener.onMinimizedStateChanged(isMinimized); 460 } catch (RemoteException e) { 461 Slog.e(TAG_WM, "Error delivering minimize changed event.", e); 462 } 463 } 464 } 465 466 /** 467 * Notifies listeners that the PIP actions have changed. 468 */ notifyActionsChanged(List<RemoteAction> actions)469 private void notifyActionsChanged(List<RemoteAction> actions) { 470 if (mPinnedStackListener != null) { 471 try { 472 mPinnedStackListener.onActionsChanged(new ParceledListSlice(actions)); 473 } catch (RemoteException e) { 474 Slog.e(TAG_WM, "Error delivering actions changed event.", e); 475 } 476 } 477 } 478 479 /** 480 * Notifies listeners that the PIP movement bounds have changed. 481 */ notifyMovementBoundsChanged(boolean fromImeAdjustment, boolean fromShelfAdjustment)482 private void notifyMovementBoundsChanged(boolean fromImeAdjustment, 483 boolean fromShelfAdjustment) { 484 synchronized (mService.mWindowMap) { 485 if (mPinnedStackListener == null) { 486 return; 487 } 488 try { 489 final Rect insetBounds = new Rect(); 490 getInsetBounds(insetBounds); 491 final Rect normalBounds = getDefaultBounds(INVALID_SNAP_FRACTION); 492 if (isValidPictureInPictureAspectRatio(mAspectRatio)) { 493 transformBoundsToAspectRatio(normalBounds, mAspectRatio, 494 false /* useCurrentMinEdgeSize */); 495 } 496 final Rect animatingBounds = mTmpAnimatingBoundsRect; 497 final TaskStack pinnedStack = mDisplayContent.getPinnedStack(); 498 if (pinnedStack != null) { 499 pinnedStack.getAnimationOrCurrentBounds(animatingBounds); 500 } else { 501 animatingBounds.set(normalBounds); 502 } 503 mPinnedStackListener.onMovementBoundsChanged(insetBounds, normalBounds, 504 animatingBounds, fromImeAdjustment, fromShelfAdjustment, 505 mDisplayInfo.rotation); 506 } catch (RemoteException e) { 507 Slog.e(TAG_WM, "Error delivering actions changed event.", e); 508 } 509 } 510 } 511 512 /** 513 * @return the bounds on the screen that the PIP can be visible in. 514 */ getInsetBounds(Rect outRect)515 private void getInsetBounds(Rect outRect) { 516 synchronized (mService.mWindowMap) { 517 mService.mPolicy.getStableInsetsLw(mDisplayInfo.rotation, mDisplayInfo.logicalWidth, 518 mDisplayInfo.logicalHeight, mDisplayInfo.displayCutout, mTmpInsets); 519 outRect.set(mTmpInsets.left + mScreenEdgeInsets.x, mTmpInsets.top + mScreenEdgeInsets.y, 520 mDisplayInfo.logicalWidth - mTmpInsets.right - mScreenEdgeInsets.x, 521 mDisplayInfo.logicalHeight - mTmpInsets.bottom - mScreenEdgeInsets.y); 522 } 523 } 524 525 /** 526 * @return the movement bounds for the given {@param stackBounds} and the current state of the 527 * controller. 528 */ getMovementBounds(Rect stackBounds)529 private Rect getMovementBounds(Rect stackBounds) { 530 synchronized (mService.mWindowMap) { 531 return getMovementBounds(stackBounds, true /* adjustForIme */, 532 true /* adjustForShelf */); 533 } 534 } 535 536 /** 537 * @return the movement bounds for the given {@param stackBounds} and the current state of the 538 * controller. 539 */ getMovementBounds(Rect stackBounds, boolean adjustForIme, boolean adjustForShelf)540 private Rect getMovementBounds(Rect stackBounds, boolean adjustForIme, boolean adjustForShelf) { 541 synchronized (mService.mWindowMap) { 542 final Rect movementBounds = new Rect(); 543 getInsetBounds(movementBounds); 544 545 // Apply the movement bounds adjustments based on the current state 546 mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds, 547 Math.max((adjustForIme && mIsImeShowing) ? mImeHeight : 0, 548 (adjustForShelf && mIsShelfShowing) ? mShelfHeight : 0)); 549 return movementBounds; 550 } 551 } 552 553 /** 554 * Applies the minimized offsets to the given stack bounds. 555 */ applyMinimizedOffset(Rect stackBounds, Rect movementBounds)556 private void applyMinimizedOffset(Rect stackBounds, Rect movementBounds) { 557 synchronized (mService.mWindowMap) { 558 mTmpDisplaySize.set(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); 559 mService.getStableInsetsLocked(mDisplayContent.getDisplayId(), mStableInsets); 560 mSnapAlgorithm.applyMinimizedOffset(stackBounds, movementBounds, mTmpDisplaySize, 561 mStableInsets); 562 } 563 } 564 565 /** 566 * @return the default snap fraction to apply instead of the default gravity when calculating 567 * the default stack bounds when first entering PiP. 568 */ getSnapFraction(Rect stackBounds)569 private float getSnapFraction(Rect stackBounds) { 570 return mSnapAlgorithm.getSnapFraction(stackBounds, getMovementBounds(stackBounds)); 571 } 572 573 /** 574 * @return the pixels for a given dp value. 575 */ dpToPx(float dpValue, DisplayMetrics dm)576 private int dpToPx(float dpValue, DisplayMetrics dm) { 577 return (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, dpValue, dm); 578 } 579 dump(String prefix, PrintWriter pw)580 void dump(String prefix, PrintWriter pw) { 581 pw.println(prefix + "PinnedStackController"); 582 pw.print(prefix + " defaultBounds="); 583 getDefaultBounds(INVALID_SNAP_FRACTION).printShortString(pw); 584 pw.println(); 585 mService.getStackBounds(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mTmpRect); 586 pw.print(prefix + " movementBounds="); getMovementBounds(mTmpRect).printShortString(pw); 587 pw.println(); 588 pw.println(prefix + " mIsImeShowing=" + mIsImeShowing); 589 pw.println(prefix + " mImeHeight=" + mImeHeight); 590 pw.println(prefix + " mIsShelfShowing=" + mIsShelfShowing); 591 pw.println(prefix + " mShelfHeight=" + mShelfHeight); 592 pw.println(prefix + " mReentrySnapFraction=" + mReentrySnapFraction); 593 pw.println(prefix + " mIsMinimized=" + mIsMinimized); 594 if (mActions.isEmpty()) { 595 pw.println(prefix + " mActions=[]"); 596 } else { 597 pw.println(prefix + " mActions=["); 598 for (int i = 0; i < mActions.size(); i++) { 599 RemoteAction action = mActions.get(i); 600 pw.print(prefix + " Action[" + i + "]: "); 601 action.dump("", pw); 602 } 603 pw.println(prefix + " ]"); 604 } 605 pw.println(prefix + " mDisplayInfo=" + mDisplayInfo); 606 } 607 writeToProto(ProtoOutputStream proto, long fieldId)608 void writeToProto(ProtoOutputStream proto, long fieldId) { 609 final long token = proto.start(fieldId); 610 getDefaultBounds(INVALID_SNAP_FRACTION).writeToProto(proto, DEFAULT_BOUNDS); 611 mService.getStackBounds(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, mTmpRect); 612 getMovementBounds(mTmpRect).writeToProto(proto, MOVEMENT_BOUNDS); 613 proto.end(token); 614 } 615 } 616