1/*
2 * Copyright (C) 2014 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
18package android.view;
19
20import android.graphics.Rect;
21
22/**
23 * Describes a set of insets for window content.
24 *
25 * <p>WindowInsets are immutable and may be expanded to include more inset types in the future.
26 * To adjust insets, use one of the supplied clone methods to obtain a new WindowInsets instance
27 * with the adjusted properties.</p>
28 *
29 * @see View.OnApplyWindowInsetsListener
30 * @see View#onApplyWindowInsets(WindowInsets)
31 */
32public final class WindowInsets {
33
34    private Rect mSystemWindowInsets;
35    private Rect mWindowDecorInsets;
36    private Rect mStableInsets;
37    private Rect mTempRect;
38    private boolean mIsRound;
39
40    /**
41     * In multi-window we force show the navigation bar. Because we don't want that the surface size
42     * changes in this mode, we instead have a flag whether the navigation bar size should always
43     * be consumed, so the app is treated like there is no virtual navigation bar at all.
44     */
45    private boolean mAlwaysConsumeNavBar;
46
47    private boolean mSystemWindowInsetsConsumed = false;
48    private boolean mWindowDecorInsetsConsumed = false;
49    private boolean mStableInsetsConsumed = false;
50
51    private static final Rect EMPTY_RECT = new Rect(0, 0, 0, 0);
52
53    /**
54     * Since new insets may be added in the future that existing apps couldn't
55     * know about, this fully empty constant shouldn't be made available to apps
56     * since it would allow them to inadvertently consume unknown insets by returning it.
57     * @hide
58     */
59    public static final WindowInsets CONSUMED;
60
61    static {
62        CONSUMED = new WindowInsets(null, null, null, false, false);
63    }
64
65    /** @hide */
66    public WindowInsets(Rect systemWindowInsets, Rect windowDecorInsets, Rect stableInsets,
67            boolean isRound, boolean alwaysConsumeNavBar) {
68        mSystemWindowInsetsConsumed = systemWindowInsets == null;
69        mSystemWindowInsets = mSystemWindowInsetsConsumed ? EMPTY_RECT : systemWindowInsets;
70
71        mWindowDecorInsetsConsumed = windowDecorInsets == null;
72        mWindowDecorInsets = mWindowDecorInsetsConsumed ? EMPTY_RECT : windowDecorInsets;
73
74        mStableInsetsConsumed = stableInsets == null;
75        mStableInsets = mStableInsetsConsumed ? EMPTY_RECT : stableInsets;
76
77        mIsRound = isRound;
78        mAlwaysConsumeNavBar = alwaysConsumeNavBar;
79    }
80
81    /**
82     * Construct a new WindowInsets, copying all values from a source WindowInsets.
83     *
84     * @param src Source to copy insets from
85     */
86    public WindowInsets(WindowInsets src) {
87        mSystemWindowInsets = src.mSystemWindowInsets;
88        mWindowDecorInsets = src.mWindowDecorInsets;
89        mStableInsets = src.mStableInsets;
90        mSystemWindowInsetsConsumed = src.mSystemWindowInsetsConsumed;
91        mWindowDecorInsetsConsumed = src.mWindowDecorInsetsConsumed;
92        mStableInsetsConsumed = src.mStableInsetsConsumed;
93        mIsRound = src.mIsRound;
94        mAlwaysConsumeNavBar = src.mAlwaysConsumeNavBar;
95    }
96
97    /** @hide */
98    public WindowInsets(Rect systemWindowInsets) {
99        this(systemWindowInsets, null, null, false, false);
100    }
101
102    /**
103     * Used to provide a safe copy of the system window insets to pass through
104     * to the existing fitSystemWindows method and other similar internals.
105     * @hide
106     */
107    public Rect getSystemWindowInsets() {
108        if (mTempRect == null) {
109            mTempRect = new Rect();
110        }
111        if (mSystemWindowInsets != null) {
112            mTempRect.set(mSystemWindowInsets);
113        } else {
114            // If there were no system window insets, this is just empty.
115            mTempRect.setEmpty();
116        }
117        return mTempRect;
118    }
119
120    /**
121     * Returns the left system window inset in pixels.
122     *
123     * <p>The system window inset represents the area of a full-screen window that is
124     * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
125     * </p>
126     *
127     * @return The left system window inset
128     */
129    public int getSystemWindowInsetLeft() {
130        return mSystemWindowInsets.left;
131    }
132
133    /**
134     * Returns the top system window inset in pixels.
135     *
136     * <p>The system window inset represents the area of a full-screen window that is
137     * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
138     * </p>
139     *
140     * @return The top system window inset
141     */
142    public int getSystemWindowInsetTop() {
143        return mSystemWindowInsets.top;
144    }
145
146    /**
147     * Returns the right system window inset in pixels.
148     *
149     * <p>The system window inset represents the area of a full-screen window that is
150     * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
151     * </p>
152     *
153     * @return The right system window inset
154     */
155    public int getSystemWindowInsetRight() {
156        return mSystemWindowInsets.right;
157    }
158
159    /**
160     * Returns the bottom system window inset in pixels.
161     *
162     * <p>The system window inset represents the area of a full-screen window that is
163     * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
164     * </p>
165     *
166     * @return The bottom system window inset
167     */
168    public int getSystemWindowInsetBottom() {
169        return mSystemWindowInsets.bottom;
170    }
171
172    /**
173     * Returns the left window decor inset in pixels.
174     *
175     * <p>The window decor inset represents the area of the window content area that is
176     * partially or fully obscured by decorations within the window provided by the framework.
177     * This can include action bars, title bars, toolbars, etc.</p>
178     *
179     * @return The left window decor inset
180     * @hide pending API
181     */
182    public int getWindowDecorInsetLeft() {
183        return mWindowDecorInsets.left;
184    }
185
186    /**
187     * Returns the top window decor inset in pixels.
188     *
189     * <p>The window decor inset represents the area of the window content area that is
190     * partially or fully obscured by decorations within the window provided by the framework.
191     * This can include action bars, title bars, toolbars, etc.</p>
192     *
193     * @return The top window decor inset
194     * @hide pending API
195     */
196    public int getWindowDecorInsetTop() {
197        return mWindowDecorInsets.top;
198    }
199
200    /**
201     * Returns the right window decor inset in pixels.
202     *
203     * <p>The window decor inset represents the area of the window content area that is
204     * partially or fully obscured by decorations within the window provided by the framework.
205     * This can include action bars, title bars, toolbars, etc.</p>
206     *
207     * @return The right window decor inset
208     * @hide pending API
209     */
210    public int getWindowDecorInsetRight() {
211        return mWindowDecorInsets.right;
212    }
213
214    /**
215     * Returns the bottom window decor inset in pixels.
216     *
217     * <p>The window decor inset represents the area of the window content area that is
218     * partially or fully obscured by decorations within the window provided by the framework.
219     * This can include action bars, title bars, toolbars, etc.</p>
220     *
221     * @return The bottom window decor inset
222     * @hide pending API
223     */
224    public int getWindowDecorInsetBottom() {
225        return mWindowDecorInsets.bottom;
226    }
227
228    /**
229     * Returns true if this WindowInsets has nonzero system window insets.
230     *
231     * <p>The system window inset represents the area of a full-screen window that is
232     * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
233     * </p>
234     *
235     * @return true if any of the system window inset values are nonzero
236     */
237    public boolean hasSystemWindowInsets() {
238        return mSystemWindowInsets.left != 0 || mSystemWindowInsets.top != 0 ||
239                mSystemWindowInsets.right != 0 || mSystemWindowInsets.bottom != 0;
240    }
241
242    /**
243     * Returns true if this WindowInsets has nonzero window decor insets.
244     *
245     * <p>The window decor inset represents the area of the window content area that is
246     * partially or fully obscured by decorations within the window provided by the framework.
247     * This can include action bars, title bars, toolbars, etc.</p>
248     *
249     * @return true if any of the window decor inset values are nonzero
250     * @hide pending API
251     */
252    public boolean hasWindowDecorInsets() {
253        return mWindowDecorInsets.left != 0 || mWindowDecorInsets.top != 0 ||
254                mWindowDecorInsets.right != 0 || mWindowDecorInsets.bottom != 0;
255    }
256
257    /**
258     * Returns true if this WindowInsets has any nonzero insets.
259     *
260     * @return true if any inset values are nonzero
261     */
262    public boolean hasInsets() {
263        return hasSystemWindowInsets() || hasWindowDecorInsets() || hasStableInsets();
264    }
265
266    /**
267     * Check if these insets have been fully consumed.
268     *
269     * <p>Insets are considered "consumed" if the applicable <code>consume*</code> methods
270     * have been called such that all insets have been set to zero. This affects propagation of
271     * insets through the view hierarchy; insets that have not been fully consumed will continue
272     * to propagate down to child views.</p>
273     *
274     * <p>The result of this method is equivalent to the return value of
275     * {@link View#fitSystemWindows(android.graphics.Rect)}.</p>
276     *
277     * @return true if the insets have been fully consumed.
278     */
279    public boolean isConsumed() {
280        return mSystemWindowInsetsConsumed && mWindowDecorInsetsConsumed && mStableInsetsConsumed;
281    }
282
283    /**
284     * Returns true if the associated window has a round shape.
285     *
286     * <p>A round window's left, top, right and bottom edges reach all the way to the
287     * associated edges of the window but the corners may not be visible. Views responding
288     * to round insets should take care to not lay out critical elements within the corners
289     * where they may not be accessible.</p>
290     *
291     * @return True if the window is round
292     */
293    public boolean isRound() {
294        return mIsRound;
295    }
296
297    /**
298     * Returns a copy of this WindowInsets with the system window insets fully consumed.
299     *
300     * @return A modified copy of this WindowInsets
301     */
302    public WindowInsets consumeSystemWindowInsets() {
303        final WindowInsets result = new WindowInsets(this);
304        result.mSystemWindowInsets = EMPTY_RECT;
305        result.mSystemWindowInsetsConsumed = true;
306        return result;
307    }
308
309    /**
310     * Returns a copy of this WindowInsets with selected system window insets fully consumed.
311     *
312     * @param left true to consume the left system window inset
313     * @param top true to consume the top system window inset
314     * @param right true to consume the right system window inset
315     * @param bottom true to consume the bottom system window inset
316     * @return A modified copy of this WindowInsets
317     * @hide pending API
318     */
319    public WindowInsets consumeSystemWindowInsets(boolean left, boolean top,
320            boolean right, boolean bottom) {
321        if (left || top || right || bottom) {
322            final WindowInsets result = new WindowInsets(this);
323            result.mSystemWindowInsets = new Rect(
324                    left ? 0 : mSystemWindowInsets.left,
325                    top ? 0 : mSystemWindowInsets.top,
326                    right ? 0 : mSystemWindowInsets.right,
327                    bottom ? 0 : mSystemWindowInsets.bottom);
328            return result;
329        }
330        return this;
331    }
332
333    /**
334     * Returns a copy of this WindowInsets with selected system window insets replaced
335     * with new values.
336     *
337     * @param left New left inset in pixels
338     * @param top New top inset in pixels
339     * @param right New right inset in pixels
340     * @param bottom New bottom inset in pixels
341     * @return A modified copy of this WindowInsets
342     */
343    public WindowInsets replaceSystemWindowInsets(int left, int top,
344            int right, int bottom) {
345        final WindowInsets result = new WindowInsets(this);
346        result.mSystemWindowInsets = new Rect(left, top, right, bottom);
347        return result;
348    }
349
350    /**
351     * Returns a copy of this WindowInsets with selected system window insets replaced
352     * with new values.
353     *
354     * @param systemWindowInsets New system window insets. Each field is the inset in pixels
355     *                           for that edge
356     * @return A modified copy of this WindowInsets
357     */
358    public WindowInsets replaceSystemWindowInsets(Rect systemWindowInsets) {
359        final WindowInsets result = new WindowInsets(this);
360        result.mSystemWindowInsets = new Rect(systemWindowInsets);
361        return result;
362    }
363
364    /**
365     * @hide
366     */
367    public WindowInsets consumeWindowDecorInsets() {
368        final WindowInsets result = new WindowInsets(this);
369        result.mWindowDecorInsets.set(0, 0, 0, 0);
370        result.mWindowDecorInsetsConsumed = true;
371        return result;
372    }
373
374    /**
375     * @hide
376     */
377    public WindowInsets consumeWindowDecorInsets(boolean left, boolean top,
378            boolean right, boolean bottom) {
379        if (left || top || right || bottom) {
380            final WindowInsets result = new WindowInsets(this);
381            result.mWindowDecorInsets = new Rect(left ? 0 : mWindowDecorInsets.left,
382                    top ? 0 : mWindowDecorInsets.top,
383                    right ? 0 : mWindowDecorInsets.right,
384                    bottom ? 0 : mWindowDecorInsets.bottom);
385            return result;
386        }
387        return this;
388    }
389
390    /**
391     * @hide
392     */
393    public WindowInsets replaceWindowDecorInsets(int left, int top, int right, int bottom) {
394        final WindowInsets result = new WindowInsets(this);
395        result.mWindowDecorInsets = new Rect(left, top, right, bottom);
396        return result;
397    }
398
399    /**
400     * Returns the top stable inset in pixels.
401     *
402     * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
403     * partially or fully obscured by the system UI elements.  This value does not change
404     * based on the visibility state of those elements; for example, if the status bar is
405     * normally shown, but temporarily hidden, the stable inset will still provide the inset
406     * associated with the status bar being shown.</p>
407     *
408     * @return The top stable inset
409     */
410    public int getStableInsetTop() {
411        return mStableInsets.top;
412    }
413
414    /**
415     * Returns the left stable inset in pixels.
416     *
417     * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
418     * partially or fully obscured by the system UI elements.  This value does not change
419     * based on the visibility state of those elements; for example, if the status bar is
420     * normally shown, but temporarily hidden, the stable inset will still provide the inset
421     * associated with the status bar being shown.</p>
422     *
423     * @return The left stable inset
424     */
425    public int getStableInsetLeft() {
426        return mStableInsets.left;
427    }
428
429    /**
430     * Returns the right stable inset in pixels.
431     *
432     * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
433     * partially or fully obscured by the system UI elements.  This value does not change
434     * based on the visibility state of those elements; for example, if the status bar is
435     * normally shown, but temporarily hidden, the stable inset will still provide the inset
436     * associated with the status bar being shown.</p>
437     *
438     * @return The right stable inset
439     */
440    public int getStableInsetRight() {
441        return mStableInsets.right;
442    }
443
444    /**
445     * Returns the bottom stable inset in pixels.
446     *
447     * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
448     * partially or fully obscured by the system UI elements.  This value does not change
449     * based on the visibility state of those elements; for example, if the status bar is
450     * normally shown, but temporarily hidden, the stable inset will still provide the inset
451     * associated with the status bar being shown.</p>
452     *
453     * @return The bottom stable inset
454     */
455    public int getStableInsetBottom() {
456        return mStableInsets.bottom;
457    }
458
459    /**
460     * Returns true if this WindowInsets has nonzero stable insets.
461     *
462     * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
463     * partially or fully obscured by the system UI elements.  This value does not change
464     * based on the visibility state of those elements; for example, if the status bar is
465     * normally shown, but temporarily hidden, the stable inset will still provide the inset
466     * associated with the status bar being shown.</p>
467     *
468     * @return true if any of the stable inset values are nonzero
469     */
470    public boolean hasStableInsets() {
471        return mStableInsets.top != 0 || mStableInsets.left != 0 || mStableInsets.right != 0
472                || mStableInsets.bottom != 0;
473    }
474
475    /**
476     * Returns a copy of this WindowInsets with the stable insets fully consumed.
477     *
478     * @return A modified copy of this WindowInsets
479     */
480    public WindowInsets consumeStableInsets() {
481        final WindowInsets result = new WindowInsets(this);
482        result.mStableInsets = EMPTY_RECT;
483        result.mStableInsetsConsumed = true;
484        return result;
485    }
486
487    /**
488     * @hide
489     */
490    public boolean shouldAlwaysConsumeNavBar() {
491        return mAlwaysConsumeNavBar;
492    }
493
494    @Override
495    public String toString() {
496        return "WindowInsets{systemWindowInsets=" + mSystemWindowInsets
497                + " windowDecorInsets=" + mWindowDecorInsets
498                + " stableInsets=" + mStableInsets +
499                (isRound() ? " round}" : "}");
500    }
501}
502