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