blob: 11922ec804aa82cfd22964277d1d7f8955259330 [file] [log] [blame]
Wale Ogunwale822e5122017-07-26 06:02:24 -07001/*
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
17package android.app;
18
19import android.annotation.IntDef;
20import android.annotation.NonNull;
21import android.content.res.Configuration;
22import android.graphics.Rect;
23import android.os.Parcel;
24import android.os.Parcelable;
25import android.view.DisplayInfo;
26
Wale Ogunwale822e5122017-07-26 06:02:24 -070027/**
28 * Class that contains windowing configuration/state for other objects that contain windows directly
29 * or indirectly. E.g. Activities, Task, Displays, ...
30 * The test class is {@link com.android.server.wm.WindowConfigurationTests} which must be kept
31 * up-to-date and ran anytime changes are made to this class.
32 * @hide
33 */
34public class WindowConfiguration implements Parcelable, Comparable<WindowConfiguration> {
35
36 /**
37 * {@link android.graphics.Rect} defining app bounds. The dimensions override usages of
38 * {@link DisplayInfo#appHeight} and {@link DisplayInfo#appWidth} and mirrors these values at
39 * the display level. Lower levels can override these values to provide custom bounds to enforce
40 * features such as a max aspect ratio.
41 */
42 private Rect mAppBounds;
43
Wale Ogunwale687b4272017-07-27 02:56:23 -070044 /** The current windowing mode of the configuration. */
45 private @WindowingMode int mWindowingMode;
46
47 /** Windowing mode is currently not defined. */
48 public static final int WINDOWING_MODE_UNDEFINED = 0;
49 /** Occupies the full area of the screen or the parent container. */
50 public static final int WINDOWING_MODE_FULLSCREEN = 1;
51 /** Always on-top (always visible). of other siblings in its parent container. */
52 public static final int WINDOWING_MODE_PINNED = 2;
53 /** Occupies a dedicated region of the screen or its parent container. */
54 public static final int WINDOWING_MODE_DOCKED = 3;
55 /** Can be freely resized within its parent container. */
56 public static final int WINDOWING_MODE_FREEFORM = 4;
57
58 @IntDef(value = {
59 WINDOWING_MODE_UNDEFINED,
60 WINDOWING_MODE_FULLSCREEN,
61 WINDOWING_MODE_PINNED,
62 WINDOWING_MODE_DOCKED,
63 WINDOWING_MODE_FREEFORM,
64 })
Wale Ogunwale687b4272017-07-27 02:56:23 -070065 public @interface WindowingMode {}
Wale Ogunwale822e5122017-07-26 06:02:24 -070066
67 /** Bit that indicates that the {@link #mAppBounds} changed. */
68 public static final int WINDOW_CONFIG_APP_BOUNDS = 1 << 0;
Wale Ogunwale687b4272017-07-27 02:56:23 -070069 /** Bit that indicates that the {@link #mWindowingMode} changed. */
70 public static final int WINDOW_CONFIG_WINDOWING_MODE = 1 << 1;
71
72 @IntDef(flag = true,
73 value = {
74 WINDOW_CONFIG_APP_BOUNDS,
75 WINDOW_CONFIG_WINDOWING_MODE,
76 })
Wale Ogunwale687b4272017-07-27 02:56:23 -070077 public @interface WindowConfig {}
Wale Ogunwale822e5122017-07-26 06:02:24 -070078
79 public WindowConfiguration() {
80 unset();
81 }
82
83 public WindowConfiguration(WindowConfiguration configuration) {
84 setTo(configuration);
85 }
86
87 private WindowConfiguration(Parcel in) {
88 readFromParcel(in);
89 }
90
91 @Override
92 public void writeToParcel(Parcel dest, int flags) {
93 dest.writeParcelable(mAppBounds, flags);
Wale Ogunwale687b4272017-07-27 02:56:23 -070094 dest.writeInt(mWindowingMode);
Wale Ogunwale822e5122017-07-26 06:02:24 -070095 }
96
97 private void readFromParcel(Parcel source) {
98 mAppBounds = source.readParcelable(Rect.class.getClassLoader());
Wale Ogunwale687b4272017-07-27 02:56:23 -070099 mWindowingMode = source.readInt();
Wale Ogunwale822e5122017-07-26 06:02:24 -0700100 }
101
102 @Override
103 public int describeContents() {
104 return 0;
105 }
106
107 public static final Creator<WindowConfiguration> CREATOR = new Creator<WindowConfiguration>() {
108 @Override
109 public WindowConfiguration createFromParcel(Parcel in) {
110 return new WindowConfiguration(in);
111 }
112
113 @Override
114 public WindowConfiguration[] newArray(int size) {
115 return new WindowConfiguration[size];
116 }
117 };
118
119 /**
120 * Set {@link #mAppBounds} to the input Rect.
121 * @param rect The rect value to set {@link #mAppBounds} to.
122 * @see #getAppBounds()
123 */
124 public void setAppBounds(Rect rect) {
125 if (rect == null) {
126 mAppBounds = null;
127 return;
128 }
129
130 setAppBounds(rect.left, rect.top, rect.right, rect.bottom);
131 }
132
133 /**
134 * @see #setAppBounds(Rect)
135 * @see #getAppBounds()
136 */
137 public void setAppBounds(int left, int top, int right, int bottom) {
138 if (mAppBounds == null) {
139 mAppBounds = new Rect();
140 }
141
142 mAppBounds.set(left, top, right, bottom);
143 }
144
145 /**
146 * @see #setAppBounds(Rect)
147 */
148 public Rect getAppBounds() {
149 return mAppBounds;
150 }
151
Wale Ogunwale687b4272017-07-27 02:56:23 -0700152 public void setWindowingMode(@WindowingMode int windowingMode) {
153 mWindowingMode = windowingMode;
154 }
155
156 @WindowingMode
157 public int getWindowingMode() {
158 return mWindowingMode;
159 }
160
Wale Ogunwale822e5122017-07-26 06:02:24 -0700161 public void setTo(WindowConfiguration other) {
162 setAppBounds(other.mAppBounds);
Wale Ogunwale687b4272017-07-27 02:56:23 -0700163 setWindowingMode(other.mWindowingMode);
Wale Ogunwale822e5122017-07-26 06:02:24 -0700164 }
165
166 /** Set this object to completely undefined. */
167 public void unset() {
168 setToDefaults();
169 }
170
171 public void setToDefaults() {
172 setAppBounds(null);
Wale Ogunwale687b4272017-07-27 02:56:23 -0700173 setWindowingMode(WINDOWING_MODE_UNDEFINED);
Wale Ogunwale822e5122017-07-26 06:02:24 -0700174 }
175
176 /**
177 * Copies the fields from delta into this Configuration object, keeping
178 * track of which ones have changed. Any undefined fields in {@code delta}
179 * are ignored and not copied in to the current Configuration.
180 *
181 * @return a bit mask of the changed fields, as per {@link #diff}
182 */
183 public @WindowConfig int updateFrom(@NonNull WindowConfiguration delta) {
184 int changed = 0;
185 if (delta.mAppBounds != null && !delta.mAppBounds.equals(mAppBounds)) {
186 changed |= WINDOW_CONFIG_APP_BOUNDS;
187 setAppBounds(delta.mAppBounds);
188 }
Wale Ogunwale687b4272017-07-27 02:56:23 -0700189 if (delta.mWindowingMode != WINDOWING_MODE_UNDEFINED
190 && mWindowingMode != delta.mWindowingMode) {
191 changed |= WINDOW_CONFIG_WINDOWING_MODE;
192 setWindowingMode(delta.mWindowingMode);
193 }
Wale Ogunwale822e5122017-07-26 06:02:24 -0700194 return changed;
195 }
196
197 /**
198 * Return a bit mask of the differences between this Configuration object and the given one.
199 * Does not change the values of either. Any undefined fields in <var>other</var> are ignored.
200 * @param other The configuration to diff against.
201 * @param compareUndefined If undefined values should be compared.
202 * @return Returns a bit mask indicating which configuration
203 * values has changed, containing any combination of {@link WindowConfig} flags.
204 *
205 * @see Configuration#diff(Configuration)
206 */
207 public @WindowConfig long diff(WindowConfiguration other, boolean compareUndefined) {
208 long changes = 0;
209
210 // Make sure that one of the values is not null and that they are not equal.
211 if ((compareUndefined || other.mAppBounds != null)
212 && mAppBounds != other.mAppBounds
213 && (mAppBounds == null || !mAppBounds.equals(other.mAppBounds))) {
214 changes |= WINDOW_CONFIG_APP_BOUNDS;
215 }
216
Wale Ogunwale687b4272017-07-27 02:56:23 -0700217 if ((compareUndefined || other.mWindowingMode != WINDOWING_MODE_UNDEFINED)
218 && mWindowingMode != other.mWindowingMode) {
219 changes |= WINDOW_CONFIG_WINDOWING_MODE;
220 }
221
Wale Ogunwale822e5122017-07-26 06:02:24 -0700222 return changes;
223 }
224
225 @Override
226 public int compareTo(WindowConfiguration that) {
227 int n = 0;
228 if (mAppBounds == null && that.mAppBounds != null) {
229 return 1;
230 } else if (mAppBounds != null && that.mAppBounds == null) {
231 return -1;
232 } else if (mAppBounds != null && that.mAppBounds != null) {
233 n = mAppBounds.left - that.mAppBounds.left;
234 if (n != 0) return n;
235 n = mAppBounds.top - that.mAppBounds.top;
236 if (n != 0) return n;
237 n = mAppBounds.right - that.mAppBounds.right;
238 if (n != 0) return n;
239 n = mAppBounds.bottom - that.mAppBounds.bottom;
240 if (n != 0) return n;
241 }
Wale Ogunwale687b4272017-07-27 02:56:23 -0700242 n = mWindowingMode - that.mWindowingMode;
243 if (n != 0) return n;
Wale Ogunwale822e5122017-07-26 06:02:24 -0700244
245 // if (n != 0) return n;
246 return n;
247 }
248
249 @Override
250 public boolean equals(Object that) {
251 if (that == null) return false;
252 if (that == this) return true;
253 if (!(that instanceof WindowConfiguration)) {
254 return false;
255 }
256 return this.compareTo((WindowConfiguration) that) == 0;
257 }
258
259 @Override
260 public int hashCode() {
261 int result = 0;
262 if (mAppBounds != null) {
263 result = 31 * result + mAppBounds.hashCode();
264 }
Wale Ogunwale687b4272017-07-27 02:56:23 -0700265 result = 31 * result + mWindowingMode;
Wale Ogunwale822e5122017-07-26 06:02:24 -0700266 return result;
267 }
268
269 @Override
270 public String toString() {
Wale Ogunwale687b4272017-07-27 02:56:23 -0700271 return "{mAppBounds=" + mAppBounds
272 + " mWindowingMode=" + windowingModeToString(mWindowingMode) + "}";
273 }
274
Wale Ogunwale3382ab12017-07-27 08:55:03 -0700275 /**
276 * Returns true if the activities associated with this window configuration display a shadow
277 * around their border.
278 */
279 public boolean hasWindowShadow() {
280 return tasksAreFloating();
281 }
282
283 /**
284 * Returns true if the activities associated with this window configuration display a decor
285 * view.
286 */
287 public boolean hasWindowDecorCaption() {
288 return mWindowingMode == WINDOWING_MODE_FREEFORM;
289 }
290
291 /**
292 * Returns true if the tasks associated with this window configuration can be resized
293 * independently of their parent container.
294 */
295 public boolean canResizeTask() {
296 return mWindowingMode == WINDOWING_MODE_FREEFORM;
297 }
298
299 /** Returns true if the task bounds should persist across power cycles. */
300 public boolean persistTaskBounds() {
301 return mWindowingMode == WINDOWING_MODE_FREEFORM;
302 }
303
304 /**
305 * Returns true if the tasks associated with this window configuration are floating.
306 * Floating tasks are laid out differently as they are allowed to extend past the display bounds
307 * without overscan insets.
308 */
309 public boolean tasksAreFloating() {
310 return mWindowingMode == WINDOWING_MODE_FREEFORM || mWindowingMode == WINDOWING_MODE_PINNED;
311 }
312
313 /**
314 * Returns true if the windows associated with this window configuration can receive input keys.
315 */
316 public boolean canReceiveKeys() {
317 return mWindowingMode != WINDOWING_MODE_PINNED;
318 }
319
320 /**
321 * Returns true if the container associated with this window configuration is always-on-top of
322 * its siblings.
323 */
324 public boolean isAlwaysOnTop() {
325 return mWindowingMode == WINDOWING_MODE_PINNED;
326 }
327
328 /**
329 * Returns true if any visible windows belonging to apps with this window configuration should
330 * be kept on screen when the app is killed due to something like the low memory killer.
331 */
332 public boolean keepVisibleDeadAppWindowOnScreen() {
333 return mWindowingMode != WINDOWING_MODE_PINNED;
334 }
335
336 /**
337 * Returns true if the backdrop on the client side should match the frame of the window.
338 * Returns false, if the backdrop should be fullscreen.
339 */
340 public boolean useWindowFrameForBackdrop() {
341 return mWindowingMode == WINDOWING_MODE_FREEFORM || mWindowingMode == WINDOWING_MODE_PINNED;
342 }
343
344 /**
345 * Returns true if this container may be scaled without resizing, and windows within may need
346 * to be configured as such.
347 */
348 public boolean windowsAreScaleable() {
349 return mWindowingMode == WINDOWING_MODE_PINNED;
350 }
351
352 /**
353 * Returns true if windows in this container should be given move animations by default.
354 */
355 public boolean hasMovementAnimations() {
356 return mWindowingMode == WINDOWING_MODE_PINNED;
357 }
358
Wale Ogunwale687b4272017-07-27 02:56:23 -0700359 private static String windowingModeToString(@WindowingMode int windowingMode) {
360 switch (windowingMode) {
361 case WINDOWING_MODE_UNDEFINED: return "undefined";
362 case WINDOWING_MODE_FULLSCREEN: return "fullscreen";
363 case WINDOWING_MODE_PINNED: return "pinned";
364 case WINDOWING_MODE_DOCKED: return "docked";
365 case WINDOWING_MODE_FREEFORM: return "freeform";
366 }
367 return String.valueOf(windowingMode);
Wale Ogunwale822e5122017-07-26 06:02:24 -0700368 }
369}