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 android.app.Activity;
20 import android.graphics.Rect;
21 import android.os.Build;
22 import android.view.View;
23 import android.view.Window;
24 
25 import androidx.annotation.IdRes;
26 import androidx.annotation.RequiresApi;
27 
28 import org.jspecify.annotations.NonNull;
29 
30 /**
31  * Helper for accessing features in {@link Window}.
32  */
33 public final class WindowCompat {
34     /**
35      * Flag for enabling the Action Bar.
36      * This is enabled by default for some devices. The Action Bar
37      * replaces the title bar and provides an alternate location
38      * for an on-screen menu button on some devices.
39      */
40     public static final int FEATURE_ACTION_BAR = 8;
41 
42     /**
43      * Flag for requesting an Action Bar that overlays window content.
44      * Normally an Action Bar will sit in the space above window content, but if this
45      * feature is requested along with {@link #FEATURE_ACTION_BAR} it will be layered over
46      * the window content itself. This is useful if you would like your app to have more control
47      * over how the Action Bar is displayed, such as letting application content scroll beneath
48      * an Action Bar with a transparent background or otherwise displaying a transparent/translucent
49      * Action Bar over application content.
50      *
51      * <p>This mode is especially useful with {@link View#SYSTEM_UI_FLAG_FULLSCREEN
52      * View.SYSTEM_UI_FLAG_FULLSCREEN}, which allows you to seamlessly hide the
53      * action bar in conjunction with other screen decorations.
54      *
55      * <p>As of {@link Build.VERSION_CODES#JELLY_BEAN}, when an
56      * ActionBar is in this mode it will adjust the insets provided to
57      * {@link View#fitSystemWindows(Rect) View.fitSystemWindows(Rect)}
58      * to include the content covered by the action bar, so you can do layout within
59      * that space.
60      */
61     public static final int FEATURE_ACTION_BAR_OVERLAY = 9;
62 
63     /**
64      * Flag for specifying the behavior of action modes when an Action Bar is not present.
65      * If overlay is enabled, the action mode UI will be allowed to cover existing window content.
66      */
67     public static final int FEATURE_ACTION_MODE_OVERLAY = 10;
68 
WindowCompat()69     private WindowCompat() {}
70 
71     /**
72      * Finds a view that was identified by the {@code android:id} XML attribute
73      * that was processed in {@link Activity#onCreate}, or throws an
74      * IllegalArgumentException if the ID is invalid, or there is no matching view in the hierarchy.
75      * <p>
76      * <strong>Note:</strong> In most cases -- depending on compiler support --
77      * the resulting view is automatically cast to the target class type. If
78      * the target class type is unconstrained, an explicit cast may be
79      * necessary.
80      *
81      * @param window window in which to find the view.
82      * @param id the ID to search for
83      * @return a view with given ID
84      * @see ViewCompat#requireViewById(View, int)
85      * @see Window#findViewById(int)
86      */
87     @SuppressWarnings("TypeParameterUnusedInFormals")
requireViewById(@onNull Window window, @IdRes int id)88     public static <T extends View> @NonNull T requireViewById(@NonNull Window window,
89             @IdRes int id) {
90         if (Build.VERSION.SDK_INT >= 28) {
91             return Api28Impl.requireViewById(window, id);
92         }
93 
94         T view = window.findViewById(id);
95         if (view == null) {
96             throw new IllegalArgumentException("ID does not reference a View inside this Window");
97         }
98         return view;
99     }
100 
101     /**
102      * Sets whether the decor view should fit root-level content views for
103      * {@link WindowInsetsCompat}.
104      * <p>
105      * If set to {@code false}, the framework will not fit the content view to the insets and will
106      * just pass through the {@link WindowInsetsCompat} to the content view.
107      * </p>
108      * <p>
109      * Please note: using the {@link View#setSystemUiVisibility(int)} API in your app can
110      * conflict with this method. Please discontinue use of {@link View#setSystemUiVisibility(int)}.
111      * </p>
112      *
113      * @param window                 The current window.
114      * @param decorFitsSystemWindows Whether the decor view should fit root-level content views for
115      *                               insets.
116      */
setDecorFitsSystemWindows(@onNull Window window, final boolean decorFitsSystemWindows)117     public static void setDecorFitsSystemWindows(@NonNull Window window,
118             final boolean decorFitsSystemWindows) {
119         if (Build.VERSION.SDK_INT >= 35) {
120             Api35Impl.setDecorFitsSystemWindows(window, decorFitsSystemWindows);
121         } else if (Build.VERSION.SDK_INT >= 30) {
122             Api30Impl.setDecorFitsSystemWindows(window, decorFitsSystemWindows);
123         } else {
124             Api16Impl.setDecorFitsSystemWindows(window, decorFitsSystemWindows);
125         }
126     }
127 
128     /**
129      * Retrieves the single {@link WindowInsetsControllerCompat} of the window this view is attached
130      * to.
131      *
132      * @return The {@link WindowInsetsControllerCompat} for the window.
133      * @see Window#getInsetsController()
134      */
getInsetsController(@onNull Window window, @NonNull View view)135     public static @NonNull WindowInsetsControllerCompat getInsetsController(@NonNull Window window,
136             @NonNull View view) {
137         return new WindowInsetsControllerCompat(window, view);
138     }
139 
140     static class Api16Impl {
Api16Impl()141         private Api16Impl() {
142             // This class is not instantiable.
143         }
144 
setDecorFitsSystemWindows(@onNull Window window, final boolean decorFitsSystemWindows)145         static void setDecorFitsSystemWindows(@NonNull Window window,
146                 final boolean decorFitsSystemWindows) {
147             final int decorFitsFlags = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
148                     | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
149                     | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN;
150 
151             final View decorView = window.getDecorView();
152             final int sysUiVis = decorView.getSystemUiVisibility();
153             decorView.setSystemUiVisibility(decorFitsSystemWindows
154                     ? sysUiVis & ~decorFitsFlags
155                     : sysUiVis | decorFitsFlags);
156         }
157     }
158 
159     @RequiresApi(30)
160     static class Api30Impl {
Api30Impl()161         private Api30Impl() {
162             // This class is not instantiable.
163         }
164 
setDecorFitsSystemWindows(@onNull Window window, final boolean decorFitsSystemWindows)165         static void setDecorFitsSystemWindows(@NonNull Window window,
166                 final boolean decorFitsSystemWindows) {
167             final int stableFlag = View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
168 
169             final View decorView = window.getDecorView();
170             final int sysUiVis = decorView.getSystemUiVisibility();
171             decorView.setSystemUiVisibility(decorFitsSystemWindows
172                     ? sysUiVis & ~stableFlag
173                     : sysUiVis | stableFlag);
174             window.setDecorFitsSystemWindows(decorFitsSystemWindows);
175         }
176     }
177 
178     @RequiresApi(35)
179     static class Api35Impl {
Api35Impl()180         private Api35Impl() {
181             // This class is not instantiable.
182         }
183 
setDecorFitsSystemWindows(@onNull Window window, final boolean decorFitsSystemWindows)184         static void setDecorFitsSystemWindows(@NonNull Window window,
185                 final boolean decorFitsSystemWindows) {
186             window.setDecorFitsSystemWindows(decorFitsSystemWindows);
187         }
188     }
189 
190     @RequiresApi(28)
191     static class Api28Impl {
Api28Impl()192         private Api28Impl() {
193             // This class is not instantiable.
194         }
195 
196         @SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
requireViewById(Window window, int id)197         static <T> T requireViewById(Window window, int id) {
198             return (T) window.requireViewById(id);
199         }
200     }
201 }
202