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