1 /* 2 * Copyright (C) 2023 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.wm.shell.common.pip; 18 19 import android.annotation.IntDef; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.app.ActivityTaskManager; 23 import android.app.PictureInPictureParams; 24 import android.app.PictureInPictureUiState; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.pm.ActivityInfo; 28 import android.graphics.Point; 29 import android.graphics.Rect; 30 import android.os.RemoteException; 31 import android.util.ArraySet; 32 import android.util.Size; 33 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.internal.protolog.common.ProtoLog; 36 import com.android.internal.util.function.TriConsumer; 37 import com.android.wm.shell.R; 38 import com.android.wm.shell.common.DisplayLayout; 39 import com.android.wm.shell.protolog.ShellProtoLogGroup; 40 41 import java.io.PrintWriter; 42 import java.lang.annotation.Retention; 43 import java.lang.annotation.RetentionPolicy; 44 import java.util.ArrayList; 45 import java.util.HashMap; 46 import java.util.List; 47 import java.util.Map; 48 import java.util.Objects; 49 import java.util.Set; 50 import java.util.function.Consumer; 51 52 /** 53 * Singleton source of truth for the current state of PIP bounds. 54 */ 55 public class PipBoundsState { 56 public static final int STASH_TYPE_NONE = 0; 57 public static final int STASH_TYPE_LEFT = 1; 58 public static final int STASH_TYPE_RIGHT = 2; 59 public static final int STASH_TYPE_BOTTOM = 3; 60 public static final int STASH_TYPE_TOP = 4; 61 62 @IntDef(prefix = { "STASH_TYPE_" }, value = { 63 STASH_TYPE_NONE, 64 STASH_TYPE_LEFT, 65 STASH_TYPE_RIGHT, 66 STASH_TYPE_BOTTOM, 67 STASH_TYPE_TOP 68 }) 69 @Retention(RetentionPolicy.SOURCE) 70 public @interface StashType {} 71 72 private static final String TAG = PipBoundsState.class.getSimpleName(); 73 74 private final @NonNull Rect mBounds = new Rect(); 75 private final @NonNull Rect mMovementBounds = new Rect(); 76 private final @NonNull Rect mNormalBounds = new Rect(); 77 private final @NonNull Rect mExpandedBounds = new Rect(); 78 private final @NonNull Rect mNormalMovementBounds = new Rect(); 79 private final @NonNull Rect mExpandedMovementBounds = new Rect(); 80 private final @NonNull PipDisplayLayoutState mPipDisplayLayoutState; 81 private final Point mMaxSize = new Point(); 82 private final Point mMinSize = new Point(); 83 private final @NonNull Context mContext; 84 private float mAspectRatio; 85 private int mStashedState = STASH_TYPE_NONE; 86 private int mStashOffset; 87 private @Nullable PipReentryState mPipReentryState; 88 private final LauncherState mLauncherState = new LauncherState(); 89 private final @NonNull SizeSpecSource mSizeSpecSource; 90 private @Nullable ComponentName mLastPipComponentName; 91 private final @NonNull MotionBoundsState mMotionBoundsState = new MotionBoundsState(); 92 private boolean mIsImeShowing; 93 private int mImeHeight; 94 private boolean mIsShelfShowing; 95 private int mShelfHeight; 96 /** Whether the user has resized the PIP manually. */ 97 private boolean mHasUserResizedPip; 98 /** Whether the user has moved the PIP manually. */ 99 private boolean mHasUserMovedPip; 100 /** 101 * Areas defined by currently visible apps that they prefer to keep clear from overlays such as 102 * the PiP. Restricted areas may only move the PiP a limited amount from its anchor position. 103 * The system will try to respect these areas, but when not possible will ignore them. 104 * 105 * @see android.view.View#setPreferKeepClearRects 106 */ 107 private final Set<Rect> mRestrictedKeepClearAreas = new ArraySet<>(); 108 /** 109 * Areas defined by currently visible apps holding 110 * {@link android.Manifest.permission#SET_UNRESTRICTED_KEEP_CLEAR_AREAS} that they prefer to 111 * keep clear from overlays such as the PiP. 112 * Unrestricted areas can move the PiP farther than restricted areas, and the system will try 113 * harder to respect these areas. 114 * 115 * @see android.view.View#setPreferKeepClearRects 116 */ 117 private final Set<Rect> mUnrestrictedKeepClearAreas = new ArraySet<>(); 118 /** 119 * Additional to {@link #mUnrestrictedKeepClearAreas}, allow the caller to append named bounds 120 * as unrestricted keep clear area. Values in this map would be appended to 121 * {@link #getUnrestrictedKeepClearAreas()} and this is meant for internal usage only. 122 */ 123 private final Map<String, Rect> mNamedUnrestrictedKeepClearAreas = new HashMap<>(); 124 125 private @Nullable Runnable mOnMinimalSizeChangeCallback; 126 private @Nullable TriConsumer<Boolean, Integer, Boolean> mOnShelfVisibilityChangeCallback; 127 private List<Consumer<Rect>> mOnPipExclusionBoundsChangeCallbacks = new ArrayList<>(); 128 private List<Consumer<Float>> mOnAspectRatioChangedCallbacks = new ArrayList<>(); 129 130 // the size of the current bounds relative to the max size spec 131 private float mBoundsScale; 132 PipBoundsState(@onNull Context context, @NonNull SizeSpecSource sizeSpecSource, @NonNull PipDisplayLayoutState pipDisplayLayoutState)133 public PipBoundsState(@NonNull Context context, @NonNull SizeSpecSource sizeSpecSource, 134 @NonNull PipDisplayLayoutState pipDisplayLayoutState) { 135 mContext = context; 136 reloadResources(); 137 mSizeSpecSource = sizeSpecSource; 138 mPipDisplayLayoutState = pipDisplayLayoutState; 139 140 // Update the relative proportion of the bounds compared to max possible size. Max size 141 // spec takes the aspect ratio of the bounds into account, so both width and height 142 // scale by the same factor. 143 addPipExclusionBoundsChangeCallback((bounds) -> { 144 updateBoundsScale(); 145 }); 146 } 147 148 /** Reloads the resources. */ onConfigurationChanged()149 public void onConfigurationChanged() { 150 reloadResources(); 151 152 // update the size spec resources upon config change too 153 mSizeSpecSource.onConfigurationChanged(); 154 } 155 156 /** Update the bounds scale percentage value. */ updateBoundsScale()157 public void updateBoundsScale() { 158 mBoundsScale = Math.min((float) mBounds.width() / mMaxSize.x, 1.0f); 159 } 160 reloadResources()161 private void reloadResources() { 162 mStashOffset = mContext.getResources().getDimensionPixelSize(R.dimen.pip_stash_offset); 163 } 164 165 /** Set the current PIP bounds. */ setBounds(@onNull Rect bounds)166 public void setBounds(@NonNull Rect bounds) { 167 mBounds.set(bounds); 168 for (Consumer<Rect> callback : mOnPipExclusionBoundsChangeCallbacks) { 169 callback.accept(bounds); 170 } 171 } 172 173 /** Get the current PIP bounds. */ 174 @NonNull getBounds()175 public Rect getBounds() { 176 return new Rect(mBounds); 177 } 178 179 /** 180 * Get the scale of the current bounds relative to the maximum size possible. 181 * 182 * @return 1.0 if {@link PipBoundsState#getBounds()} equals {@link PipBoundsState#getMaxSize()}. 183 */ getBoundsScale()184 public float getBoundsScale() { 185 return mBoundsScale; 186 } 187 188 /** Returns the current movement bounds. */ 189 @NonNull getMovementBounds()190 public Rect getMovementBounds() { 191 return mMovementBounds; 192 } 193 194 /** Set the current normal PIP bounds. */ setNormalBounds(@onNull Rect bounds)195 public void setNormalBounds(@NonNull Rect bounds) { 196 mNormalBounds.set(bounds); 197 } 198 199 /** Get the current normal PIP bounds. */ 200 @NonNull getNormalBounds()201 public Rect getNormalBounds() { 202 return mNormalBounds; 203 } 204 205 /** Set the expanded bounds of PIP. */ setExpandedBounds(@onNull Rect bounds)206 public void setExpandedBounds(@NonNull Rect bounds) { 207 mExpandedBounds.set(bounds); 208 } 209 210 /** Get the PIP expanded bounds. */ 211 @NonNull getExpandedBounds()212 public Rect getExpandedBounds() { 213 return mExpandedBounds; 214 } 215 216 /** Set the normal movement bounds. */ setNormalMovementBounds(@onNull Rect bounds)217 public void setNormalMovementBounds(@NonNull Rect bounds) { 218 mNormalMovementBounds.set(bounds); 219 } 220 221 /** Returns the normal movement bounds. */ 222 @NonNull getNormalMovementBounds()223 public Rect getNormalMovementBounds() { 224 return mNormalMovementBounds; 225 } 226 227 /** Set the expanded movement bounds. */ setExpandedMovementBounds(@onNull Rect bounds)228 public void setExpandedMovementBounds(@NonNull Rect bounds) { 229 mExpandedMovementBounds.set(bounds); 230 } 231 232 /** Updates the min and max sizes based on the size spec and aspect ratio. */ updateMinMaxSize(float aspectRatio)233 public void updateMinMaxSize(float aspectRatio) { 234 final Size minSize = mSizeSpecSource.getMinSize(aspectRatio); 235 mMinSize.set(minSize.getWidth(), minSize.getHeight()); 236 final Size maxSize = mSizeSpecSource.getMaxSize(aspectRatio); 237 mMaxSize.set(maxSize.getWidth(), maxSize.getHeight()); 238 } 239 240 /** Sets the max possible size for resize. */ setMaxSize(int width, int height)241 public void setMaxSize(int width, int height) { 242 mMaxSize.set(width, height); 243 } 244 245 /** Sets the min possible size for resize. */ setMinSize(int width, int height)246 public void setMinSize(int width, int height) { 247 mMinSize.set(width, height); 248 } 249 getMaxSize()250 public Point getMaxSize() { 251 return mMaxSize; 252 } 253 getMinSize()254 public Point getMinSize() { 255 return mMinSize; 256 } 257 258 /** Returns the expanded movement bounds. */ 259 @NonNull getExpandedMovementBounds()260 public Rect getExpandedMovementBounds() { 261 return mExpandedMovementBounds; 262 } 263 264 /** Dictate where PiP currently should be stashed, if at all. */ setStashed(@tashType int stashedState)265 public void setStashed(@StashType int stashedState) { 266 if (mStashedState == stashedState) { 267 return; 268 } 269 270 mStashedState = stashedState; 271 try { 272 ActivityTaskManager.getService().onPictureInPictureUiStateChanged( 273 new PictureInPictureUiState(stashedState != STASH_TYPE_NONE /* isStashed */) 274 ); 275 } catch (RemoteException | IllegalStateException e) { 276 ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, 277 "%s: Unable to set alert PiP state change.", TAG); 278 } 279 } 280 281 /** 282 * Return where the PiP is stashed, if at all. 283 * @return {@code STASH_NONE}, {@code STASH_LEFT} or {@code STASH_RIGHT}. 284 */ getStashedState()285 public @StashType int getStashedState() { 286 return mStashedState; 287 } 288 289 /** Whether PiP is stashed or not. */ isStashed()290 public boolean isStashed() { 291 return mStashedState != STASH_TYPE_NONE; 292 } 293 294 /** Returns the offset from the edge of the screen for PiP stash. */ getStashOffset()295 public int getStashOffset() { 296 return mStashOffset; 297 } 298 299 /** Set the PIP aspect ratio. */ setAspectRatio(float aspectRatio)300 public void setAspectRatio(float aspectRatio) { 301 if (Float.compare(mAspectRatio, aspectRatio) != 0) { 302 mAspectRatio = aspectRatio; 303 for (Consumer<Float> callback : mOnAspectRatioChangedCallbacks) { 304 callback.accept(mAspectRatio); 305 } 306 } 307 } 308 309 /** Get the PIP aspect ratio. */ getAspectRatio()310 public float getAspectRatio() { 311 return mAspectRatio; 312 } 313 314 /** Save the reentry state to restore to when re-entering PIP mode. */ saveReentryState(float fraction)315 public void saveReentryState(float fraction) { 316 mPipReentryState = new PipReentryState(mBoundsScale, fraction); 317 } 318 319 /** Returns the saved reentry state. */ 320 @Nullable getReentryState()321 public PipReentryState getReentryState() { 322 return mPipReentryState; 323 } 324 325 /** Set the last {@link ComponentName} to enter PIP mode. */ setLastPipComponentName(@ullable ComponentName lastPipComponentName)326 public void setLastPipComponentName(@Nullable ComponentName lastPipComponentName) { 327 final boolean changed = !Objects.equals(mLastPipComponentName, lastPipComponentName); 328 mLastPipComponentName = lastPipComponentName; 329 if (changed) { 330 clearReentryState(); 331 setHasUserResizedPip(false); 332 setHasUserMovedPip(false); 333 } 334 } 335 336 /** Get the last PIP component name, if any. */ 337 @Nullable getLastPipComponentName()338 public ComponentName getLastPipComponentName() { 339 return mLastPipComponentName; 340 } 341 342 /** Returns the display's bounds. */ 343 @NonNull getDisplayBounds()344 public Rect getDisplayBounds() { 345 return mPipDisplayLayoutState.getDisplayBounds(); 346 } 347 348 /** Get a copy of the display layout. */ 349 @NonNull getDisplayLayout()350 public DisplayLayout getDisplayLayout() { 351 return mPipDisplayLayoutState.getDisplayLayout(); 352 } 353 354 /** 355 * Clears the PiP re-entry state. 356 */ 357 @VisibleForTesting clearReentryState()358 public void clearReentryState() { 359 mPipReentryState = null; 360 } 361 362 /** Sets the preferred size of PIP as specified by the activity in PIP mode. */ setOverrideMinSize(@ullable Size overrideMinSize)363 public void setOverrideMinSize(@Nullable Size overrideMinSize) { 364 final boolean changed = !Objects.equals(overrideMinSize, getOverrideMinSize()); 365 mSizeSpecSource.setOverrideMinSize(overrideMinSize); 366 if (changed && mOnMinimalSizeChangeCallback != null) { 367 mOnMinimalSizeChangeCallback.run(); 368 } 369 } 370 371 /** Returns the preferred minimal size specified by the activity in PIP. */ 372 @Nullable getOverrideMinSize()373 public Size getOverrideMinSize() { 374 return mSizeSpecSource.getOverrideMinSize(); 375 } 376 377 /** Returns the minimum edge size of the override minimum size, or 0 if not set. */ getOverrideMinEdgeSize()378 public int getOverrideMinEdgeSize() { 379 return mSizeSpecSource.getOverrideMinEdgeSize(); 380 } 381 382 /** Get the state of the bounds in motion. */ 383 @NonNull getMotionBoundsState()384 public MotionBoundsState getMotionBoundsState() { 385 return mMotionBoundsState; 386 } 387 388 /** Set whether the IME is currently showing and its height. */ setImeVisibility(boolean imeShowing, int imeHeight)389 public void setImeVisibility(boolean imeShowing, int imeHeight) { 390 mIsImeShowing = imeShowing; 391 mImeHeight = imeHeight; 392 } 393 394 /** Returns whether the IME is currently showing. */ isImeShowing()395 public boolean isImeShowing() { 396 return mIsImeShowing; 397 } 398 399 /** Returns the IME height. */ getImeHeight()400 public int getImeHeight() { 401 return mImeHeight; 402 } 403 404 /** Set whether the shelf is showing and its height. */ setShelfVisibility(boolean showing, int height)405 public void setShelfVisibility(boolean showing, int height) { 406 setShelfVisibility(showing, height, true); 407 } 408 409 /** Set whether the shelf is showing and its height. */ setShelfVisibility(boolean showing, int height, boolean updateMovementBounds)410 public void setShelfVisibility(boolean showing, int height, boolean updateMovementBounds) { 411 final boolean shelfShowing = showing && height > 0; 412 if (shelfShowing == mIsShelfShowing && height == mShelfHeight) { 413 return; 414 } 415 416 mIsShelfShowing = showing; 417 mShelfHeight = height; 418 if (mOnShelfVisibilityChangeCallback != null) { 419 mOnShelfVisibilityChangeCallback.accept(mIsShelfShowing, mShelfHeight, 420 updateMovementBounds); 421 } 422 } 423 424 /** Set the keep clear areas onscreen. The PiP should ideally not cover them. */ setKeepClearAreas(@onNull Set<Rect> restrictedAreas, @NonNull Set<Rect> unrestrictedAreas)425 public void setKeepClearAreas(@NonNull Set<Rect> restrictedAreas, 426 @NonNull Set<Rect> unrestrictedAreas) { 427 mRestrictedKeepClearAreas.clear(); 428 mRestrictedKeepClearAreas.addAll(restrictedAreas); 429 mUnrestrictedKeepClearAreas.clear(); 430 mUnrestrictedKeepClearAreas.addAll(unrestrictedAreas); 431 } 432 433 /** Add a named unrestricted keep clear area. */ addNamedUnrestrictedKeepClearArea(@onNull String name, Rect unrestrictedArea)434 public void addNamedUnrestrictedKeepClearArea(@NonNull String name, Rect unrestrictedArea) { 435 mNamedUnrestrictedKeepClearAreas.put(name, unrestrictedArea); 436 } 437 438 /** Remove a named unrestricted keep clear area. */ removeNamedUnrestrictedKeepClearArea(@onNull String name)439 public void removeNamedUnrestrictedKeepClearArea(@NonNull String name) { 440 mNamedUnrestrictedKeepClearAreas.remove(name); 441 } 442 443 444 /** 445 * @return restricted keep clear areas. 446 */ 447 @NonNull getRestrictedKeepClearAreas()448 public Set<Rect> getRestrictedKeepClearAreas() { 449 return mRestrictedKeepClearAreas; 450 } 451 452 /** 453 * @return unrestricted keep clear areas. 454 */ 455 @NonNull getUnrestrictedKeepClearAreas()456 public Set<Rect> getUnrestrictedKeepClearAreas() { 457 if (mNamedUnrestrictedKeepClearAreas.isEmpty()) return mUnrestrictedKeepClearAreas; 458 final Set<Rect> unrestrictedAreas = new ArraySet<>(mUnrestrictedKeepClearAreas); 459 unrestrictedAreas.addAll(mNamedUnrestrictedKeepClearAreas.values()); 460 return unrestrictedAreas; 461 } 462 463 /** 464 * Initialize states when first entering PiP. 465 */ setBoundsStateForEntry(ComponentName componentName, ActivityInfo activityInfo, PictureInPictureParams params, PipBoundsAlgorithm pipBoundsAlgorithm)466 public void setBoundsStateForEntry(ComponentName componentName, ActivityInfo activityInfo, 467 PictureInPictureParams params, PipBoundsAlgorithm pipBoundsAlgorithm) { 468 setLastPipComponentName(componentName); 469 setAspectRatio(pipBoundsAlgorithm.getAspectRatioOrDefault(params)); 470 setOverrideMinSize(pipBoundsAlgorithm.getMinimalSize(activityInfo)); 471 } 472 473 /** Returns whether the shelf is currently showing. */ isShelfShowing()474 public boolean isShelfShowing() { 475 return mIsShelfShowing; 476 } 477 478 /** Returns the shelf height. */ getShelfHeight()479 public int getShelfHeight() { 480 return mShelfHeight; 481 } 482 483 /** Returns whether the user has resized the PIP. */ hasUserResizedPip()484 public boolean hasUserResizedPip() { 485 return mHasUserResizedPip; 486 } 487 488 /** Set whether the user has resized the PIP. */ setHasUserResizedPip(boolean hasUserResizedPip)489 public void setHasUserResizedPip(boolean hasUserResizedPip) { 490 mHasUserResizedPip = hasUserResizedPip; 491 } 492 493 /** Returns whether the user has moved the PIP. */ hasUserMovedPip()494 public boolean hasUserMovedPip() { 495 return mHasUserMovedPip; 496 } 497 498 /** Set whether the user has moved the PIP. */ setHasUserMovedPip(boolean hasUserMovedPip)499 public void setHasUserMovedPip(boolean hasUserMovedPip) { 500 mHasUserMovedPip = hasUserMovedPip; 501 } 502 503 /** 504 * Registers a callback when the minimal size of PIP that is set by the app changes. 505 */ setOnMinimalSizeChangeCallback(@ullable Runnable onMinimalSizeChangeCallback)506 public void setOnMinimalSizeChangeCallback(@Nullable Runnable onMinimalSizeChangeCallback) { 507 mOnMinimalSizeChangeCallback = onMinimalSizeChangeCallback; 508 } 509 510 /** Set a callback to be notified when the shelf visibility changes. */ setOnShelfVisibilityChangeCallback( @ullable TriConsumer<Boolean, Integer, Boolean> onShelfVisibilityChangeCallback)511 public void setOnShelfVisibilityChangeCallback( 512 @Nullable TriConsumer<Boolean, Integer, Boolean> onShelfVisibilityChangeCallback) { 513 mOnShelfVisibilityChangeCallback = onShelfVisibilityChangeCallback; 514 } 515 516 /** 517 * Add a callback to watch out for PiP bounds. This is mostly used by SystemUI's 518 * Back-gesture handler, to avoid conflicting with PiP when it's stashed. 519 */ addPipExclusionBoundsChangeCallback( @ullable Consumer<Rect> onPipExclusionBoundsChangeCallback)520 public void addPipExclusionBoundsChangeCallback( 521 @Nullable Consumer<Rect> onPipExclusionBoundsChangeCallback) { 522 mOnPipExclusionBoundsChangeCallbacks.add(onPipExclusionBoundsChangeCallback); 523 for (Consumer<Rect> callback : mOnPipExclusionBoundsChangeCallbacks) { 524 callback.accept(getBounds()); 525 } 526 } 527 528 /** 529 * Remove a callback that was previously added. 530 */ removePipExclusionBoundsChangeCallback( @ullable Consumer<Rect> onPipExclusionBoundsChangeCallback)531 public void removePipExclusionBoundsChangeCallback( 532 @Nullable Consumer<Rect> onPipExclusionBoundsChangeCallback) { 533 mOnPipExclusionBoundsChangeCallbacks.remove(onPipExclusionBoundsChangeCallback); 534 } 535 536 /** Adds callback to listen on aspect ratio change. */ addOnAspectRatioChangedCallback( @onNull Consumer<Float> onAspectRatioChangedCallback)537 public void addOnAspectRatioChangedCallback( 538 @NonNull Consumer<Float> onAspectRatioChangedCallback) { 539 if (!mOnAspectRatioChangedCallbacks.contains(onAspectRatioChangedCallback)) { 540 mOnAspectRatioChangedCallbacks.add(onAspectRatioChangedCallback); 541 onAspectRatioChangedCallback.accept(mAspectRatio); 542 } 543 } 544 545 /** Removes callback to listen on aspect ratio change. */ removeOnAspectRatioChangedCallback( @onNull Consumer<Float> onAspectRatioChangedCallback)546 public void removeOnAspectRatioChangedCallback( 547 @NonNull Consumer<Float> onAspectRatioChangedCallback) { 548 if (mOnAspectRatioChangedCallbacks.contains(onAspectRatioChangedCallback)) { 549 mOnAspectRatioChangedCallbacks.remove(onAspectRatioChangedCallback); 550 } 551 } 552 getLauncherState()553 public LauncherState getLauncherState() { 554 return mLauncherState; 555 } 556 557 /** Source of truth for the current bounds of PIP that may be in motion. */ 558 public static class MotionBoundsState { 559 /** The bounds used when PIP is in motion (e.g. during a drag or animation) */ 560 private final @NonNull Rect mBoundsInMotion = new Rect(); 561 /** The destination bounds to which PIP is animating. */ 562 private final @NonNull Rect mAnimatingToBounds = new Rect(); 563 564 /** Whether PIP is being dragged or animated (e.g. resizing, in fling, etc). */ isInMotion()565 public boolean isInMotion() { 566 return !mBoundsInMotion.isEmpty(); 567 } 568 569 /** Set the temporary bounds used to represent the drag or animation bounds of PIP. */ setBoundsInMotion(@onNull Rect bounds)570 public void setBoundsInMotion(@NonNull Rect bounds) { 571 mBoundsInMotion.set(bounds); 572 } 573 574 /** Set the bounds to which PIP is animating. */ setAnimatingToBounds(@onNull Rect bounds)575 public void setAnimatingToBounds(@NonNull Rect bounds) { 576 mAnimatingToBounds.set(bounds); 577 } 578 579 /** Called when all ongoing motion operations have ended. */ onAllAnimationsEnded()580 public void onAllAnimationsEnded() { 581 mBoundsInMotion.setEmpty(); 582 } 583 584 /** Called when an ongoing physics animation has ended. */ onPhysicsAnimationEnded()585 public void onPhysicsAnimationEnded() { 586 mAnimatingToBounds.setEmpty(); 587 } 588 589 /** Returns the motion bounds. */ 590 @NonNull getBoundsInMotion()591 public Rect getBoundsInMotion() { 592 return mBoundsInMotion; 593 } 594 595 /** Returns the destination bounds to which PIP is currently animating. */ 596 @NonNull getAnimatingToBounds()597 public Rect getAnimatingToBounds() { 598 return mAnimatingToBounds; 599 } 600 dump(PrintWriter pw, String prefix)601 void dump(PrintWriter pw, String prefix) { 602 final String innerPrefix = prefix + " "; 603 pw.println(prefix + MotionBoundsState.class.getSimpleName()); 604 pw.println(innerPrefix + "mBoundsInMotion=" + mBoundsInMotion); 605 pw.println(innerPrefix + "mAnimatingToBounds=" + mAnimatingToBounds); 606 } 607 } 608 609 /** Data class for Launcher state. */ 610 public static final class LauncherState { 611 private int mAppIconSizePx; 612 setAppIconSizePx(int appIconSizePx)613 public void setAppIconSizePx(int appIconSizePx) { 614 mAppIconSizePx = appIconSizePx; 615 } 616 getAppIconSizePx()617 public int getAppIconSizePx() { 618 return mAppIconSizePx; 619 } 620 dump(PrintWriter pw, String prefix)621 void dump(PrintWriter pw, String prefix) { 622 final String innerPrefix = prefix + " "; 623 pw.println(prefix + LauncherState.class.getSimpleName()); 624 pw.println(innerPrefix + "getAppIconSizePx=" + getAppIconSizePx()); 625 } 626 } 627 628 /** 629 * Represents the state of pip to potentially restore upon reentry. 630 */ 631 @VisibleForTesting 632 public static final class PipReentryState { 633 private static final String TAG = PipReentryState.class.getSimpleName(); 634 635 private final float mSnapFraction; 636 private final float mBoundsScale; 637 PipReentryState(float boundsScale, float snapFraction)638 PipReentryState(float boundsScale, float snapFraction) { 639 mBoundsScale = boundsScale; 640 mSnapFraction = snapFraction; 641 } 642 getBoundsScale()643 public float getBoundsScale() { 644 return mBoundsScale; 645 } 646 getSnapFraction()647 public float getSnapFraction() { 648 return mSnapFraction; 649 } 650 dump(PrintWriter pw, String prefix)651 void dump(PrintWriter pw, String prefix) { 652 final String innerPrefix = prefix + " "; 653 pw.println(prefix + TAG); 654 pw.println(innerPrefix + "mBoundsScale=" + mBoundsScale); 655 pw.println(innerPrefix + "mSnapFraction=" + mSnapFraction); 656 } 657 } 658 659 /** Dumps internal state. */ dump(PrintWriter pw, String prefix)660 public void dump(PrintWriter pw, String prefix) { 661 final String innerPrefix = prefix + " "; 662 pw.println(prefix + TAG); 663 pw.println(innerPrefix + "mBounds=" + mBounds); 664 pw.println(innerPrefix + "mNormalBounds=" + mNormalBounds); 665 pw.println(innerPrefix + "mExpandedBounds=" + mExpandedBounds); 666 pw.println(innerPrefix + "mMovementBounds=" + mMovementBounds); 667 pw.println(innerPrefix + "mNormalMovementBounds=" + mNormalMovementBounds); 668 pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds); 669 pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName); 670 pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio); 671 pw.println(innerPrefix + "mStashedState=" + mStashedState); 672 pw.println(innerPrefix + "mStashOffset=" + mStashOffset); 673 pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing); 674 pw.println(innerPrefix + "mImeHeight=" + mImeHeight); 675 pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing); 676 pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight); 677 pw.println(innerPrefix + "mHasUserMovedPip=" + mHasUserMovedPip); 678 pw.println(innerPrefix + "mHasUserResizedPip=" + mHasUserResizedPip); 679 pw.println(innerPrefix + "mMinSize=" + mMinSize); 680 pw.println(innerPrefix + "mMaxSize=" + mMaxSize); 681 pw.println(innerPrefix + "mBoundsScale" + mBoundsScale); 682 if (mPipReentryState == null) { 683 pw.println(innerPrefix + "mPipReentryState=null"); 684 } else { 685 mPipReentryState.dump(pw, innerPrefix); 686 } 687 mLauncherState.dump(pw, innerPrefix); 688 mMotionBoundsState.dump(pw, innerPrefix); 689 mSizeSpecSource.dump(pw, innerPrefix); 690 } 691 } 692