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