1 /*
2  * Copyright 2018 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 androidx.core.view;
18 
19 import static android.os.Build.VERSION.SDK_INT;
20 
21 import android.graphics.Rect;
22 import android.view.WindowInsets;
23 import androidx.annotation.Nullable;
24 
25 /**
26  * Describes a set of insets for window content.
27  *
28  * <p>WindowInsetsCompats are immutable and may be expanded to include more inset types in the
29  * future. To adjust insets, use one of the supplied clone methods to obtain a new
30  * WindowInsetsCompat instance with the adjusted properties.</p>
31  */
32 public class WindowInsetsCompat {
33     private final Object mInsets;
34 
WindowInsetsCompat(Object insets)35     private WindowInsetsCompat(Object insets) {
36         mInsets = insets;
37     }
38 
39     /**
40      * Constructs a new WindowInsetsCompat, copying all values from a source WindowInsetsCompat.
41      *
42      * @param src source from which values are copied
43      */
WindowInsetsCompat(WindowInsetsCompat src)44     public WindowInsetsCompat(WindowInsetsCompat src) {
45         if (SDK_INT >= 20) {
46             mInsets = src == null ? null : new WindowInsets((WindowInsets) src.mInsets);
47         } else {
48             mInsets = null;
49         }
50     }
51 
52     /**
53      * Returns the left system window inset in pixels.
54      *
55      * <p>The system window inset represents the area of a full-screen window that is
56      * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
57      * </p>
58      *
59      * @return The left system window inset
60      */
getSystemWindowInsetLeft()61     public int getSystemWindowInsetLeft() {
62         if (SDK_INT >= 20) {
63             return ((WindowInsets) mInsets).getSystemWindowInsetLeft();
64         } else {
65             return 0;
66         }
67     }
68 
69     /**
70      * Returns the top system window inset in pixels.
71      *
72      * <p>The system window inset represents the area of a full-screen window that is
73      * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
74      * </p>
75      *
76      * @return The top system window inset
77      */
getSystemWindowInsetTop()78     public int getSystemWindowInsetTop() {
79         if (SDK_INT >= 20) {
80             return ((WindowInsets) mInsets).getSystemWindowInsetTop();
81         } else {
82             return 0;
83         }
84     }
85 
86     /**
87      * Returns the right system window inset in pixels.
88      *
89      * <p>The system window inset represents the area of a full-screen window that is
90      * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
91      * </p>
92      *
93      * @return The right system window inset
94      */
getSystemWindowInsetRight()95     public int getSystemWindowInsetRight() {
96         if (SDK_INT >= 20) {
97             return ((WindowInsets) mInsets).getSystemWindowInsetRight();
98         } else {
99             return 0;
100         }
101     }
102 
103     /**
104      * Returns the bottom system window inset in pixels.
105      *
106      * <p>The system window inset represents the area of a full-screen window that is
107      * partially or fully obscured by the status bar, navigation bar, IME or other system windows.
108      * </p>
109      *
110      * @return The bottom system window inset
111      */
getSystemWindowInsetBottom()112     public int getSystemWindowInsetBottom() {
113         if (SDK_INT >= 20) {
114             return ((WindowInsets) mInsets).getSystemWindowInsetBottom();
115         } else {
116             return 0;
117         }
118     }
119 
120     /**
121      * Returns true if this WindowInsets has nonzero system window insets.
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 true if any of the system window inset values are nonzero
128      */
hasSystemWindowInsets()129     public boolean hasSystemWindowInsets() {
130         if (SDK_INT >= 20) {
131             return ((WindowInsets) mInsets).hasSystemWindowInsets();
132         } else {
133             return false;
134         }
135     }
136 
137     /**
138      * Returns true if this WindowInsets has any nonzero insets.
139      *
140      * @return true if any inset values are nonzero
141      */
hasInsets()142     public boolean hasInsets() {
143         if (SDK_INT >= 20) {
144             return ((WindowInsets) mInsets).hasInsets();
145         } else {
146             return false;
147         }
148     }
149 
150     /**
151      * Check if these insets have been fully consumed.
152      *
153      * <p>Insets are considered "consumed" if the applicable <code>consume*</code> methods
154      * have been called such that all insets have been set to zero. This affects propagation of
155      * insets through the view hierarchy; insets that have not been fully consumed will continue
156      * to propagate down to child views.</p>
157      *
158      * <p>The result of this method is equivalent to the return value of
159      * {@link android.view.View#fitSystemWindows(android.graphics.Rect)}.</p>
160      *
161      * @return true if the insets have been fully consumed.
162      */
isConsumed()163     public boolean isConsumed() {
164         if (SDK_INT >= 21) {
165             return ((WindowInsets) mInsets).isConsumed();
166         } else {
167             return false;
168         }
169     }
170 
171     /**
172      * Returns true if the associated window has a round shape.
173      *
174      * <p>A round window's left, top, right and bottom edges reach all the way to the
175      * associated edges of the window but the corners may not be visible. Views responding
176      * to round insets should take care to not lay out critical elements within the corners
177      * where they may not be accessible.</p>
178      *
179      * @return True if the window is round
180      */
isRound()181     public boolean isRound() {
182         if (SDK_INT >= 20) {
183             return ((WindowInsets) mInsets).isRound();
184         } else {
185             return false;
186         }
187     }
188 
189     /**
190      * Returns a copy of this WindowInsets with the system window insets fully consumed.
191      *
192      * @return A modified copy of this WindowInsets
193      */
consumeSystemWindowInsets()194     public WindowInsetsCompat consumeSystemWindowInsets() {
195         if (SDK_INT >= 20) {
196             return new WindowInsetsCompat(((WindowInsets) mInsets).consumeSystemWindowInsets());
197         } else {
198             return null;
199         }
200     }
201 
202     /**
203      * Returns a copy of this WindowInsets with selected system window insets replaced
204      * with new values.
205      *
206      * @param left New left inset in pixels
207      * @param top New top inset in pixels
208      * @param right New right inset in pixels
209      * @param bottom New bottom inset in pixels
210      * @return A modified copy of this WindowInsets
211      */
replaceSystemWindowInsets(int left, int top, int right, int bottom)212     public WindowInsetsCompat replaceSystemWindowInsets(int left, int top, int right, int bottom) {
213         if (SDK_INT >= 20) {
214             return new WindowInsetsCompat(
215                     ((WindowInsets) mInsets).replaceSystemWindowInsets(left, top, right, bottom));
216         } else {
217             return null;
218         }
219     }
220 
221     /**
222      * Returns a copy of this WindowInsets with selected system window insets replaced
223      * with new values.
224      *
225      * @param systemWindowInsets New system window insets. Each field is the inset in pixels
226      *                           for that edge
227      * @return A modified copy of this WindowInsets
228      */
replaceSystemWindowInsets(Rect systemWindowInsets)229     public WindowInsetsCompat replaceSystemWindowInsets(Rect systemWindowInsets) {
230         if (SDK_INT >= 21) {
231             return new WindowInsetsCompat(
232                     ((WindowInsets) mInsets).replaceSystemWindowInsets(systemWindowInsets));
233         } else {
234             return null;
235         }
236     }
237 
238     /**
239      * Returns the top stable inset in pixels.
240      *
241      * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
242      * partially or fully obscured by the system UI elements.  This value does not change
243      * based on the visibility state of those elements; for example, if the status bar is
244      * normally shown, but temporarily hidden, the stable inset will still provide the inset
245      * associated with the status bar being shown.</p>
246      *
247      * @return The top stable inset
248      */
getStableInsetTop()249     public int getStableInsetTop() {
250         if (SDK_INT >= 21) {
251             return ((WindowInsets) mInsets).getStableInsetTop();
252         } else {
253             return 0;
254         }
255     }
256 
257     /**
258      * Returns the left stable inset in pixels.
259      *
260      * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
261      * partially or fully obscured by the system UI elements.  This value does not change
262      * based on the visibility state of those elements; for example, if the status bar is
263      * normally shown, but temporarily hidden, the stable inset will still provide the inset
264      * associated with the status bar being shown.</p>
265      *
266      * @return The left stable inset
267      */
getStableInsetLeft()268     public int getStableInsetLeft() {
269         if (SDK_INT >= 21) {
270             return ((WindowInsets) mInsets).getStableInsetLeft();
271         } else {
272             return 0;
273         }
274     }
275 
276     /**
277      * Returns the right stable inset in pixels.
278      *
279      * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
280      * partially or fully obscured by the system UI elements.  This value does not change
281      * based on the visibility state of those elements; for example, if the status bar is
282      * normally shown, but temporarily hidden, the stable inset will still provide the inset
283      * associated with the status bar being shown.</p>
284      *
285      * @return The right stable inset
286      */
getStableInsetRight()287     public int getStableInsetRight() {
288         if (SDK_INT >= 21) {
289             return ((WindowInsets) mInsets).getStableInsetRight();
290         } else {
291             return 0;
292         }
293     }
294 
295 
296     /**
297      * Returns the bottom stable inset in pixels.
298      *
299      * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
300      * partially or fully obscured by the system UI elements.  This value does not change
301      * based on the visibility state of those elements; for example, if the status bar is
302      * normally shown, but temporarily hidden, the stable inset will still provide the inset
303      * associated with the status bar being shown.</p>
304      *
305      * @return The bottom stable inset
306      */
getStableInsetBottom()307     public int getStableInsetBottom() {
308         if (SDK_INT >= 21) {
309             return ((WindowInsets) mInsets).getStableInsetBottom();
310         } else {
311             return 0;
312         }
313     }
314 
315     /**
316      * Returns true if this WindowInsets has nonzero stable insets.
317      *
318      * <p>The stable inset represents the area of a full-screen window that <b>may</b> be
319      * partially or fully obscured by the system UI elements.  This value does not change
320      * based on the visibility state of those elements; for example, if the status bar is
321      * normally shown, but temporarily hidden, the stable inset will still provide the inset
322      * associated with the status bar being shown.</p>
323      *
324      * @return true if any of the stable inset values are nonzero
325      */
hasStableInsets()326     public boolean hasStableInsets() {
327         if (SDK_INT >= 21) {
328             return ((WindowInsets) mInsets).hasStableInsets();
329         } else {
330             return false;
331         }
332     }
333 
334     /**
335      * Returns a copy of this WindowInsets with the stable insets fully consumed.
336      *
337      * @return A modified copy of this WindowInsetsCompat
338      */
consumeStableInsets()339     public WindowInsetsCompat consumeStableInsets() {
340         if (SDK_INT >= 21) {
341             return new WindowInsetsCompat(((WindowInsets) mInsets).consumeStableInsets());
342         } else {
343             return null;
344         }
345     }
346 
347     /**
348      * Returns the display cutout if there is one.
349      *
350      * @return the display cutout or null if there is none
351      * @see DisplayCutoutCompat
352      */
353     @Nullable
getDisplayCutout()354     public DisplayCutoutCompat getDisplayCutout() {
355         if (SDK_INT >= 28) {
356             return DisplayCutoutCompat.wrap(((WindowInsets) mInsets).getDisplayCutout());
357         } else {
358             return null;
359         }
360     }
361 
362     /**
363      * Returns a copy of this WindowInsets with the cutout fully consumed.
364      *
365      * @return A modified copy of this WindowInsets
366      */
consumeDisplayCutout()367     public WindowInsetsCompat consumeDisplayCutout() {
368         if (SDK_INT >= 28) {
369             return new WindowInsetsCompat(((WindowInsets) mInsets).consumeDisplayCutout());
370         } else {
371             return null;
372         }
373     }
374 
375     @Override
equals(Object o)376     public boolean equals(Object o) {
377         if (this == o) {
378             return true;
379         }
380         if (o == null || getClass() != o.getClass()) {
381             return false;
382         }
383         WindowInsetsCompat other = (WindowInsetsCompat) o;
384         return mInsets == null ? other.mInsets == null : mInsets.equals(other.mInsets);
385     }
386 
387     @Override
hashCode()388     public int hashCode() {
389         return mInsets == null ? 0 : mInsets.hashCode();
390     }
391 
wrap(Object insets)392     static WindowInsetsCompat wrap(Object insets) {
393         return insets == null ? null : new WindowInsetsCompat(insets);
394     }
395 
unwrap(WindowInsetsCompat insets)396     static Object unwrap(WindowInsetsCompat insets) {
397         return insets == null ? null : insets.mInsets;
398     }
399 }
400