• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.impl;
18 
19 import com.android.ide.common.rendering.api.HardwareConfig;
20 import com.android.ide.common.rendering.api.RenderResources;
21 import com.android.ide.common.rendering.api.ResourceReference;
22 import com.android.ide.common.rendering.api.ResourceValue;
23 import com.android.ide.common.rendering.api.SessionParams;
24 import com.android.layoutlib.bridge.Bridge;
25 import com.android.layoutlib.bridge.android.BridgeContext;
26 import com.android.layoutlib.bridge.android.RenderParamsFlags;
27 import com.android.layoutlib.bridge.bars.AppCompatActionBar;
28 import com.android.layoutlib.bridge.bars.BridgeActionBar;
29 import com.android.layoutlib.bridge.bars.Config;
30 import com.android.layoutlib.bridge.bars.FrameworkActionBar;
31 import com.android.layoutlib.bridge.bars.NavigationBar;
32 import com.android.layoutlib.bridge.bars.NavigationHandle;
33 import com.android.layoutlib.bridge.bars.StatusBar;
34 import com.android.layoutlib.bridge.bars.TitleBar;
35 import com.android.resources.Density;
36 import com.android.resources.ResourceType;
37 import com.android.resources.ScreenOrientation;
38 
39 import android.R.id;
40 import android.annotation.NonNull;
41 import android.annotation.Nullable;
42 import android.graphics.Point;
43 import android.graphics.Rect;
44 import android.graphics.drawable.Drawable;
45 import android.util.DisplayMetrics;
46 import android.util.TypedValue;
47 import android.view.AttachInfo_Accessor;
48 import android.view.DisplayCutout.BoundsPosition;
49 import android.view.InsetsFrameProvider;
50 import android.view.Surface;
51 import android.view.View;
52 import android.view.ViewGroup;
53 import android.view.ViewRootImpl;
54 import android.view.ViewRootImpl_Accessor;
55 import android.view.WindowManager;
56 import android.widget.FrameLayout;
57 import android.widget.LinearLayout;
58 import android.widget.RelativeLayout;
59 
60 import java.util.ArrayList;
61 import java.util.Arrays;
62 import java.util.List;
63 
64 import static android.os._Original_Build.VERSION_CODES.VANILLA_ICE_CREAM;
65 import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
66 import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
67 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
68 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
69 import static android.widget.LinearLayout.HORIZONTAL;
70 import static android.widget.LinearLayout.VERTICAL;
71 import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_EDGE_TO_EDGE;
72 import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_USE_GESTURE_NAV;
73 import static com.android.layoutlib.bridge.android.RenderParamsFlags.FLAG_KEY_SHOW_CUTOUT;
74 import static com.android.layoutlib.bridge.bars.Config.isGreaterOrEqual;
75 import static com.android.layoutlib.bridge.impl.ResourceHelper.getBooleanThemeFrameworkAttrValue;
76 import static com.android.layoutlib.bridge.impl.ResourceHelper.getBooleanThemeValue;
77 import static com.android.layoutlib.bridge.util.InsetUtil.getNavBarLayoutParamsForRotation;
78 
79 /**
80  * The Layout used to create the system decor.
81  * <p>
82  * The layout inflated will contain a content frame where the user's layout can be inflated.
83  * <pre>
84  *  +-------------------------------------------------+---+
85  *  | Status bar                                      | N |
86  *  +-------------------------------------------------+ a |
87  *  | Title/Framework Action bar (optional)           | v |
88  *  +-------------------------------------------------+   |
89  *  | AppCompat Action bar (optional)                 |   |
90  *  +-------------------------------------------------+   |
91  *  | Content, vertical extending                     | b |
92  *  |                                                 | a |
93  *  |                                                 | r |
94  *  +-------------------------------------------------+---+
95  * </pre>
96  * or
97  * <pre>
98  *  +--------------------------------------+
99  *  | Status bar                           |
100  *  +--------------------------------------+
101  *  | Title/Framework Action bar (optional)|
102  *  +--------------------------------------+
103  *  | AppCompat Action bar (optional)      |
104  *  +--------------------------------------+
105  *  | Content, vertical extending          |
106  *  |                                      |
107  *  |                                      |
108  *  +--------------------------------------+
109  *  | Nav bar                              |
110  *  +--------------------------------------+
111  * </pre>
112  */
113 public class Layout extends FrameLayout {
114 
115     // Theme attributes used for configuring appearance of the system decor.
116     private static final String ATTR_WINDOW_FLOATING = "windowIsFloating";
117     private static final String ATTR_WINDOW_BACKGROUND = "windowBackground";
118     private static final String ATTR_WINDOW_FULL_SCREEN = "windowFullscreen";
119     private static final String ATTR_NAV_BAR_HEIGHT = "navigation_bar_height";
120     private static final String ATTR_NAV_BAR_WIDTH = "navigation_bar_width";
121     private static final String ATTR_STATUS_BAR_HEIGHT = "status_bar_height";
122     private static final String ATTR_WINDOW_ACTION_BAR = "windowActionBar";
123     private static final String ATTR_ACTION_BAR_SIZE = "actionBarSize";
124     private static final String ATTR_WINDOW_NO_TITLE = "windowNoTitle";
125     private static final String ATTR_WINDOW_TITLE_SIZE = "windowTitleSize";
126     private static final String ATTR_WINDOW_TRANSLUCENT_STATUS = StatusBar.ATTR_TRANSLUCENT;
127     private static final String ATTR_WINDOW_TRANSLUCENT_NAV = NavigationBar.ATTR_TRANSLUCENT;
128 
129     // Default sizes
130     private static final int DEFAULT_STATUS_BAR_HEIGHT = 25;
131     private static final int DEFAULT_TITLE_BAR_HEIGHT = 25;
132     private static final int DEFAULT_NAV_BAR_SIZE = 48;
133 
134     // Ids assigned to components created. This is so that we can refer to other components in
135     // layout params.
136     private static final String ID_NAV_BAR = "navBar";
137     private static final String ID_STATUS_BAR = "statusBar";
138     private static final String ID_APP_COMPAT_ACTION_BAR = "appCompatActionBar";
139     private static final String ID_FRAMEWORK_BAR = "frameworkBar";
140     // Prefix used with the above ids in order to make them unique in framework namespace.
141     private static final String ID_PREFIX = "android_layoutlib_";
142 
143     private final List<InsetsFrameProvider> mInsetsFrameProviders = new ArrayList<>();
144 
145     /**
146      * Temporarily store the builder so that it doesn't have to be passed to all methods used
147      * during inflation.
148      */
149     private Builder mBuilder;
150 
151     /**
152      * App UI layout
153      */
154     private final RelativeLayout mAppUiRoot;
155 
156     /**
157      * This holds user's layout.
158      */
159     private FrameLayout mContentRoot;
160 
Layout(@onNull Builder builder)161     public Layout(@NonNull Builder builder) {
162         super(builder.mContext);
163 
164         mBuilder = builder;
165         View frameworkActionBar = null;
166         View appCompatActionBar = null;
167         TitleBar titleBar = null;
168         StatusBar statusBar = null;
169         View navBar = null;
170 
171         if (builder.mWindowBackground != null) {
172             Drawable d = ResourceHelper.getDrawable(builder.mWindowBackground, builder.mContext,
173                     builder.mContext.getTheme());
174             setBackground(d);
175         }
176 
177         int simulatedPlatformVersion = getParams().getSimulatedPlatformVersion();
178         HardwareConfig hwConfig = getParams().getHardwareConfig();
179         Density density = hwConfig.getDensity();
180         boolean isRtl = Bridge.isLocaleRtl(getParams().getLocale());
181         setLayoutDirection(isRtl ? LAYOUT_DIRECTION_RTL : LAYOUT_DIRECTION_LTR);
182 
183         if (mBuilder.hasNavBar()) {
184             navBar = createNavBar(getContext(), mBuilder.useGestureNav(), density, isRtl,
185                     getParams().isRtlSupported(), mBuilder.mIsEdgeToEdge, simulatedPlatformVersion,
186                     false);
187         }
188 
189         if (builder.hasStatusBar()) {
190             statusBar = createStatusBar(getContext(), density, isRtl, getParams().isRtlSupported(),
191                     mBuilder.mIsEdgeToEdge, simulatedPlatformVersion);
192         }
193 
194         if (mBuilder.hasAppCompatActionBar()) {
195             BridgeActionBar bar =
196                     createActionBar(getContext(), getParams(), true, navBar, statusBar);
197             mContentRoot = bar.getContentRoot();
198             appCompatActionBar = bar.getRootView();
199         }
200 
201         // Title bar must appear on top of the Action bar
202         if (mBuilder.hasTitleBar()) {
203             titleBar = createTitleBar(getContext(), getParams().getAppLabel(),
204                     simulatedPlatformVersion, navBar, statusBar);
205         } else if (mBuilder.hasFrameworkActionBar()) {
206             BridgeActionBar bar =
207                     createActionBar(getContext(), getParams(), false, navBar, statusBar);
208             if (mContentRoot == null) {
209                 // We only set the content root if the AppCompat action bar did not already
210                 // provide it
211                 mContentRoot = bar.getContentRoot();
212             }
213             frameworkActionBar = bar.getRootView();
214         }
215 
216         mAppUiRoot = new RelativeLayout(builder.mContext);
217         addAppUiViews(titleBar,
218                 mContentRoot == null ? (mContentRoot = createContentFrame(navBar, statusBar)) :
219                         frameworkActionBar, appCompatActionBar);
220         addView(mAppUiRoot);
221 
222         ViewGroup sysUiRoot = buildSysUi(statusBar, navBar,
223                 hwConfig.getOrientation() == ScreenOrientation.LANDSCAPE);
224         if (sysUiRoot != null) {
225             addView(sysUiRoot, MATCH_PARENT, MATCH_PARENT);
226         }
227         // Done with the builder. Don't hold a reference to it.
228         mBuilder = null;
229     }
230 
231     @Nullable
buildSysUi(@ullable StatusBar statusBar, @Nullable View navBar, boolean rotated)232     private ViewGroup buildSysUi(@Nullable StatusBar statusBar, @Nullable View navBar,
233             boolean rotated) {
234         if (statusBar == null && navBar == null && !mBuilder.mShowCutout) {
235             return null;
236         }
237 
238         FrameLayout sysUiRoot = new FrameLayout(mContext);
239         if (navBar != null && statusBar != null) {
240             if (!mBuilder.useGestureNav() && mBuilder.mNavBarOrientation == VERTICAL) {
241                 LinearLayout insideLayout = new LinearLayout(mContext);
242                 insideLayout.setOrientation(HORIZONTAL);
243                 ViewGroup statusBarContainer = new FrameLayout(mContext);
244                 statusBarContainer.addView(statusBar);
245                 insideLayout.addView(statusBarContainer,
246                         new LinearLayout.LayoutParams(WRAP_CONTENT, MATCH_PARENT, 1.0f));
247                 insideLayout.addView(navBar);
248                 sysUiRoot.addView(insideLayout, MATCH_PARENT, MATCH_PARENT);
249             } else {
250                 sysUiRoot.addView(statusBar);
251                 sysUiRoot.addView(navBar);
252             }
253         } else if (navBar == null) {
254             sysUiRoot.addView(statusBar);
255         } else {
256             sysUiRoot.addView(navBar);
257         }
258 
259         if (mBuilder.mShowCutout) {
260             sysUiRoot.addView(
261                     new DisplayCutoutView(mBuilder.mContext, rotated? BOUNDS_POSITION_LEFT : BOUNDS_POSITION_TOP),
262                     MATCH_PARENT, MATCH_PARENT);
263         }
264         return sysUiRoot;
265     }
266 
267     @Override
getChildVisibleRect(View child, Rect r, Point offset, boolean forceParentCheck)268     public boolean getChildVisibleRect(View child, Rect r, Point offset, boolean forceParentCheck) {
269         return r.intersect(0, 0, getWidth(), getHeight());
270     }
271 
272     @Override
getGlobalVisibleRect(Rect r, Point globalOffset)273     public boolean getGlobalVisibleRect(Rect r, Point globalOffset) {
274         int width = mRight - mLeft;
275         int height = mBottom - mTop;
276         if (width > 0 && height > 0) {
277             r.set(0, 0, width, height);
278             if (globalOffset != null) {
279                 globalOffset.set(-mScrollX, -mScrollY);
280             }
281             return true;
282         }
283         return false;
284     }
285 
286     @NonNull
createContentFrame(@ullable View navBar, @Nullable StatusBar statusBar)287     private FrameLayout createContentFrame(@Nullable View navBar, @Nullable StatusBar statusBar) {
288         FrameLayout contentRoot = new FrameLayout(getContext());
289         RelativeLayout.LayoutParams params = createAppUiLayoutParams(MATCH_PARENT, MATCH_PARENT);
290         if (navBar != null && mBuilder.hasSolidNavBar()) {
291             if (mBuilder.isNavBarVertical()) {
292                 params.bottomMargin = navBar.getLayoutParams().height;
293             } else {
294                 params.rightMargin = navBar.getLayoutParams().width;
295             }
296         }
297         if (!mBuilder.mIsEdgeToEdge) {
298             int below = -1;
299             if (mBuilder.mAppCompatActionBarSize > 0) {
300                 below = getId(ID_APP_COMPAT_ACTION_BAR);
301             } else if (mBuilder.hasFrameworkActionBar() || mBuilder.hasTitleBar()) {
302                 below = getId(ID_FRAMEWORK_BAR);
303             } else if (statusBar != null && mBuilder.hasSolidStatusBar()) {
304                 params.topMargin = statusBar.getLayoutParams().height;
305             }
306             if (below != -1) {
307                 params.addRule(RelativeLayout.BELOW, below);
308             }
309         }
310         contentRoot.setLayoutParams(params);
311         contentRoot.setId(id.content);
312         return contentRoot;
313     }
314 
315     @NonNull
createAppUiLayoutParams(int width, int height)316     private RelativeLayout.LayoutParams createAppUiLayoutParams(int width, int height) {
317         DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
318         if (width > 0) {
319             width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, width, metrics);
320         }
321         if (height > 0) {
322             height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, height, metrics);
323         }
324         return new RelativeLayout.LayoutParams(width, height);
325     }
326 
327     @NonNull
getContentRoot()328     public FrameLayout getContentRoot() {
329         return mContentRoot;
330     }
331 
332     @NonNull
getParams()333     private SessionParams getParams() {
334         return mBuilder.mParams;
335     }
336 
337     @NonNull
338     @Override
getContext()339     public BridgeContext getContext() {
340         return (BridgeContext) super.getContext();
341     }
342 
343     @NonNull
getInsetsFrameProviders()344     public List<InsetsFrameProvider> getInsetsFrameProviders() {
345         return mInsetsFrameProviders;
346     }
347 
348     /**
349      * @param isRtl whether the current locale is an RTL locale.
350      * @param isRtlSupported whether the applications supports RTL (i.e. has supportsRtl=true in the
351      * manifest and targetSdkVersion >= 17.
352      */
353     @NonNull
createStatusBar(BridgeContext context, Density density, boolean isRtl, boolean isRtlSupported, boolean isEdgeToEdge, int simulatedPlatformVersion)354     private StatusBar createStatusBar(BridgeContext context, Density density, boolean isRtl,
355             boolean isRtlSupported, boolean isEdgeToEdge, int simulatedPlatformVersion) {
356         StatusBar statusBar = new StatusBar(context, density, isRtl, isRtlSupported, isEdgeToEdge,
357                 simulatedPlatformVersion);
358         statusBar.setId(getId(ID_STATUS_BAR));
359         WindowManager.LayoutParams layoutParams = statusBar.getBarLayoutParams();
360         mInsetsFrameProviders.addAll(Arrays.asList(layoutParams.providedInsets));
361         FrameLayout.LayoutParams lparams = new FrameLayout.LayoutParams(layoutParams);
362         lparams.gravity = layoutParams.gravity;
363         statusBar.setLayoutParams(lparams);
364         return statusBar;
365     }
366 
createActionBar(@onNull BridgeContext context, @NonNull SessionParams params, boolean appCompatActionBar, @Nullable View navBar, @Nullable StatusBar statusBar)367     private BridgeActionBar createActionBar(@NonNull BridgeContext context,
368             @NonNull SessionParams params, boolean appCompatActionBar, @Nullable View navBar,
369             @Nullable StatusBar statusBar) {
370         boolean isMenu = "menu".equals(params.getFlag(RenderParamsFlags.FLAG_KEY_ROOT_TAG));
371         String id;
372 
373         // For the framework action bar, we set the height to MATCH_PARENT only if there is no
374         // AppCompat ActionBar below it
375         int heightRule = appCompatActionBar || !mBuilder.hasAppCompatActionBar() ? MATCH_PARENT :
376                 WRAP_CONTENT;
377         RelativeLayout.LayoutParams layoutParams =
378                 createAppUiLayoutParams(MATCH_PARENT, heightRule);
379         if (navBar != null && mBuilder.hasSolidNavBar()) {
380             // If there
381             if (mBuilder.isNavBarVertical()) {
382                 layoutParams.rightMargin = navBar.getLayoutParams().width;
383             } else if (appCompatActionBar || !mBuilder.hasAppCompatActionBar()) {
384                 layoutParams.bottomMargin = navBar.getLayoutParams().height;
385             }
386         }
387 
388 
389         BridgeActionBar actionBar;
390         if (appCompatActionBar && !isMenu) {
391             actionBar = new AppCompatActionBar(context, params);
392             id = ID_APP_COMPAT_ACTION_BAR;
393 
394             if (mBuilder.hasTitleBar() || mBuilder.hasFrameworkActionBar()) {
395                 layoutParams.addRule(RelativeLayout.BELOW, getId(ID_FRAMEWORK_BAR));
396             } else if (statusBar != null && mBuilder.hasSolidStatusBar()) {
397                 layoutParams.topMargin = statusBar.getLayoutParams().height;
398             }
399         } else {
400             actionBar = new FrameworkActionBar(context, params);
401             id = ID_FRAMEWORK_BAR;
402             if (statusBar != null && mBuilder.hasSolidStatusBar()) {
403                 layoutParams.topMargin = statusBar.getLayoutParams().height;
404             }
405         }
406 
407         actionBar.getRootView().setLayoutParams(layoutParams);
408         actionBar.getRootView().setId(getId(id));
409         actionBar.createMenuPopup();
410         return actionBar;
411     }
412 
413     @NonNull
createTitleBar(BridgeContext context, String title, int simulatedPlatformVersion, @Nullable View navBar, @Nullable StatusBar statusBar)414     private TitleBar createTitleBar(BridgeContext context, String title,
415             int simulatedPlatformVersion, @Nullable View navBar, @Nullable StatusBar statusBar) {
416         TitleBar titleBar = new TitleBar(context, title, simulatedPlatformVersion);
417         RelativeLayout.LayoutParams params =
418                 createAppUiLayoutParams(MATCH_PARENT, mBuilder.mTitleBarSize);
419         if (statusBar != null && mBuilder.hasSolidStatusBar()) {
420             params.topMargin = statusBar.getLayoutParams().height;
421         }
422         if (navBar != null && mBuilder.isNavBarVertical() && mBuilder.hasSolidNavBar()) {
423             params.rightMargin = navBar.getLayoutParams().width;
424         }
425         titleBar.setLayoutParams(params);
426         titleBar.setId(getId(ID_FRAMEWORK_BAR));
427         return titleBar;
428     }
429 
430     /**
431      * @param useGestureNav whether the system UI is using gesture navigation.
432      * @param isRtl whether the current locale is an RTL locale.
433      * @param isRtlSupported whether the applications supports RTL (i.e. has supportsRtl=true in the
434      * manifest and targetSdkVersion >= 17.
435      */
436     @NonNull
createNavBar(BridgeContext context, boolean useGestureNav, Density density, boolean isRtl, boolean isRtlSupported, boolean isEdgeToEdge, int simulatedPlatformVersion, boolean isQuickStepEnabled)437     private View createNavBar(BridgeContext context, boolean useGestureNav, Density density,
438             boolean isRtl, boolean isRtlSupported, boolean isEdgeToEdge,
439             int simulatedPlatformVersion, boolean isQuickStepEnabled) {
440         int rotation = Surface.ROTATION_0;
441         // Only allow quickstep in the latest version or >= 28
442         isQuickStepEnabled = isQuickStepEnabled &&
443                 (simulatedPlatformVersion == 0 || simulatedPlatformVersion >= 28);
444         View navBar;
445         if (useGestureNav) {
446             navBar = new NavigationHandle(context);
447         } else {
448             navBar = new NavigationBar(context, density, mBuilder.mNavBarOrientation, isRtl,
449                     isRtlSupported, isEdgeToEdge, simulatedPlatformVersion, isQuickStepEnabled);
450             if (mBuilder.mNavBarOrientation == VERTICAL) {
451                 rotation = Surface.ROTATION_90;
452             }
453         }
454         WindowManager.LayoutParams layoutParams =
455                 getNavBarLayoutParamsForRotation(mBuilder.mContext, navBar, rotation);
456         mInsetsFrameProviders.addAll(Arrays.asList(layoutParams.providedInsets));
457         FrameLayout.LayoutParams lparams = new FrameLayout.LayoutParams(layoutParams);
458         lparams.gravity = layoutParams.gravity;
459         navBar.setLayoutParams(lparams);
460         navBar.setId(getId(ID_NAV_BAR));
461         return navBar;
462     }
463 
addAppUiViews(@onNull View... views)464     private void addAppUiViews(@NonNull View... views) {
465         for (View view : views) {
466             if (view != null) {
467                 mAppUiRoot.addView(view);
468             }
469         }
470     }
471 
getId(String name)472     private int getId(String name) {
473         return Bridge.getResourceId(ResourceType.ID, ID_PREFIX + name);
474     }
475 
476     @SuppressWarnings("deprecation")
477     @Override
requestFitSystemWindows()478     public void requestFitSystemWindows() {
479         // The framework call would usually bubble up to ViewRootImpl but, in layoutlib, Layout will
480         // act as view root for most purposes. That way, we can also save going through the Handler
481         // to dispatch the new applied insets.
482         ViewRootImpl root = AttachInfo_Accessor.getRootView(this);
483         if (root != null) {
484             ViewRootImpl_Accessor.dispatchApplyInsets(root, this);
485         }
486     }
487 
488     /**
489      * A helper class to help initialize the Layout.
490      */
491     static class Builder {
492         @NonNull
493         private final SessionParams mParams;
494         @NonNull
495         private final BridgeContext mContext;
496         private final RenderResources mResources;
497 
498         private final boolean mWindowIsFloating;
499         private ResourceValue mWindowBackground;
500         private int mStatusBarSize;
501         private int mNavBarSize;
502         private int mNavBarOrientation;
503         private int mAppCompatActionBarSize;
504         private int mFrameworkActionBarSize;
505         private int mTitleBarSize;
506         private boolean mTranslucentStatus;
507         private boolean mTranslucentNav;
508         private boolean mUseGestureNav;
509         private boolean mIsEdgeToEdge;
510         private boolean mShowCutout;
511 
Builder(@onNull SessionParams params, @NonNull BridgeContext context)512         public Builder(@NonNull SessionParams params, @NonNull BridgeContext context) {
513             mParams = params;
514             mContext = context;
515             mResources = mParams.getResources();
516             mWindowIsFloating =
517                     getBooleanThemeFrameworkAttrValue(mResources, ATTR_WINDOW_FLOATING, true);
518 
519             findBackground();
520 
521             if (!mParams.isForceNoDecor()) {
522                 mIsEdgeToEdge = isGreaterOrEqual(mParams.getSimulatedPlatformVersion(),
523                         VANILLA_ICE_CREAM) ||
524                         Boolean.TRUE.equals(mParams.getFlag(FLAG_KEY_EDGE_TO_EDGE));
525                 mShowCutout = Boolean.TRUE.equals(mParams.getFlag(FLAG_KEY_SHOW_CUTOUT));
526                 findStatusBar();
527                 findFrameworkBar();
528                 findAppCompatActionBar();
529                 findNavBar();
530             }
531         }
532 
findBackground()533         private void findBackground() {
534             if (!mParams.isTransparentBackground()) {
535                 mWindowBackground = mResources.findItemInTheme(
536                         BridgeContext.createFrameworkAttrReference(ATTR_WINDOW_BACKGROUND));
537                 mWindowBackground = mResources.resolveResValue(mWindowBackground);
538             }
539         }
540 
findStatusBar()541         private void findStatusBar() {
542             boolean windowFullScreen =
543                     getBooleanThemeFrameworkAttrValue(mResources, ATTR_WINDOW_FULL_SCREEN, false);
544             if (!windowFullScreen && !mWindowIsFloating) {
545                 mStatusBarSize =
546                         getFrameworkAttrDimension(ATTR_STATUS_BAR_HEIGHT, DEFAULT_STATUS_BAR_HEIGHT);
547                 mTranslucentStatus =
548                         getBooleanThemeFrameworkAttrValue(
549                                 mResources, ATTR_WINDOW_TRANSLUCENT_STATUS, false);
550             }
551         }
552 
553         /**
554          * The behavior is different whether the App is using AppCompat or not.
555          * <h1>With App compat :</h1>
556          * <li> framework ("android:") attributes have to effect
557          * <li> windowNoTile=true hides the AppCompatActionBar
558          * <li> windowActionBar=false throws an exception
559          */
findAppCompatActionBar()560         private void findAppCompatActionBar() {
561             if (mWindowIsFloating || !mContext.isAppCompatTheme()) {
562                 return;
563             }
564 
565             boolean windowNoTitle =
566                     getBooleanThemeValue(mResources,
567                             mContext.createAppCompatAttrReference(ATTR_WINDOW_NO_TITLE), false);
568 
569             boolean windowActionBar =
570                     getBooleanThemeValue(mResources,
571                             mContext.createAppCompatAttrReference(ATTR_WINDOW_ACTION_BAR), true);
572 
573             if (!windowNoTitle && windowActionBar) {
574                 mAppCompatActionBarSize =
575                         getDimension(mContext.createAppCompatAttrReference(ATTR_ACTION_BAR_SIZE),
576                                 DEFAULT_TITLE_BAR_HEIGHT);
577             }
578         }
579 
580         /**
581          * Find if we should show either the titleBar or the framework ActionBar
582          * <p>
583          * <h1> Without App compat :</h1>
584          * <li> windowNoTitle has no effect
585          * <li> android:windowNoTile=true hides the <b>ActionBar</b>
586          * <li> android:windowActionBar=true/false toggles between ActionBar/TitleBar
587          * </ul>
588          * <pre>
589          * +------------------------------------------------------------+
590          * |               |         android:windowNoTitle              |
591          * |android:       |    TRUE             |      FALSE           |
592          * |windowActionBar|---------------------+----------------------+
593          * |    TRUE       | Nothing             | ActionBar (Default)  |
594          * |    FALSE      | Nothing             | TitleBar             |
595          * +---------------+--------------------------------------------+
596          * </pre>
597          *
598          * @see #findAppCompatActionBar()
599          */
findFrameworkBar()600         private void findFrameworkBar() {
601             if (mWindowIsFloating) {
602                 return;
603             }
604             boolean frameworkWindowNoTitle =
605                     getBooleanThemeFrameworkAttrValue(mResources, ATTR_WINDOW_NO_TITLE, false);
606 
607             // Check if an actionbar is needed
608             boolean isMenu = "menu".equals(mParams.getFlag(RenderParamsFlags.FLAG_KEY_ROOT_TAG));
609 
610             boolean windowActionBar =
611                     getBooleanThemeFrameworkAttrValue(mResources, ATTR_WINDOW_ACTION_BAR, true);
612 
613             if (!frameworkWindowNoTitle || isMenu) {
614                 if (isMenu || windowActionBar) {
615                     mFrameworkActionBarSize =
616                             getFrameworkAttrDimension(ATTR_ACTION_BAR_SIZE, DEFAULT_TITLE_BAR_HEIGHT);
617                 } else {
618                     mTitleBarSize = getDimension(
619                             mContext.createAppCompatAttrReference(ATTR_WINDOW_TITLE_SIZE),
620                             DEFAULT_TITLE_BAR_HEIGHT);
621                 }
622             }
623         }
624 
findNavBar()625         private void findNavBar() {
626             if (hasSoftwareButtons() && !mWindowIsFloating) {
627                 mUseGestureNav = Boolean.TRUE.equals(mParams.getFlag(FLAG_KEY_USE_GESTURE_NAV));
628                 // get orientation
629                 HardwareConfig hwConfig = mParams.getHardwareConfig();
630                 boolean barOnBottom = true;
631 
632                 if (hwConfig.getOrientation() == ScreenOrientation.LANDSCAPE && !mUseGestureNav) {
633                     int shortSize = hwConfig.getScreenHeight();
634                     int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT /
635                             hwConfig.getDensity().getDpiValue();
636 
637                     // 0-599dp: "phone" UI with bar on the side
638                     // 600+dp: "tablet" UI with bar on the bottom
639                     barOnBottom = shortSizeDp >= 600;
640                 }
641 
642                 mNavBarOrientation = barOnBottom ? LinearLayout.HORIZONTAL : VERTICAL;
643                 mNavBarSize =
644                         getFrameworkAttrDimension(
645                                 barOnBottom ? ATTR_NAV_BAR_HEIGHT : ATTR_NAV_BAR_WIDTH,
646                                 DEFAULT_NAV_BAR_SIZE);
647                 mTranslucentNav =
648                         getBooleanThemeFrameworkAttrValue(mResources, ATTR_WINDOW_TRANSLUCENT_NAV,
649                                 false);
650             }
651         }
652 
getDimension(@onNull ResourceReference attrRef, int defaultValue)653         private int getDimension(@NonNull ResourceReference attrRef, int defaultValue) {
654             ResourceValue value = mResources.findItemInTheme(attrRef);
655             value = mResources.resolveResValue(value);
656             if (value != null) {
657                 TypedValue typedValue = ResourceHelper.getValue(attrRef.getName(), value.getValue(),
658                         true);
659                 if (typedValue != null) {
660                     return (int) typedValue.getDimension(mContext.getMetrics());
661                 }
662             }
663             return defaultValue;
664         }
665 
getFrameworkAttrDimension(@onNull String attr, int defaultValue)666         private int getFrameworkAttrDimension(@NonNull String attr, int defaultValue) {
667             return getDimension(BridgeContext.createFrameworkAttrReference(attr), defaultValue);
668         }
669 
hasSoftwareButtons()670         private boolean hasSoftwareButtons() {
671             return mParams.getHardwareConfig().hasSoftwareButtons();
672         }
673 
674         /**
675          * Returns true if the nav bar is present and not translucent.
676          */
hasSolidNavBar()677         private boolean hasSolidNavBar() {
678             return hasNavBar() && !mTranslucentNav && !mIsEdgeToEdge;
679         }
680 
681         /**
682          * Returns true if the status bar is present and not translucent.
683          */
hasSolidStatusBar()684         private boolean hasSolidStatusBar() {
685             return hasStatusBar() && !mTranslucentStatus && !mIsEdgeToEdge;
686         }
687 
hasNavBar()688         private boolean hasNavBar() {
689             return Config.showOnScreenNavBar(mParams.getSimulatedPlatformVersion()) &&
690                     hasSoftwareButtons() && mNavBarSize > 0;
691         }
692 
useGestureNav()693         private boolean useGestureNav() {
694             return mUseGestureNav;
695         }
696 
hasTitleBar()697         private boolean hasTitleBar() {
698             return mTitleBarSize > 0;
699         }
700 
hasStatusBar()701         private boolean hasStatusBar() {
702             return mStatusBarSize > 0;
703         }
704 
hasAppCompatActionBar()705         private boolean hasAppCompatActionBar() {
706             return mAppCompatActionBarSize > 0;
707         }
708 
709         /**
710          * Return true if the nav bar is present and is vertical.
711          */
isNavBarVertical()712         private boolean isNavBarVertical() {
713             return hasNavBar() && mNavBarOrientation == VERTICAL;
714         }
715 
hasFrameworkActionBar()716         private boolean hasFrameworkActionBar() {
717             return mFrameworkActionBarSize > 0;
718         }
719 
hasNotch()720         private boolean hasNotch() {
721             return !mParams.isForceNoDecor();
722         }
723     }
724 }
725