• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 android.app;
18 
19 import static android.app.ActivityThread.isSystem;
20 import static android.app.WindowConfigurationProto.ACTIVITY_TYPE;
21 import static android.app.WindowConfigurationProto.APP_BOUNDS;
22 import static android.app.WindowConfigurationProto.BOUNDS;
23 import static android.app.WindowConfigurationProto.MAX_BOUNDS;
24 import static android.app.WindowConfigurationProto.WINDOWING_MODE;
25 import static android.view.Surface.rotationToString;
26 
27 import android.annotation.IntDef;
28 import android.annotation.NonNull;
29 import android.annotation.Nullable;
30 import android.annotation.TestApi;
31 import android.compat.annotation.UnsupportedAppUsage;
32 import android.content.res.Configuration;
33 import android.graphics.Rect;
34 import android.os.Parcel;
35 import android.os.Parcelable;
36 import android.util.proto.ProtoInputStream;
37 import android.util.proto.ProtoOutputStream;
38 import android.util.proto.WireTypeMismatchException;
39 import android.view.DisplayInfo;
40 import android.view.WindowManager;
41 
42 import java.io.IOException;
43 import java.util.Objects;
44 
45 /**
46  * Class that contains windowing configuration/state for other objects that contain windows directly
47  * or indirectly. E.g. Activities, Task, Displays, ...
48  * The test class is {@link com.android.server.wm.WindowConfigurationTests} which must be kept
49  * up-to-date and ran anytime changes are made to this class.
50  * @hide
51  */
52 @TestApi
53 public class WindowConfiguration implements Parcelable, Comparable<WindowConfiguration> {
54     /**
55      * bounds that can differ from app bounds, which may include things such as insets.
56      *
57      * TODO: Investigate combining with {@link #mAppBounds}. Can the latter be a product of the
58      * former?
59      */
60     private final Rect mBounds = new Rect();
61 
62     /**
63      * {@link android.graphics.Rect} defining app bounds. The dimensions override usages of
64      * {@link DisplayInfo#appHeight} and {@link DisplayInfo#appWidth} and mirrors these values at
65      * the display level. Lower levels can override these values to provide custom bounds to enforce
66      * features such as a max aspect ratio.
67      */
68     private Rect mAppBounds;
69 
70     /**
71      * The maximum {@link Rect} bounds that an app can expect. It is used to report value of
72      * {@link WindowManager#getMaximumWindowMetrics()}.
73      */
74     private final Rect mMaxBounds = new Rect();
75 
76     /**
77      * The current rotation of this window container relative to the default
78      * orientation of the display it is on (regardless of how deep in the hierarchy
79      * it is). It is used by the configuration hierarchy to apply rotation-dependent
80      * policy during bounds calculation.
81      */
82     private int mRotation = ROTATION_UNDEFINED;
83 
84     /** Rotation is not defined, use the parent containers rotation. */
85     public static final int ROTATION_UNDEFINED = -1;
86 
87     /** The current windowing mode of the configuration. */
88     private @WindowingMode int mWindowingMode;
89 
90     /** The display windowing mode of the configuration */
91     private @WindowingMode int mDisplayWindowingMode;
92 
93     /** Windowing mode is currently not defined. */
94     public static final int WINDOWING_MODE_UNDEFINED = 0;
95     /** Occupies the full area of the screen or the parent container. */
96     public static final int WINDOWING_MODE_FULLSCREEN = 1;
97     /** Always on-top (always visible). of other siblings in its parent container. */
98     public static final int WINDOWING_MODE_PINNED = 2;
99     /** The primary container driving the screen to be in split-screen mode. */
100     // TODO: Remove once split-screen is migrated to wm-shell.
101     public static final int WINDOWING_MODE_SPLIT_SCREEN_PRIMARY = 3;
102     /**
103      * The containers adjacent to the {@link #WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} container in
104      * split-screen mode.
105      * NOTE: Containers launched with the windowing mode with APIs like
106      * {@link ActivityOptions#setLaunchWindowingMode(int)} will be launched in
107      * {@link #WINDOWING_MODE_FULLSCREEN} if the display isn't currently in split-screen windowing
108      * mode
109      */
110     // TODO: Remove once split-screen is migrated to wm-shell.
111     public static final int WINDOWING_MODE_SPLIT_SCREEN_SECONDARY = 4;
112     /** Can be freely resized within its parent container. */
113     // TODO: Remove once freeform is migrated to wm-shell.
114     public static final int WINDOWING_MODE_FREEFORM = 5;
115     /** Generic multi-window with no presentation attribution from the window manager. */
116     public static final int WINDOWING_MODE_MULTI_WINDOW = 6;
117 
118     /** @hide */
119     @IntDef(prefix = { "WINDOWING_MODE_" }, value = {
120             WINDOWING_MODE_UNDEFINED,
121             WINDOWING_MODE_FULLSCREEN,
122             WINDOWING_MODE_MULTI_WINDOW,
123             WINDOWING_MODE_PINNED,
124             WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
125             WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
126             WINDOWING_MODE_FREEFORM,
127     })
128     public @interface WindowingMode {}
129 
130     /** The current activity type of the configuration. */
131     private @ActivityType int mActivityType;
132 
133     /** Activity type is currently not defined. */
134     public static final int ACTIVITY_TYPE_UNDEFINED = 0;
135     /** Standard activity type. Nothing special about the activity... */
136     public static final int ACTIVITY_TYPE_STANDARD = 1;
137     /** Home/Launcher activity type. */
138     public static final int ACTIVITY_TYPE_HOME = 2;
139     /** Recents/Overview activity type. There is only one activity with this type in the system. */
140     public static final int ACTIVITY_TYPE_RECENTS = 3;
141     /** Assistant activity type. */
142     public static final int ACTIVITY_TYPE_ASSISTANT = 4;
143     /** Dream activity type. */
144     public static final int ACTIVITY_TYPE_DREAM = 5;
145 
146     /** @hide */
147     @IntDef(prefix = { "ACTIVITY_TYPE_" }, value = {
148             ACTIVITY_TYPE_UNDEFINED,
149             ACTIVITY_TYPE_STANDARD,
150             ACTIVITY_TYPE_HOME,
151             ACTIVITY_TYPE_RECENTS,
152             ACTIVITY_TYPE_ASSISTANT,
153             ACTIVITY_TYPE_DREAM,
154     })
155     public @interface ActivityType {}
156 
157     /** The current always on top status of the configuration. */
158     private @AlwaysOnTop int mAlwaysOnTop;
159 
160     /** Always on top is currently not defined. */
161     private static final int ALWAYS_ON_TOP_UNDEFINED = 0;
162     /** Always on top is currently on for this configuration. */
163     private static final int ALWAYS_ON_TOP_ON = 1;
164     /** Always on top is currently off for this configuration. */
165     private static final int ALWAYS_ON_TOP_OFF = 2;
166 
167     /** @hide */
168     @IntDef(prefix = { "ALWAYS_ON_TOP_" }, value = {
169             ALWAYS_ON_TOP_UNDEFINED,
170             ALWAYS_ON_TOP_ON,
171             ALWAYS_ON_TOP_OFF,
172     })
173     private @interface AlwaysOnTop {}
174 
175     /** Bit that indicates that the {@link #mBounds} changed.
176      * @hide */
177     public static final int WINDOW_CONFIG_BOUNDS = 1 << 0;
178     /** Bit that indicates that the {@link #mAppBounds} changed.
179      * @hide */
180     public static final int WINDOW_CONFIG_APP_BOUNDS = 1 << 1;
181     /** Bit that indicates that the {@link #mMaxBounds} changed.
182      * @hide */
183     public static final int WINDOW_CONFIG_MAX_BOUNDS = 1 << 2;
184     /** Bit that indicates that the {@link #mWindowingMode} changed.
185      * @hide */
186     public static final int WINDOW_CONFIG_WINDOWING_MODE = 1 << 3;
187     /** Bit that indicates that the {@link #mActivityType} changed.
188      * @hide */
189     public static final int WINDOW_CONFIG_ACTIVITY_TYPE = 1 << 4;
190     /** Bit that indicates that the {@link #mAlwaysOnTop} changed.
191      * @hide */
192     public static final int WINDOW_CONFIG_ALWAYS_ON_TOP = 1 << 5;
193     /** Bit that indicates that the {@link #mRotation} changed.
194      * @hide */
195     public static final int WINDOW_CONFIG_ROTATION = 1 << 6;
196     /** Bit that indicates that the {@link #mDisplayWindowingMode} changed.
197      * @hide */
198     public static final int WINDOW_CONFIG_DISPLAY_WINDOWING_MODE = 1 << 7;
199 
200     /** @hide */
201     @IntDef(flag = true, prefix = { "WINDOW_CONFIG_" }, value = {
202             WINDOW_CONFIG_BOUNDS,
203             WINDOW_CONFIG_APP_BOUNDS,
204             WINDOW_CONFIG_MAX_BOUNDS,
205             WINDOW_CONFIG_WINDOWING_MODE,
206             WINDOW_CONFIG_ACTIVITY_TYPE,
207             WINDOW_CONFIG_ALWAYS_ON_TOP,
208             WINDOW_CONFIG_ROTATION,
209             WINDOW_CONFIG_DISPLAY_WINDOWING_MODE,
210     })
211     public @interface WindowConfig {}
212 
213     /** @hide */
214     public static final int PINNED_WINDOWING_MODE_ELEVATION_IN_DIP = 5;
215 
216     @UnsupportedAppUsage
WindowConfiguration()217     public WindowConfiguration() {
218         unset();
219     }
220 
221     /** @hide */
WindowConfiguration(WindowConfiguration configuration)222     public WindowConfiguration(WindowConfiguration configuration) {
223         setTo(configuration);
224     }
225 
WindowConfiguration(Parcel in)226     private WindowConfiguration(Parcel in) {
227         readFromParcel(in);
228     }
229 
230     @Override
writeToParcel(Parcel dest, int flags)231     public void writeToParcel(Parcel dest, int flags) {
232         mBounds.writeToParcel(dest, flags);
233         dest.writeTypedObject(mAppBounds, flags);
234         mMaxBounds.writeToParcel(dest, flags);
235         dest.writeInt(mWindowingMode);
236         dest.writeInt(mActivityType);
237         dest.writeInt(mAlwaysOnTop);
238         dest.writeInt(mRotation);
239         dest.writeInt(mDisplayWindowingMode);
240     }
241 
242     /** @hide */
readFromParcel(@onNull Parcel source)243     public void readFromParcel(@NonNull Parcel source) {
244         mBounds.readFromParcel(source);
245         mAppBounds = source.readTypedObject(Rect.CREATOR);
246         mMaxBounds.readFromParcel(source);
247         mWindowingMode = source.readInt();
248         mActivityType = source.readInt();
249         mAlwaysOnTop = source.readInt();
250         mRotation = source.readInt();
251         mDisplayWindowingMode = source.readInt();
252     }
253 
254     @Override
describeContents()255     public int describeContents() {
256         return 0;
257     }
258 
259     /** @hide */
260     public static final @android.annotation.NonNull Creator<WindowConfiguration> CREATOR = new Creator<WindowConfiguration>() {
261         @Override
262         public WindowConfiguration createFromParcel(Parcel in) {
263             return new WindowConfiguration(in);
264         }
265 
266         @Override
267         public WindowConfiguration[] newArray(int size) {
268             return new WindowConfiguration[size];
269         }
270     };
271 
272     /**
273      * Sets the bounds to the provided {@link Rect}.
274      * @param rect the new bounds value.
275      */
setBounds(Rect rect)276     public void setBounds(Rect rect) {
277         if (rect == null) {
278             mBounds.setEmpty();
279             return;
280         }
281 
282         mBounds.set(rect);
283     }
284 
285     /**
286      * Set {@link #mAppBounds} to the input Rect.
287      * @param rect The rect value to set {@link #mAppBounds} to.
288      * @see #getAppBounds()
289      */
setAppBounds(Rect rect)290     public void setAppBounds(Rect rect) {
291         if (rect == null) {
292             mAppBounds = null;
293             return;
294         }
295 
296         setAppBounds(rect.left, rect.top, rect.right, rect.bottom);
297     }
298 
299     /**
300      * Sets the maximum bounds to the provided {@link Rect}.
301      * @param rect the new bounds value.
302      * @see #getMaxBounds()
303      */
setMaxBounds(@ullable Rect rect)304     public void setMaxBounds(@Nullable Rect rect) {
305         if (rect == null) {
306             mMaxBounds.setEmpty();
307             return;
308         }
309         mMaxBounds.set(rect);
310     }
311 
312     /**
313      * @see #setMaxBounds(Rect)
314      * @hide
315      */
setMaxBounds(int left, int top, int right, int bottom)316     public void setMaxBounds(int left, int top, int right, int bottom) {
317         mMaxBounds.set(left, top, right, bottom);
318     }
319 
320     /**
321      * Sets whether this window should be always on top.
322      * @param alwaysOnTop {@code true} to set window always on top, otherwise {@code false}
323      * @hide
324      */
setAlwaysOnTop(boolean alwaysOnTop)325     public void setAlwaysOnTop(boolean alwaysOnTop) {
326         mAlwaysOnTop = alwaysOnTop ? ALWAYS_ON_TOP_ON : ALWAYS_ON_TOP_OFF;
327     }
328 
setAlwaysOnTop(@lwaysOnTop int alwaysOnTop)329     private void setAlwaysOnTop(@AlwaysOnTop int alwaysOnTop) {
330         mAlwaysOnTop = alwaysOnTop;
331     }
332 
333     /**
334      * @see #setAppBounds(Rect)
335      * @see #getAppBounds()
336      * @hide
337      */
setAppBounds(int left, int top, int right, int bottom)338     public void setAppBounds(int left, int top, int right, int bottom) {
339         if (mAppBounds == null) {
340             mAppBounds = new Rect();
341         }
342 
343         mAppBounds.set(left, top, right, bottom);
344     }
345 
346     /** @see #setAppBounds(Rect) */
getAppBounds()347     public Rect getAppBounds() {
348         return mAppBounds;
349     }
350 
351     /** @see #setBounds(Rect) */
getBounds()352     public Rect getBounds() {
353         return mBounds;
354     }
355 
356     /** @see #setMaxBounds(Rect) */
357     @NonNull
getMaxBounds()358     public Rect getMaxBounds() {
359         return mMaxBounds;
360     }
361 
getRotation()362     public int getRotation() {
363         return mRotation;
364     }
365 
setRotation(int rotation)366     public void setRotation(int rotation) {
367         mRotation = rotation;
368     }
369 
setWindowingMode(@indowingMode int windowingMode)370     public void setWindowingMode(@WindowingMode int windowingMode) {
371         mWindowingMode = windowingMode;
372     }
373 
374     @WindowingMode
getWindowingMode()375     public int getWindowingMode() {
376         return mWindowingMode;
377     }
378 
379     /** @hide */
setDisplayWindowingMode(@indowingMode int windowingMode)380     public void setDisplayWindowingMode(@WindowingMode int windowingMode) {
381         mDisplayWindowingMode = windowingMode;
382     }
383 
384     /** @hide */
385     @WindowingMode
getDisplayWindowingMode()386     public int getDisplayWindowingMode() {
387         return mDisplayWindowingMode;
388     }
389 
setActivityType(@ctivityType int activityType)390     public void setActivityType(@ActivityType int activityType) {
391         if (mActivityType == activityType) {
392             return;
393         }
394 
395         // Error check within system server that we are not changing activity type which can be
396         // dangerous. It is okay for things to change in the application process as it doesn't
397         // affect how other things is the system is managed.
398         if (isSystem()
399                 && mActivityType != ACTIVITY_TYPE_UNDEFINED
400                 && activityType != ACTIVITY_TYPE_UNDEFINED) {
401             throw new IllegalStateException("Can't change activity type once set: " + this
402                     + " activityType=" + activityTypeToString(activityType));
403         }
404         mActivityType = activityType;
405     }
406 
407     @ActivityType
getActivityType()408     public int getActivityType() {
409         return mActivityType;
410     }
411 
setTo(WindowConfiguration other)412     public void setTo(WindowConfiguration other) {
413         setBounds(other.mBounds);
414         setAppBounds(other.mAppBounds);
415         setMaxBounds(other.mMaxBounds);
416         setWindowingMode(other.mWindowingMode);
417         setActivityType(other.mActivityType);
418         setAlwaysOnTop(other.mAlwaysOnTop);
419         setRotation(other.mRotation);
420         setDisplayWindowingMode(other.mDisplayWindowingMode);
421     }
422 
423     /** Set this object to completely undefined.
424      * @hide */
unset()425     public void unset() {
426         setToDefaults();
427     }
428 
429     /** @hide */
setToDefaults()430     public void setToDefaults() {
431         setAppBounds(null);
432         setBounds(null);
433         setMaxBounds(null);
434         setWindowingMode(WINDOWING_MODE_UNDEFINED);
435         setActivityType(ACTIVITY_TYPE_UNDEFINED);
436         setAlwaysOnTop(ALWAYS_ON_TOP_UNDEFINED);
437         setRotation(ROTATION_UNDEFINED);
438         setDisplayWindowingMode(WINDOWING_MODE_UNDEFINED);
439     }
440 
441     /**
442      * Copies the fields from delta into this Configuration object, keeping
443      * track of which ones have changed. Any undefined fields in {@code delta}
444      * are ignored and not copied in to the current Configuration.
445      *
446      * @return a bit mask of the changed fields, as per {@link #diff}
447      * @hide
448      */
updateFrom(@onNull WindowConfiguration delta)449     public @WindowConfig int updateFrom(@NonNull WindowConfiguration delta) {
450         int changed = 0;
451         // Only allow override if bounds is not empty
452         if (!delta.mBounds.isEmpty() && !delta.mBounds.equals(mBounds)) {
453             changed |= WINDOW_CONFIG_BOUNDS;
454             setBounds(delta.mBounds);
455         }
456         if (delta.mAppBounds != null && !delta.mAppBounds.equals(mAppBounds)) {
457             changed |= WINDOW_CONFIG_APP_BOUNDS;
458             setAppBounds(delta.mAppBounds);
459         }
460         if (!delta.mMaxBounds.isEmpty() && !delta.mMaxBounds.equals(mMaxBounds)) {
461             changed |= WINDOW_CONFIG_MAX_BOUNDS;
462             setMaxBounds(delta.mMaxBounds);
463         }
464         if (delta.mWindowingMode != WINDOWING_MODE_UNDEFINED
465                 && mWindowingMode != delta.mWindowingMode) {
466             changed |= WINDOW_CONFIG_WINDOWING_MODE;
467             setWindowingMode(delta.mWindowingMode);
468         }
469         if (delta.mActivityType != ACTIVITY_TYPE_UNDEFINED
470                 && mActivityType != delta.mActivityType) {
471             changed |= WINDOW_CONFIG_ACTIVITY_TYPE;
472             setActivityType(delta.mActivityType);
473         }
474         if (delta.mAlwaysOnTop != ALWAYS_ON_TOP_UNDEFINED
475                 && mAlwaysOnTop != delta.mAlwaysOnTop) {
476             changed |= WINDOW_CONFIG_ALWAYS_ON_TOP;
477             setAlwaysOnTop(delta.mAlwaysOnTop);
478         }
479         if (delta.mRotation != ROTATION_UNDEFINED && delta.mRotation != mRotation) {
480             changed |= WINDOW_CONFIG_ROTATION;
481             setRotation(delta.mRotation);
482         }
483         if (delta.mDisplayWindowingMode != WINDOWING_MODE_UNDEFINED
484                 && mDisplayWindowingMode != delta.mDisplayWindowingMode) {
485             changed |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE;
486             setDisplayWindowingMode(delta.mDisplayWindowingMode);
487         }
488         return changed;
489     }
490 
491     /**
492      * Copies the fields specified by mask from delta into this Configuration object.
493      * @hide
494      */
setTo(@onNull WindowConfiguration delta, @WindowConfig int mask)495     public void setTo(@NonNull WindowConfiguration delta, @WindowConfig int mask) {
496         if ((mask & WINDOW_CONFIG_BOUNDS) != 0) {
497             setBounds(delta.mBounds);
498         }
499         if ((mask & WINDOW_CONFIG_APP_BOUNDS) != 0) {
500             setAppBounds(delta.mAppBounds);
501         }
502         if ((mask & WINDOW_CONFIG_MAX_BOUNDS) != 0) {
503             setMaxBounds(delta.mMaxBounds);
504         }
505         if ((mask & WINDOW_CONFIG_WINDOWING_MODE) != 0) {
506             setWindowingMode(delta.mWindowingMode);
507         }
508         if ((mask & WINDOW_CONFIG_ACTIVITY_TYPE) != 0) {
509             setActivityType(delta.mActivityType);
510         }
511         if ((mask & WINDOW_CONFIG_ALWAYS_ON_TOP) != 0) {
512             setAlwaysOnTop(delta.mAlwaysOnTop);
513         }
514         if ((mask & WINDOW_CONFIG_ROTATION) != 0) {
515             setRotation(delta.mRotation);
516         }
517         if ((mask & WINDOW_CONFIG_DISPLAY_WINDOWING_MODE) != 0) {
518             setDisplayWindowingMode(delta.mDisplayWindowingMode);
519         }
520     }
521 
522     /**
523      * Return a bit mask of the differences between this Configuration object and the given one.
524      * Does not change the values of either. Any undefined fields in <var>other</var> are ignored.
525      * @param other The configuration to diff against.
526      * @param compareUndefined If undefined values should be compared.
527      * @return Returns a bit mask indicating which configuration
528      * values has changed, containing any combination of {@link WindowConfig} flags.
529      *
530      * @see Configuration#diff(Configuration)
531      * @hide
532      */
diff(WindowConfiguration other, boolean compareUndefined)533     public @WindowConfig long diff(WindowConfiguration other, boolean compareUndefined) {
534         long changes = 0;
535 
536         if (!mBounds.equals(other.mBounds)) {
537             changes |= WINDOW_CONFIG_BOUNDS;
538         }
539 
540         // Make sure that one of the values is not null and that they are not equal.
541         if ((compareUndefined || other.mAppBounds != null)
542                 && mAppBounds != other.mAppBounds
543                 && (mAppBounds == null || !mAppBounds.equals(other.mAppBounds))) {
544             changes |= WINDOW_CONFIG_APP_BOUNDS;
545         }
546 
547         if (!mMaxBounds.equals(other.mMaxBounds)) {
548             changes |= WINDOW_CONFIG_MAX_BOUNDS;
549         }
550 
551         if ((compareUndefined || other.mWindowingMode != WINDOWING_MODE_UNDEFINED)
552                 && mWindowingMode != other.mWindowingMode) {
553             changes |= WINDOW_CONFIG_WINDOWING_MODE;
554         }
555 
556         if ((compareUndefined || other.mActivityType != ACTIVITY_TYPE_UNDEFINED)
557                 && mActivityType != other.mActivityType) {
558             changes |= WINDOW_CONFIG_ACTIVITY_TYPE;
559         }
560 
561         if ((compareUndefined || other.mAlwaysOnTop != ALWAYS_ON_TOP_UNDEFINED)
562                 && mAlwaysOnTop != other.mAlwaysOnTop) {
563             changes |= WINDOW_CONFIG_ALWAYS_ON_TOP;
564         }
565 
566         if ((compareUndefined || other.mRotation != ROTATION_UNDEFINED)
567                 && mRotation != other.mRotation) {
568             changes |= WINDOW_CONFIG_ROTATION;
569         }
570 
571         if ((compareUndefined || other.mDisplayWindowingMode != WINDOWING_MODE_UNDEFINED)
572                 && mDisplayWindowingMode != other.mDisplayWindowingMode) {
573             changes |= WINDOW_CONFIG_DISPLAY_WINDOWING_MODE;
574         }
575 
576         return changes;
577     }
578 
579     @Override
compareTo(WindowConfiguration that)580     public int compareTo(WindowConfiguration that) {
581         int n = 0;
582         if (mAppBounds == null && that.mAppBounds != null) {
583             return 1;
584         } else if (mAppBounds != null && that.mAppBounds == null) {
585             return -1;
586         } else if (mAppBounds != null && that.mAppBounds != null) {
587             n = mAppBounds.left - that.mAppBounds.left;
588             if (n != 0) return n;
589             n = mAppBounds.top - that.mAppBounds.top;
590             if (n != 0) return n;
591             n = mAppBounds.right - that.mAppBounds.right;
592             if (n != 0) return n;
593             n = mAppBounds.bottom - that.mAppBounds.bottom;
594             if (n != 0) return n;
595         }
596 
597         n = mMaxBounds.left - that.mMaxBounds.left;
598         if (n != 0) return n;
599         n = mMaxBounds.top - that.mMaxBounds.top;
600         if (n != 0) return n;
601         n = mMaxBounds.right - that.mMaxBounds.right;
602         if (n != 0) return n;
603         n = mMaxBounds.bottom - that.mMaxBounds.bottom;
604         if (n != 0) return n;
605 
606         n = mBounds.left - that.mBounds.left;
607         if (n != 0) return n;
608         n = mBounds.top - that.mBounds.top;
609         if (n != 0) return n;
610         n = mBounds.right - that.mBounds.right;
611         if (n != 0) return n;
612         n = mBounds.bottom - that.mBounds.bottom;
613         if (n != 0) return n;
614 
615         n = mWindowingMode - that.mWindowingMode;
616         if (n != 0) return n;
617         n = mActivityType - that.mActivityType;
618         if (n != 0) return n;
619         n = mAlwaysOnTop - that.mAlwaysOnTop;
620         if (n != 0) return n;
621         n = mRotation - that.mRotation;
622         if (n != 0) return n;
623         n = mDisplayWindowingMode - that.mDisplayWindowingMode;
624         if (n != 0) return n;
625 
626         // if (n != 0) return n;
627         return n;
628     }
629 
630     /** @hide */
631     @Override
equals(@ullable Object that)632     public boolean equals(@Nullable Object that) {
633         if (that == null) return false;
634         if (that == this) return true;
635         if (!(that instanceof WindowConfiguration)) {
636             return false;
637         }
638         return this.compareTo((WindowConfiguration) that) == 0;
639     }
640 
641     /** @hide */
642     @Override
hashCode()643     public int hashCode() {
644         int result = 0;
645         result = 31 * result + Objects.hashCode(mAppBounds);
646         result = 31 * result + Objects.hashCode(mBounds);
647         result = 31 * result + Objects.hashCode(mMaxBounds);
648         result = 31 * result + mWindowingMode;
649         result = 31 * result + mActivityType;
650         result = 31 * result + mAlwaysOnTop;
651         result = 31 * result + mRotation;
652         result = 31 * result + mDisplayWindowingMode;
653         return result;
654     }
655 
656     /** @hide */
657     @Override
toString()658     public String toString() {
659         return "{ mBounds=" + mBounds
660                 + " mAppBounds=" + mAppBounds
661                 + " mMaxBounds=" + mMaxBounds
662                 + " mWindowingMode=" + windowingModeToString(mWindowingMode)
663                 + " mDisplayWindowingMode=" + windowingModeToString(mDisplayWindowingMode)
664                 + " mActivityType=" + activityTypeToString(mActivityType)
665                 + " mAlwaysOnTop=" + alwaysOnTopToString(mAlwaysOnTop)
666                 + " mRotation=" + (mRotation == ROTATION_UNDEFINED
667                         ? "undefined" : rotationToString(mRotation))
668                 + "}";
669     }
670 
671     /**
672      * Write to a protocol buffer output stream.
673      * Protocol buffer message definition at {@link android.app.WindowConfigurationProto}
674      *
675      * @param protoOutputStream Stream to write the WindowConfiguration object to.
676      * @param fieldId           Field Id of the WindowConfiguration as defined in the parent message
677      * @hide
678      */
dumpDebug(ProtoOutputStream protoOutputStream, long fieldId)679     public void dumpDebug(ProtoOutputStream protoOutputStream, long fieldId) {
680         final long token = protoOutputStream.start(fieldId);
681         if (mAppBounds != null) {
682             mAppBounds.dumpDebug(protoOutputStream, APP_BOUNDS);
683         }
684         protoOutputStream.write(WINDOWING_MODE, mWindowingMode);
685         protoOutputStream.write(ACTIVITY_TYPE, mActivityType);
686         mBounds.dumpDebug(protoOutputStream, BOUNDS);
687         mMaxBounds.dumpDebug(protoOutputStream, MAX_BOUNDS);
688         protoOutputStream.end(token);
689     }
690 
691     /**
692      * Read from a protocol buffer input stream.
693      * Protocol buffer message definition at {@link android.app.WindowConfigurationProto}
694      *
695      * @param proto   Stream to read the WindowConfiguration object from.
696      * @param fieldId Field Id of the WindowConfiguration as defined in the parent message
697      * @hide
698      */
readFromProto(ProtoInputStream proto, long fieldId)699     public void readFromProto(ProtoInputStream proto, long fieldId)
700             throws IOException, WireTypeMismatchException {
701         final long token = proto.start(fieldId);
702         try {
703             while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) {
704                 switch (proto.getFieldNumber()) {
705                     case (int) APP_BOUNDS:
706                         mAppBounds = new Rect();
707                         mAppBounds.readFromProto(proto, APP_BOUNDS);
708                         break;
709                     case (int) BOUNDS:
710                         mBounds.readFromProto(proto, BOUNDS);
711                         break;
712                     case (int) MAX_BOUNDS:
713                         mMaxBounds.readFromProto(proto, MAX_BOUNDS);
714                         break;
715                     case (int) WINDOWING_MODE:
716                         mWindowingMode = proto.readInt(WINDOWING_MODE);
717                         break;
718                     case (int) ACTIVITY_TYPE:
719                         mActivityType = proto.readInt(ACTIVITY_TYPE);
720                         break;
721                 }
722             }
723         } finally {
724             // Let caller handle any exceptions
725             proto.end(token);
726         }
727     }
728 
729     /**
730      * Returns true if the activities associated with this window configuration display a shadow
731      * around their border.
732      * @hide
733      */
hasWindowShadow()734     public boolean hasWindowShadow() {
735         return mWindowingMode != WINDOWING_MODE_MULTI_WINDOW && tasksAreFloating();
736     }
737 
738     /**
739      * Returns true if the activities associated with this window configuration display a decor
740      * view.
741      * @hide
742      */
hasWindowDecorCaption()743     public boolean hasWindowDecorCaption() {
744         return mActivityType == ACTIVITY_TYPE_STANDARD && (mWindowingMode == WINDOWING_MODE_FREEFORM
745                 || mDisplayWindowingMode == WINDOWING_MODE_FREEFORM);
746     }
747 
748     /**
749      * Returns true if the tasks associated with this window configuration can be resized
750      * independently of their parent container.
751      * @hide
752      */
canResizeTask()753     public boolean canResizeTask() {
754         return mWindowingMode == WINDOWING_MODE_FREEFORM
755                 || mWindowingMode == WINDOWING_MODE_MULTI_WINDOW;
756     }
757 
758     /** Returns true if the task bounds should persist across power cycles.
759      * @hide */
persistTaskBounds()760     public boolean persistTaskBounds() {
761         return mWindowingMode == WINDOWING_MODE_FREEFORM;
762     }
763 
764     /**
765      * Returns true if the tasks associated with this window configuration are floating.
766      * Floating tasks are laid out differently as they are allowed to extend past the display bounds
767      * without overscan insets.
768      * @hide
769      */
tasksAreFloating()770     public boolean tasksAreFloating() {
771         return isFloating(mWindowingMode);
772     }
773 
774     /**
775      * Returns true if the windowingMode represents a floating window.
776      * @hide
777      */
isFloating(int windowingMode)778     public static boolean isFloating(int windowingMode) {
779         return windowingMode == WINDOWING_MODE_FREEFORM || windowingMode == WINDOWING_MODE_PINNED;
780     }
781 
782     /**
783      * Returns {@code true} if the windowingMode represents a window in multi-window mode.
784      * I.e. sharing the screen with another activity.
785      * @hide
786      */
inMultiWindowMode(int windowingMode)787     public static boolean inMultiWindowMode(int windowingMode) {
788         return windowingMode != WINDOWING_MODE_FULLSCREEN
789                 && windowingMode != WINDOWING_MODE_UNDEFINED;
790     }
791 
792     /**
793      * Returns true if the windowingMode represents a split window.
794      * @hide
795      */
isSplitScreenWindowingMode(int windowingMode)796     public static boolean isSplitScreenWindowingMode(int windowingMode) {
797         return windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
798                 || windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
799     }
800 
801     /**
802      * Returns true if the windows associated with this window configuration can receive input keys.
803      * @hide
804      */
canReceiveKeys()805     public boolean canReceiveKeys() {
806         return mWindowingMode != WINDOWING_MODE_PINNED;
807     }
808 
809     /**
810      * Returns true if the container associated with this window configuration is always-on-top of
811      * its siblings.
812      * @hide
813      */
isAlwaysOnTop()814     public boolean isAlwaysOnTop() {
815         if (mWindowingMode == WINDOWING_MODE_PINNED) return true;
816         if (mActivityType == ACTIVITY_TYPE_DREAM) return true;
817         if (mAlwaysOnTop != ALWAYS_ON_TOP_ON) return false;
818         return mWindowingMode == WINDOWING_MODE_FREEFORM
819                     || mWindowingMode == WINDOWING_MODE_MULTI_WINDOW;
820     }
821 
822     /**
823      * Returns true if any visible windows belonging to apps with this window configuration should
824      * be kept on screen when the app is killed due to something like the low memory killer.
825      * @hide
826      */
keepVisibleDeadAppWindowOnScreen()827     public boolean keepVisibleDeadAppWindowOnScreen() {
828         return mWindowingMode != WINDOWING_MODE_PINNED;
829     }
830 
831     /**
832      * Returns true if the backdrop on the client side should match the frame of the window.
833      * Returns false, if the backdrop should be fullscreen.
834      * @hide
835      */
useWindowFrameForBackdrop()836     public boolean useWindowFrameForBackdrop() {
837         return mWindowingMode == WINDOWING_MODE_FREEFORM || mWindowingMode == WINDOWING_MODE_PINNED;
838     }
839 
840     /**
841      * Returns true if windows in this container should be given move animations by default.
842      * @hide
843      */
hasMovementAnimations()844     public boolean hasMovementAnimations() {
845         return mWindowingMode != WINDOWING_MODE_PINNED;
846     }
847 
848     /**
849      * Returns true if this container can be put in either
850      * {@link #WINDOWING_MODE_SPLIT_SCREEN_PRIMARY} or
851      * {@link #WINDOWING_MODE_SPLIT_SCREEN_SECONDARY} windowing modes based on its current state.
852      * @hide
853      */
supportSplitScreenWindowingMode()854     public boolean supportSplitScreenWindowingMode() {
855         return supportSplitScreenWindowingMode(mActivityType);
856     }
857 
858     /** @hide */
supportSplitScreenWindowingMode(int activityType)859     public static boolean supportSplitScreenWindowingMode(int activityType) {
860         return activityType != ACTIVITY_TYPE_ASSISTANT && activityType != ACTIVITY_TYPE_DREAM;
861     }
862 
863     /** @hide */
windowingModeToString(@indowingMode int windowingMode)864     public static String windowingModeToString(@WindowingMode int windowingMode) {
865         switch (windowingMode) {
866             case WINDOWING_MODE_UNDEFINED: return "undefined";
867             case WINDOWING_MODE_FULLSCREEN: return "fullscreen";
868             case WINDOWING_MODE_MULTI_WINDOW: return "multi-window";
869             case WINDOWING_MODE_PINNED: return "pinned";
870             case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: return "split-screen-primary";
871             case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return "split-screen-secondary";
872             case WINDOWING_MODE_FREEFORM: return "freeform";
873         }
874         return String.valueOf(windowingMode);
875     }
876 
877     /** @hide */
activityTypeToString(@ctivityType int applicationType)878     public static String activityTypeToString(@ActivityType int applicationType) {
879         switch (applicationType) {
880             case ACTIVITY_TYPE_UNDEFINED: return "undefined";
881             case ACTIVITY_TYPE_STANDARD: return "standard";
882             case ACTIVITY_TYPE_HOME: return "home";
883             case ACTIVITY_TYPE_RECENTS: return "recents";
884             case ACTIVITY_TYPE_ASSISTANT: return "assistant";
885             case ACTIVITY_TYPE_DREAM: return "dream";
886         }
887         return String.valueOf(applicationType);
888     }
889 
890     /** @hide */
alwaysOnTopToString(@lwaysOnTop int alwaysOnTop)891     public static String alwaysOnTopToString(@AlwaysOnTop int alwaysOnTop) {
892         switch (alwaysOnTop) {
893             case ALWAYS_ON_TOP_UNDEFINED: return "undefined";
894             case ALWAYS_ON_TOP_ON: return "on";
895             case ALWAYS_ON_TOP_OFF: return "off";
896         }
897         return String.valueOf(alwaysOnTop);
898     }
899 }
900