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