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