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