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