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 
PipBoundsState(@onNull Context context, @NonNull SizeSpecSource sizeSpecSource, @NonNull PipDisplayLayoutState pipDisplayLayoutState)129     public PipBoundsState(@NonNull Context context, @NonNull SizeSpecSource sizeSpecSource,
130             @NonNull PipDisplayLayoutState pipDisplayLayoutState) {
131         mContext = context;
132         reloadResources();
133         mSizeSpecSource = sizeSpecSource;
134         mPipDisplayLayoutState = pipDisplayLayoutState;
135     }
136 
137     /** Reloads the resources. */
onConfigurationChanged()138     public void onConfigurationChanged() {
139         reloadResources();
140 
141         // update the size spec resources upon config change too
142         mSizeSpecSource.onConfigurationChanged();
143     }
144 
reloadResources()145     private void reloadResources() {
146         mStashOffset = mContext.getResources().getDimensionPixelSize(R.dimen.pip_stash_offset);
147     }
148 
149     /** Set the current PIP bounds. */
setBounds(@onNull Rect bounds)150     public void setBounds(@NonNull Rect bounds) {
151         mBounds.set(bounds);
152         for (Consumer<Rect> callback : mOnPipExclusionBoundsChangeCallbacks) {
153             callback.accept(bounds);
154         }
155     }
156 
157     /** Get the current PIP bounds. */
158     @NonNull
getBounds()159     public Rect getBounds() {
160         return new Rect(mBounds);
161     }
162 
163     /** Returns the current movement bounds. */
164     @NonNull
getMovementBounds()165     public Rect getMovementBounds() {
166         return mMovementBounds;
167     }
168 
169     /** Set the current normal PIP bounds. */
setNormalBounds(@onNull Rect bounds)170     public void setNormalBounds(@NonNull Rect bounds) {
171         mNormalBounds.set(bounds);
172     }
173 
174     /** Get the current normal PIP bounds. */
175     @NonNull
getNormalBounds()176     public Rect getNormalBounds() {
177         return mNormalBounds;
178     }
179 
180     /** Set the expanded bounds of PIP. */
setExpandedBounds(@onNull Rect bounds)181     public void setExpandedBounds(@NonNull Rect bounds) {
182         mExpandedBounds.set(bounds);
183     }
184 
185     /** Get the PIP expanded bounds. */
186     @NonNull
getExpandedBounds()187     public Rect getExpandedBounds() {
188         return mExpandedBounds;
189     }
190 
191     /** Set the normal movement bounds. */
setNormalMovementBounds(@onNull Rect bounds)192     public void setNormalMovementBounds(@NonNull Rect bounds) {
193         mNormalMovementBounds.set(bounds);
194     }
195 
196     /** Returns the normal movement bounds. */
197     @NonNull
getNormalMovementBounds()198     public Rect getNormalMovementBounds() {
199         return mNormalMovementBounds;
200     }
201 
202     /** Set the expanded movement bounds. */
setExpandedMovementBounds(@onNull Rect bounds)203     public void setExpandedMovementBounds(@NonNull Rect bounds) {
204         mExpandedMovementBounds.set(bounds);
205     }
206 
207     /** Sets the max possible size for resize. */
setMaxSize(int width, int height)208     public void setMaxSize(int width, int height) {
209         mMaxSize.set(width, height);
210     }
211 
212     /** Sets the min possible size for resize. */
setMinSize(int width, int height)213     public void setMinSize(int width, int height) {
214         mMinSize.set(width, height);
215     }
216 
getMaxSize()217     public Point getMaxSize() {
218         return mMaxSize;
219     }
220 
getMinSize()221     public Point getMinSize() {
222         return mMinSize;
223     }
224 
225     /** Returns the expanded movement bounds. */
226     @NonNull
getExpandedMovementBounds()227     public Rect getExpandedMovementBounds() {
228         return mExpandedMovementBounds;
229     }
230 
231     /** Dictate where PiP currently should be stashed, if at all. */
setStashed(@tashType int stashedState)232     public void setStashed(@StashType int stashedState) {
233         if (mStashedState == stashedState) {
234             return;
235         }
236 
237         mStashedState = stashedState;
238         try {
239             ActivityTaskManager.getService().onPictureInPictureStateChanged(
240                     new PictureInPictureUiState(stashedState != STASH_TYPE_NONE /* isStashed */)
241             );
242         } catch (RemoteException e) {
243             ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
244                     "%s: Unable to set alert PiP state change.", TAG);
245         }
246     }
247 
248     /**
249      * Return where the PiP is stashed, if at all.
250      * @return {@code STASH_NONE}, {@code STASH_LEFT} or {@code STASH_RIGHT}.
251      */
getStashedState()252     public @StashType int getStashedState() {
253         return mStashedState;
254     }
255 
256     /** Whether PiP is stashed or not. */
isStashed()257     public boolean isStashed() {
258         return mStashedState != STASH_TYPE_NONE;
259     }
260 
261     /** Returns the offset from the edge of the screen for PiP stash. */
getStashOffset()262     public int getStashOffset() {
263         return mStashOffset;
264     }
265 
266     /** Set the PIP aspect ratio. */
setAspectRatio(float aspectRatio)267     public void setAspectRatio(float aspectRatio) {
268         mAspectRatio = aspectRatio;
269     }
270 
271     /** Get the PIP aspect ratio. */
getAspectRatio()272     public float getAspectRatio() {
273         return mAspectRatio;
274     }
275 
276     /** Save the reentry state to restore to when re-entering PIP mode. */
saveReentryState(Size size, float fraction)277     public void saveReentryState(Size size, float fraction) {
278         mPipReentryState = new PipReentryState(size, fraction);
279     }
280 
281     /** Returns the saved reentry state. */
282     @Nullable
getReentryState()283     public PipReentryState getReentryState() {
284         return mPipReentryState;
285     }
286 
287     /** Set the last {@link ComponentName} to enter PIP mode. */
setLastPipComponentName(@ullable ComponentName lastPipComponentName)288     public void setLastPipComponentName(@Nullable ComponentName lastPipComponentName) {
289         final boolean changed = !Objects.equals(mLastPipComponentName, lastPipComponentName);
290         mLastPipComponentName = lastPipComponentName;
291         if (changed) {
292             clearReentryState();
293             setHasUserResizedPip(false);
294             setHasUserMovedPip(false);
295         }
296     }
297 
298     /** Get the last PIP component name, if any. */
299     @Nullable
getLastPipComponentName()300     public ComponentName getLastPipComponentName() {
301         return mLastPipComponentName;
302     }
303 
304     /** Returns the display's bounds. */
305     @NonNull
getDisplayBounds()306     public Rect getDisplayBounds() {
307         return mPipDisplayLayoutState.getDisplayBounds();
308     }
309 
310     /** Get a copy of the display layout. */
311     @NonNull
getDisplayLayout()312     public DisplayLayout getDisplayLayout() {
313         return mPipDisplayLayoutState.getDisplayLayout();
314     }
315 
316     /**
317      * Clears the PiP re-entry state.
318      */
319     @VisibleForTesting
clearReentryState()320     public void clearReentryState() {
321         mPipReentryState = null;
322     }
323 
324     /** Sets the preferred size of PIP as specified by the activity in PIP mode. */
setOverrideMinSize(@ullable Size overrideMinSize)325     public void setOverrideMinSize(@Nullable Size overrideMinSize) {
326         final boolean changed = !Objects.equals(overrideMinSize, getOverrideMinSize());
327         mSizeSpecSource.setOverrideMinSize(overrideMinSize);
328         if (changed && mOnMinimalSizeChangeCallback != null) {
329             mOnMinimalSizeChangeCallback.run();
330         }
331     }
332 
333     /** Returns the preferred minimal size specified by the activity in PIP. */
334     @Nullable
getOverrideMinSize()335     public Size getOverrideMinSize() {
336         return mSizeSpecSource.getOverrideMinSize();
337     }
338 
339     /** Returns the minimum edge size of the override minimum size, or 0 if not set. */
getOverrideMinEdgeSize()340     public int getOverrideMinEdgeSize() {
341         return mSizeSpecSource.getOverrideMinEdgeSize();
342     }
343 
344     /** Get the state of the bounds in motion. */
345     @NonNull
getMotionBoundsState()346     public MotionBoundsState getMotionBoundsState() {
347         return mMotionBoundsState;
348     }
349 
350     /** Set whether the IME is currently showing and its height. */
setImeVisibility(boolean imeShowing, int imeHeight)351     public void setImeVisibility(boolean imeShowing, int imeHeight) {
352         mIsImeShowing = imeShowing;
353         mImeHeight = imeHeight;
354     }
355 
356     /** Returns whether the IME is currently showing. */
isImeShowing()357     public boolean isImeShowing() {
358         return mIsImeShowing;
359     }
360 
361     /** Returns the IME height. */
getImeHeight()362     public int getImeHeight() {
363         return mImeHeight;
364     }
365 
366     /** Set whether the shelf is showing and its height. */
setShelfVisibility(boolean showing, int height)367     public void setShelfVisibility(boolean showing, int height) {
368         setShelfVisibility(showing, height, true);
369     }
370 
371     /** Set whether the shelf is showing and its height. */
setShelfVisibility(boolean showing, int height, boolean updateMovementBounds)372     public void setShelfVisibility(boolean showing, int height, boolean updateMovementBounds) {
373         final boolean shelfShowing = showing && height > 0;
374         if (shelfShowing == mIsShelfShowing && height == mShelfHeight) {
375             return;
376         }
377 
378         mIsShelfShowing = showing;
379         mShelfHeight = height;
380         if (mOnShelfVisibilityChangeCallback != null) {
381             mOnShelfVisibilityChangeCallback.accept(mIsShelfShowing, mShelfHeight,
382                     updateMovementBounds);
383         }
384     }
385 
386     /** Set the keep clear areas onscreen. The PiP should ideally not cover them. */
setKeepClearAreas(@onNull Set<Rect> restrictedAreas, @NonNull Set<Rect> unrestrictedAreas)387     public void setKeepClearAreas(@NonNull Set<Rect> restrictedAreas,
388             @NonNull Set<Rect> unrestrictedAreas) {
389         mRestrictedKeepClearAreas.clear();
390         mRestrictedKeepClearAreas.addAll(restrictedAreas);
391         mUnrestrictedKeepClearAreas.clear();
392         mUnrestrictedKeepClearAreas.addAll(unrestrictedAreas);
393     }
394 
395     /** Add a named unrestricted keep clear area. */
addNamedUnrestrictedKeepClearArea(@onNull String name, Rect unrestrictedArea)396     public void addNamedUnrestrictedKeepClearArea(@NonNull String name, Rect unrestrictedArea) {
397         mNamedUnrestrictedKeepClearAreas.put(name, unrestrictedArea);
398     }
399 
400     /** Remove a named unrestricted keep clear area. */
removeNamedUnrestrictedKeepClearArea(@onNull String name)401     public void removeNamedUnrestrictedKeepClearArea(@NonNull String name) {
402         mNamedUnrestrictedKeepClearAreas.remove(name);
403     }
404 
405 
406     /**
407      * @return restricted keep clear areas.
408      */
409     @NonNull
getRestrictedKeepClearAreas()410     public Set<Rect> getRestrictedKeepClearAreas() {
411         return mRestrictedKeepClearAreas;
412     }
413 
414     /**
415      * @return unrestricted keep clear areas.
416      */
417     @NonNull
getUnrestrictedKeepClearAreas()418     public Set<Rect> getUnrestrictedKeepClearAreas() {
419         if (mNamedUnrestrictedKeepClearAreas.isEmpty()) return mUnrestrictedKeepClearAreas;
420         final Set<Rect> unrestrictedAreas = new ArraySet<>(mUnrestrictedKeepClearAreas);
421         unrestrictedAreas.addAll(mNamedUnrestrictedKeepClearAreas.values());
422         return unrestrictedAreas;
423     }
424 
425     /**
426      * Initialize states when first entering PiP.
427      */
setBoundsStateForEntry(ComponentName componentName, ActivityInfo activityInfo, PictureInPictureParams params, PipBoundsAlgorithm pipBoundsAlgorithm)428     public void setBoundsStateForEntry(ComponentName componentName, ActivityInfo activityInfo,
429             PictureInPictureParams params, PipBoundsAlgorithm pipBoundsAlgorithm) {
430         setLastPipComponentName(componentName);
431         setAspectRatio(pipBoundsAlgorithm.getAspectRatioOrDefault(params));
432         setOverrideMinSize(pipBoundsAlgorithm.getMinimalSize(activityInfo));
433     }
434 
435     /** Returns whether the shelf is currently showing. */
isShelfShowing()436     public boolean isShelfShowing() {
437         return mIsShelfShowing;
438     }
439 
440     /** Returns the shelf height. */
getShelfHeight()441     public int getShelfHeight() {
442         return mShelfHeight;
443     }
444 
445     /** Returns whether the user has resized the PIP. */
hasUserResizedPip()446     public boolean hasUserResizedPip() {
447         return mHasUserResizedPip;
448     }
449 
450     /** Set whether the user has resized the PIP. */
setHasUserResizedPip(boolean hasUserResizedPip)451     public void setHasUserResizedPip(boolean hasUserResizedPip) {
452         mHasUserResizedPip = hasUserResizedPip;
453     }
454 
455     /** Returns whether the user has moved the PIP. */
hasUserMovedPip()456     public boolean hasUserMovedPip() {
457         return mHasUserMovedPip;
458     }
459 
460     /** Set whether the user has moved the PIP. */
setHasUserMovedPip(boolean hasUserMovedPip)461     public void setHasUserMovedPip(boolean hasUserMovedPip) {
462         mHasUserMovedPip = hasUserMovedPip;
463     }
464 
465     /**
466      * Registers a callback when the minimal size of PIP that is set by the app changes.
467      */
setOnMinimalSizeChangeCallback(@ullable Runnable onMinimalSizeChangeCallback)468     public void setOnMinimalSizeChangeCallback(@Nullable Runnable onMinimalSizeChangeCallback) {
469         mOnMinimalSizeChangeCallback = onMinimalSizeChangeCallback;
470     }
471 
472     /** Set a callback to be notified when the shelf visibility changes. */
setOnShelfVisibilityChangeCallback( @ullable TriConsumer<Boolean, Integer, Boolean> onShelfVisibilityChangeCallback)473     public void setOnShelfVisibilityChangeCallback(
474             @Nullable TriConsumer<Boolean, Integer, Boolean> onShelfVisibilityChangeCallback) {
475         mOnShelfVisibilityChangeCallback = onShelfVisibilityChangeCallback;
476     }
477 
478     /**
479      * Add a callback to watch out for PiP bounds. This is mostly used by SystemUI's
480      * Back-gesture handler, to avoid conflicting with PiP when it's stashed.
481      */
addPipExclusionBoundsChangeCallback( @ullable Consumer<Rect> onPipExclusionBoundsChangeCallback)482     public void addPipExclusionBoundsChangeCallback(
483             @Nullable Consumer<Rect> onPipExclusionBoundsChangeCallback) {
484         mOnPipExclusionBoundsChangeCallbacks.add(onPipExclusionBoundsChangeCallback);
485         for (Consumer<Rect> callback : mOnPipExclusionBoundsChangeCallbacks) {
486             callback.accept(getBounds());
487         }
488     }
489 
490     /**
491      * Remove a callback that was previously added.
492      */
removePipExclusionBoundsChangeCallback( @ullable Consumer<Rect> onPipExclusionBoundsChangeCallback)493     public void removePipExclusionBoundsChangeCallback(
494             @Nullable Consumer<Rect> onPipExclusionBoundsChangeCallback) {
495         mOnPipExclusionBoundsChangeCallbacks.remove(onPipExclusionBoundsChangeCallback);
496     }
497 
getLauncherState()498     public LauncherState getLauncherState() {
499         return mLauncherState;
500     }
501 
502     /** Source of truth for the current bounds of PIP that may be in motion. */
503     public static class MotionBoundsState {
504         /** The bounds used when PIP is in motion (e.g. during a drag or animation) */
505         private final @NonNull Rect mBoundsInMotion = new Rect();
506         /** The destination bounds to which PIP is animating. */
507         private final @NonNull Rect mAnimatingToBounds = new Rect();
508 
509         /** Whether PIP is being dragged or animated (e.g. resizing, in fling, etc). */
isInMotion()510         public boolean isInMotion() {
511             return !mBoundsInMotion.isEmpty();
512         }
513 
514         /** Set the temporary bounds used to represent the drag or animation bounds of PIP. */
setBoundsInMotion(@onNull Rect bounds)515         public void setBoundsInMotion(@NonNull Rect bounds) {
516             mBoundsInMotion.set(bounds);
517         }
518 
519         /** Set the bounds to which PIP is animating. */
setAnimatingToBounds(@onNull Rect bounds)520         public void setAnimatingToBounds(@NonNull Rect bounds) {
521             mAnimatingToBounds.set(bounds);
522         }
523 
524         /** Called when all ongoing motion operations have ended. */
onAllAnimationsEnded()525         public void onAllAnimationsEnded() {
526             mBoundsInMotion.setEmpty();
527         }
528 
529         /** Called when an ongoing physics animation has ended. */
onPhysicsAnimationEnded()530         public void onPhysicsAnimationEnded() {
531             mAnimatingToBounds.setEmpty();
532         }
533 
534         /** Returns the motion bounds. */
535         @NonNull
getBoundsInMotion()536         public Rect getBoundsInMotion() {
537             return mBoundsInMotion;
538         }
539 
540         /** Returns the destination bounds to which PIP is currently animating. */
541         @NonNull
getAnimatingToBounds()542         public Rect getAnimatingToBounds() {
543             return mAnimatingToBounds;
544         }
545 
dump(PrintWriter pw, String prefix)546         void dump(PrintWriter pw, String prefix) {
547             final String innerPrefix = prefix + "  ";
548             pw.println(prefix + MotionBoundsState.class.getSimpleName());
549             pw.println(innerPrefix + "mBoundsInMotion=" + mBoundsInMotion);
550             pw.println(innerPrefix + "mAnimatingToBounds=" + mAnimatingToBounds);
551         }
552     }
553 
554     /** Data class for Launcher state. */
555     public static final class LauncherState {
556         private int mAppIconSizePx;
557 
setAppIconSizePx(int appIconSizePx)558         public void setAppIconSizePx(int appIconSizePx) {
559             mAppIconSizePx = appIconSizePx;
560         }
561 
getAppIconSizePx()562         public int getAppIconSizePx() {
563             return mAppIconSizePx;
564         }
565 
dump(PrintWriter pw, String prefix)566         void dump(PrintWriter pw, String prefix) {
567             final String innerPrefix = prefix + "    ";
568             pw.println(prefix + LauncherState.class.getSimpleName());
569             pw.println(innerPrefix + "getAppIconSizePx=" + getAppIconSizePx());
570         }
571     }
572 
573     /**
574      * Represents the state of pip to potentially restore upon reentry.
575      */
576     @VisibleForTesting
577     public static final class PipReentryState {
578         private static final String TAG = PipReentryState.class.getSimpleName();
579 
580         private final @Nullable Size mSize;
581         private final float mSnapFraction;
582 
PipReentryState(@ullable Size size, float snapFraction)583         PipReentryState(@Nullable Size size, float snapFraction) {
584             mSize = size;
585             mSnapFraction = snapFraction;
586         }
587 
588         @Nullable
getSize()589         public Size getSize() {
590             return mSize;
591         }
592 
getSnapFraction()593         public float getSnapFraction() {
594             return mSnapFraction;
595         }
596 
dump(PrintWriter pw, String prefix)597         void dump(PrintWriter pw, String prefix) {
598             final String innerPrefix = prefix + "  ";
599             pw.println(prefix + TAG);
600             pw.println(innerPrefix + "mSize=" + mSize);
601             pw.println(innerPrefix + "mSnapFraction=" + mSnapFraction);
602         }
603     }
604 
605     /** Dumps internal state. */
dump(PrintWriter pw, String prefix)606     public void dump(PrintWriter pw, String prefix) {
607         final String innerPrefix = prefix + "  ";
608         pw.println(prefix + TAG);
609         pw.println(innerPrefix + "mBounds=" + mBounds);
610         pw.println(innerPrefix + "mNormalBounds=" + mNormalBounds);
611         pw.println(innerPrefix + "mExpandedBounds=" + mExpandedBounds);
612         pw.println(innerPrefix + "mMovementBounds=" + mMovementBounds);
613         pw.println(innerPrefix + "mNormalMovementBounds=" + mNormalMovementBounds);
614         pw.println(innerPrefix + "mExpandedMovementBounds=" + mExpandedMovementBounds);
615         pw.println(innerPrefix + "mLastPipComponentName=" + mLastPipComponentName);
616         pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
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         mSizeSpecSource.dump(pw, innerPrefix);
633     }
634 }
635