• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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 com.android.layoutlib.bridge.util;
18 
19 import com.android.layoutlib.bridge.bars.NavigationHandle;
20 
21 import android.app.ResourcesManager;
22 import android.content.Context;
23 import android.content.res.Resources;
24 import android.graphics.Insets;
25 import android.graphics.PixelFormat;
26 import android.graphics.Rect;
27 import android.util.DisplayMetrics;
28 import android.util.TypedValue;
29 import android.view.Gravity;
30 import android.view.InsetsController;
31 import android.view.InsetsFrameProvider;
32 import android.view.InsetsSource;
33 import android.view.InsetsState;
34 import android.view.Surface;
35 import android.view.View;
36 import android.view.WindowInsets;
37 import android.view.WindowManager;
38 
39 import java.util.List;
40 
41 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
42 import static android.view.InsetsSource.FLAG_SUPPRESS_SCRIM;
43 import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
44 
45 public class InsetUtil {
getCurrentBounds(Context context)46     public static Rect getCurrentBounds(Context context) {
47         synchronized (ResourcesManager.getInstance()) {
48             return context.getResources().getConfiguration().windowConfiguration.getBounds();
49         }
50     }
51 
52     /**
53      * This applies all insets provided by the System UI.
54      * This is a simplified version of what happens in
55      * services/core/java/com/android/server/wm/DisplayPolicy.java.
56      */
setupSysUiInsets(Context context, InsetsController insetsController, List<InsetsFrameProvider> insetsFrameProviders)57     public static void setupSysUiInsets(Context context, InsetsController insetsController,
58             List<InsetsFrameProvider> insetsFrameProviders) {
59         Rect currentBounds = getCurrentBounds(context);
60         insetsController.onFrameChanged(currentBounds);
61         InsetsState insetsState = insetsController.getState();
62         Rect tmpRect = new Rect();
63         // First set the window frame to all inset sources
64         for (InsetsFrameProvider provider : insetsFrameProviders) {
65             InsetsSource source =
66                     insetsState.getOrCreateSource(provider.getId(), provider.getType());
67             source.getFrame().set(currentBounds);
68         }
69         // Then apply the insets
70         for (InsetsFrameProvider provider : insetsFrameProviders) {
71             Insets insets = provider.getInsetsSize();
72             InsetsSource source =
73                     insetsState.getOrCreateSource(provider.getId(), provider.getType());
74             Rect sourceFrame = source.getFrame();
75             if (provider.getMinimalInsetsSizeInDisplayCutoutSafe() != null) {
76                 tmpRect.set(sourceFrame);
77             }
78             source.updateSideHint(currentBounds);
79             calculateInsetsFrame(sourceFrame, insets);
80 
81             if (provider.getMinimalInsetsSizeInDisplayCutoutSafe() != null) {
82                 // The insets is at least with the given size within the display cutout safe area.
83                 // Calculate the smallest size.
84                 calculateInsetsFrame(tmpRect, provider.getMinimalInsetsSizeInDisplayCutoutSafe());
85                 // If it's larger than previous calculation, use it.
86                 if (tmpRect.contains(sourceFrame)) {
87                     sourceFrame.set(tmpRect);
88                 }
89             }
90         }
91     }
92 
93     // Copied from services/core/java/com/android/server/wm/DisplayPolicy.java
calculateInsetsFrame(Rect inOutFrame, Insets insetsSize)94     private static void calculateInsetsFrame(Rect inOutFrame, Insets insetsSize) {
95         if (insetsSize == null) {
96             inOutFrame.setEmpty();
97             return;
98         }
99         // Only one side of the provider shall be applied. Check in the order of left - top -
100         // right - bottom, only the first non-zero value will be applied.
101         if (insetsSize.left != 0) {
102             inOutFrame.right = inOutFrame.left + insetsSize.left;
103         } else if (insetsSize.top != 0) {
104             inOutFrame.bottom = inOutFrame.top + insetsSize.top;
105         } else if (insetsSize.right != 0) {
106             inOutFrame.left = inOutFrame.right - insetsSize.right;
107         } else if (insetsSize.bottom != 0) {
108             inOutFrame.top = inOutFrame.bottom - insetsSize.bottom;
109         } else {
110             inOutFrame.setEmpty();
111         }
112     }
113 
114     // Copied/adapted from packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
getNavBarLayoutParamsForRotation(Context context, View navBar, int rotation)115     public static WindowManager.LayoutParams getNavBarLayoutParamsForRotation(Context context,
116             View navBar, int rotation) {
117         int width = WindowManager.LayoutParams.MATCH_PARENT;
118         int height = WindowManager.LayoutParams.MATCH_PARENT;
119         int insetsHeight = -1;
120         int gravity = Gravity.BOTTOM;
121         boolean navBarCanMove = true;
122         WindowManager windowManager = context.getSystemService(WindowManager.class);
123         if (windowManager != null) {
124             Rect displaySize = windowManager.getCurrentWindowMetrics().getBounds();
125             navBarCanMove = displaySize.width() != displaySize.height() &&
126                     context.getResources().getBoolean(
127                             com.android.internal.R.bool.config_navBarCanMove);
128         }
129         if (!navBarCanMove) {
130             height = context.getResources().getDimensionPixelSize(
131                     com.android.internal.R.dimen.navigation_bar_frame_height);
132             insetsHeight = context.getResources().getDimensionPixelSize(
133                     com.android.internal.R.dimen.navigation_bar_height);
134         } else {
135             switch (rotation) {
136                 case ROTATION_UNDEFINED:
137                 case Surface.ROTATION_0:
138                 case Surface.ROTATION_180:
139                     height = context.getResources().getDimensionPixelSize(
140                             com.android.internal.R.dimen.navigation_bar_frame_height);
141                     insetsHeight = context.getResources().getDimensionPixelSize(
142                             com.android.internal.R.dimen.navigation_bar_height);
143                     break;
144                 case Surface.ROTATION_90:
145                     gravity = Gravity.RIGHT;
146                     width = context.getResources().getDimensionPixelSize(
147                             com.android.internal.R.dimen.navigation_bar_width);
148                     break;
149                 case Surface.ROTATION_270:
150                     gravity = Gravity.LEFT;
151                     width = context.getResources().getDimensionPixelSize(
152                             com.android.internal.R.dimen.navigation_bar_width);
153                     break;
154             }
155         }
156         WindowManager.LayoutParams lp = new WindowManager.LayoutParams(width, height,
157                 WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
158                 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
159                         WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
160                         WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH |
161                         WindowManager.LayoutParams.FLAG_SPLIT_TOUCH |
162                         WindowManager.LayoutParams.FLAG_SLIPPERY, PixelFormat.TRANSLUCENT);
163         lp.gravity = gravity;
164         lp.providedInsets = getInsetsFrameProvider(navBar, insetsHeight, context);
165 
166         lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC |
167                 WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_SIZE_EXTENDED_BY_CUTOUT;
168         lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
169         return lp;
170     }
171 
172     // Copied/adapted from packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
getInsetsFrameProvider(View navBar, int insetsHeight, Context userContext)173     private static InsetsFrameProvider[] getInsetsFrameProvider(View navBar, int insetsHeight,
174             Context userContext) {
175         final InsetsFrameProvider navBarProvider =
176                 new InsetsFrameProvider(navBar, 0, WindowInsets.Type.navigationBars());
177         if (insetsHeight != -1) {
178             navBarProvider.setInsetsSize(Insets.of(0, 0, 0, insetsHeight));
179         }
180         final boolean needsScrim = userContext.getResources().getBoolean(
181                 com.android.internal.R.bool.config_navBarNeedsScrim);
182         navBarProvider.setFlags(needsScrim ? 0 : FLAG_SUPPRESS_SCRIM, FLAG_SUPPRESS_SCRIM);
183 
184         final InsetsFrameProvider tappableElementProvider =
185                 new InsetsFrameProvider(navBar, 0, WindowInsets.Type.tappableElement());
186         final boolean tapThrough = userContext.getResources().getBoolean(
187                 com.android.internal.R.bool.config_navBarTapThrough);
188         if (tapThrough) {
189             tappableElementProvider.setInsetsSize(Insets.NONE);
190         }
191 
192         final int gestureHeight = userContext.getResources().getDimensionPixelSize(
193                 com.android.internal.R.dimen.navigation_bar_gesture_height);
194         final boolean handlingGesture = navBar instanceof NavigationHandle;
195         final InsetsFrameProvider mandatoryGestureProvider =
196                 new InsetsFrameProvider(navBar, 0, WindowInsets.Type.mandatorySystemGestures());
197         if (handlingGesture) {
198             mandatoryGestureProvider.setInsetsSize(Insets.of(0, 0, 0, gestureHeight));
199         }
200         final int gestureInset = handlingGesture ? getUnscaledInset(userContext.getResources()) : 0;
201         return new InsetsFrameProvider[]{navBarProvider, tappableElementProvider,
202                 mandatoryGestureProvider,
203                 new InsetsFrameProvider(navBar, 0, WindowInsets.Type.systemGestures()).setSource(
204                         InsetsFrameProvider.SOURCE_DISPLAY).setInsetsSize(
205                         Insets.of(gestureInset, 0, 0, 0)).setMinimalInsetsSizeInDisplayCutoutSafe(
206                         Insets.of(gestureInset, 0, 0, 0)),
207                 new InsetsFrameProvider(navBar, 1, WindowInsets.Type.systemGestures()).setSource(
208                         InsetsFrameProvider.SOURCE_DISPLAY).setInsetsSize(
209                         Insets.of(0, 0, gestureInset, 0)).setMinimalInsetsSizeInDisplayCutoutSafe(
210                         Insets.of(0, 0, gestureInset, 0))};
211     }
212 
213     // Copied/adapted from packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
getUnscaledInset(Resources userRes)214     private static int getUnscaledInset(Resources userRes) {
215         final DisplayMetrics dm = userRes.getDisplayMetrics();
216         final float defaultInset =
217                 userRes.getDimension(com.android.internal.R.dimen.config_backGestureInset) /
218                         dm.density;
219         final float inset =
220                 TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, defaultInset, dm);
221         return (int) inset;
222     }
223 }
224