1 /* 2 * Copyright (C) 2019 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.wm.shell.common; 18 19 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; 20 import static android.content.res.Configuration.ORIENTATION_PORTRAIT; 21 import static android.content.res.Configuration.UI_MODE_TYPE_CAR; 22 import static android.content.res.Configuration.UI_MODE_TYPE_MASK; 23 import static android.os.Process.SYSTEM_UID; 24 import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; 25 import static android.util.RotationUtils.rotateBounds; 26 import static android.util.RotationUtils.rotateInsets; 27 import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; 28 import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR; 29 import static android.view.Surface.ROTATION_0; 30 import static android.view.Surface.ROTATION_270; 31 import static android.view.Surface.ROTATION_90; 32 33 import android.annotation.IntDef; 34 import android.annotation.NonNull; 35 import android.annotation.Nullable; 36 import android.content.ContentResolver; 37 import android.content.Context; 38 import android.content.res.Resources; 39 import android.graphics.Insets; 40 import android.graphics.Rect; 41 import android.os.SystemProperties; 42 import android.provider.Settings; 43 import android.util.DisplayMetrics; 44 import android.util.Size; 45 import android.view.Display; 46 import android.view.DisplayCutout; 47 import android.view.DisplayInfo; 48 import android.view.Gravity; 49 import android.view.InsetsSource; 50 import android.view.InsetsState; 51 import android.view.Surface; 52 53 import androidx.annotation.VisibleForTesting; 54 55 import com.android.internal.R; 56 import com.android.internal.policy.SystemBarUtils; 57 58 import java.lang.annotation.Retention; 59 import java.lang.annotation.RetentionPolicy; 60 import java.util.Objects; 61 62 /** 63 * Contains information about the layout-properties of a display. This refers to internal layout 64 * like insets/cutout/rotation. In general, this can be thought of as the shell analog to 65 * DisplayPolicy. 66 */ 67 public class DisplayLayout { 68 @IntDef(prefix = { "NAV_BAR_" }, value = { 69 NAV_BAR_LEFT, 70 NAV_BAR_RIGHT, 71 NAV_BAR_BOTTOM, 72 }) 73 @Retention(RetentionPolicy.SOURCE) 74 public @interface NavBarPosition {} 75 76 // Navigation bar position values 77 public static final int NAV_BAR_LEFT = 1 << 0; 78 public static final int NAV_BAR_RIGHT = 1 << 1; 79 public static final int NAV_BAR_BOTTOM = 1 << 2; 80 81 private int mUiMode; 82 private int mWidth; 83 private int mHeight; 84 private DisplayCutout mCutout; 85 private int mRotation; 86 private int mDensityDpi; 87 private final Rect mNonDecorInsets = new Rect(); 88 private final Rect mStableInsets = new Rect(); 89 private boolean mHasNavigationBar = false; 90 private boolean mHasStatusBar = false; 91 private int mNavBarFrameHeight = 0; 92 private boolean mAllowSeamlessRotationDespiteNavBarMoving = false; 93 private boolean mNavigationBarCanMove = false; 94 private boolean mReverseDefaultRotation = false; 95 private InsetsState mInsetsState = new InsetsState(); 96 97 /** 98 * Different from {@link #equals(Object)}, this method compares the basic geometry properties 99 * of two {@link DisplayLayout} objects including width, height, rotation, density, cutout. 100 * @return {@code true} if the given {@link DisplayLayout} is identical geometry wise. 101 */ isSameGeometry(@onNull DisplayLayout other)102 public boolean isSameGeometry(@NonNull DisplayLayout other) { 103 return mWidth == other.mWidth 104 && mHeight == other.mHeight 105 && mRotation == other.mRotation 106 && mDensityDpi == other.mDensityDpi 107 && Objects.equals(mCutout, other.mCutout); 108 } 109 110 @Override equals(Object o)111 public boolean equals(Object o) { 112 if (this == o) return true; 113 if (!(o instanceof DisplayLayout)) return false; 114 final DisplayLayout other = (DisplayLayout) o; 115 return mUiMode == other.mUiMode 116 && mWidth == other.mWidth 117 && mHeight == other.mHeight 118 && Objects.equals(mCutout, other.mCutout) 119 && mRotation == other.mRotation 120 && mDensityDpi == other.mDensityDpi 121 && Objects.equals(mNonDecorInsets, other.mNonDecorInsets) 122 && Objects.equals(mStableInsets, other.mStableInsets) 123 && mHasNavigationBar == other.mHasNavigationBar 124 && mHasStatusBar == other.mHasStatusBar 125 && mAllowSeamlessRotationDespiteNavBarMoving 126 == other.mAllowSeamlessRotationDespiteNavBarMoving 127 && mNavigationBarCanMove == other.mNavigationBarCanMove 128 && mReverseDefaultRotation == other.mReverseDefaultRotation 129 && mNavBarFrameHeight == other.mNavBarFrameHeight 130 && Objects.equals(mInsetsState, other.mInsetsState); 131 } 132 133 @Override hashCode()134 public int hashCode() { 135 return Objects.hash(mUiMode, mWidth, mHeight, mCutout, mRotation, mDensityDpi, 136 mNonDecorInsets, mStableInsets, mHasNavigationBar, mHasStatusBar, 137 mNavBarFrameHeight, mAllowSeamlessRotationDespiteNavBarMoving, 138 mNavigationBarCanMove, mReverseDefaultRotation, mInsetsState); 139 } 140 141 /** 142 * Create empty layout. 143 */ DisplayLayout()144 public DisplayLayout() { 145 } 146 147 /** 148 * Construct a custom display layout using a DisplayInfo. 149 * @param info 150 * @param res 151 */ DisplayLayout(DisplayInfo info, Resources res, boolean hasNavigationBar, boolean hasStatusBar)152 public DisplayLayout(DisplayInfo info, Resources res, boolean hasNavigationBar, 153 boolean hasStatusBar) { 154 init(info, res, hasNavigationBar, hasStatusBar); 155 } 156 157 /** 158 * Construct a display layout based on a live display. 159 * @param context Used for resources. 160 */ DisplayLayout(@onNull Context context, @NonNull Display rawDisplay)161 public DisplayLayout(@NonNull Context context, @NonNull Display rawDisplay) { 162 final int displayId = rawDisplay.getDisplayId(); 163 DisplayInfo info = new DisplayInfo(); 164 rawDisplay.getDisplayInfo(info); 165 init(info, context.getResources(), hasNavigationBar(info, context, displayId), 166 hasStatusBar(displayId)); 167 } 168 DisplayLayout(DisplayLayout dl)169 public DisplayLayout(DisplayLayout dl) { 170 set(dl); 171 } 172 173 /** sets this DisplayLayout to a copy of another on. */ set(DisplayLayout dl)174 public void set(DisplayLayout dl) { 175 mUiMode = dl.mUiMode; 176 mWidth = dl.mWidth; 177 mHeight = dl.mHeight; 178 mCutout = dl.mCutout; 179 mRotation = dl.mRotation; 180 mDensityDpi = dl.mDensityDpi; 181 mHasNavigationBar = dl.mHasNavigationBar; 182 mHasStatusBar = dl.mHasStatusBar; 183 mAllowSeamlessRotationDespiteNavBarMoving = dl.mAllowSeamlessRotationDespiteNavBarMoving; 184 mNavigationBarCanMove = dl.mNavigationBarCanMove; 185 mReverseDefaultRotation = dl.mReverseDefaultRotation; 186 mNavBarFrameHeight = dl.mNavBarFrameHeight; 187 mNonDecorInsets.set(dl.mNonDecorInsets); 188 mStableInsets.set(dl.mStableInsets); 189 mInsetsState.set(dl.mInsetsState, true /* copySources */); 190 } 191 init(DisplayInfo info, Resources res, boolean hasNavigationBar, boolean hasStatusBar)192 private void init(DisplayInfo info, Resources res, boolean hasNavigationBar, 193 boolean hasStatusBar) { 194 mUiMode = res.getConfiguration().uiMode; 195 mWidth = info.logicalWidth; 196 mHeight = info.logicalHeight; 197 mRotation = info.rotation; 198 mCutout = info.displayCutout; 199 mDensityDpi = info.logicalDensityDpi; 200 mHasNavigationBar = hasNavigationBar; 201 mHasStatusBar = hasStatusBar; 202 mAllowSeamlessRotationDespiteNavBarMoving = res.getBoolean( 203 R.bool.config_allowSeamlessRotationDespiteNavBarMoving); 204 mNavigationBarCanMove = res.getBoolean(R.bool.config_navBarCanMove); 205 mReverseDefaultRotation = res.getBoolean(R.bool.config_reverseDefaultRotation); 206 recalcInsets(res); 207 } 208 209 /** 210 * Updates the current insets. 211 */ setInsets(Resources res, InsetsState state)212 public void setInsets(Resources res, InsetsState state) { 213 mInsetsState = state; 214 recalcInsets(res); 215 } 216 217 @VisibleForTesting recalcInsets(Resources res)218 void recalcInsets(Resources res) { 219 computeNonDecorInsets(res, mRotation, mWidth, mHeight, mCutout, mInsetsState, mUiMode, 220 mNonDecorInsets, mHasNavigationBar); 221 mStableInsets.set(mNonDecorInsets); 222 if (mHasStatusBar) { 223 convertNonDecorInsetsToStableInsets(res, mStableInsets, mCutout, mHasStatusBar); 224 } 225 mNavBarFrameHeight = getNavigationBarFrameHeight(res, mWidth > mHeight); 226 } 227 228 /** 229 * Apply a rotation to this layout and its parameters. 230 * @param res 231 * @param targetRotation 232 */ rotateTo(Resources res, @Surface.Rotation int targetRotation)233 public void rotateTo(Resources res, @Surface.Rotation int targetRotation) { 234 final int rotationDelta = (targetRotation - mRotation + 4) % 4; 235 final boolean changeOrient = (rotationDelta % 2) != 0; 236 237 final int origWidth = mWidth; 238 final int origHeight = mHeight; 239 240 mRotation = targetRotation; 241 if (changeOrient) { 242 mWidth = origHeight; 243 mHeight = origWidth; 244 } 245 246 if (mCutout != null && !mCutout.isEmpty()) { 247 mCutout = calculateDisplayCutoutForRotation(mCutout, rotationDelta, origWidth, 248 origHeight); 249 } 250 251 recalcInsets(res); 252 } 253 254 /** Get this layout's non-decor insets. */ nonDecorInsets()255 public Rect nonDecorInsets() { 256 return mNonDecorInsets; 257 } 258 259 /** Get this layout's stable insets. */ stableInsets()260 public Rect stableInsets() { 261 return mStableInsets; 262 } 263 264 /** Get this layout's width. */ width()265 public int width() { 266 return mWidth; 267 } 268 269 /** Get this layout's height. */ height()270 public int height() { 271 return mHeight; 272 } 273 274 /** Get this layout's display rotation. */ rotation()275 public int rotation() { 276 return mRotation; 277 } 278 279 /** Get this layout's display density. */ densityDpi()280 public int densityDpi() { 281 return mDensityDpi; 282 } 283 284 /** Get the density scale for the display. */ density()285 public float density() { 286 return mDensityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE; 287 } 288 289 /** Get whether this layout is landscape. */ isLandscape()290 public boolean isLandscape() { 291 return mWidth > mHeight; 292 } 293 294 /** Get the navbar frame (or window) height (used by ime). */ navBarFrameHeight()295 public int navBarFrameHeight() { 296 return mNavBarFrameHeight; 297 } 298 299 /** @return whether we can seamlessly rotate even if nav-bar can change sides. */ allowSeamlessRotationDespiteNavBarMoving()300 public boolean allowSeamlessRotationDespiteNavBarMoving() { 301 return mAllowSeamlessRotationDespiteNavBarMoving; 302 } 303 304 /** @return whether the navigation bar will change sides during rotation. */ navigationBarCanMove()305 public boolean navigationBarCanMove() { 306 return mNavigationBarCanMove; 307 } 308 309 /** @return the rotation that would make the physical display "upside down". */ getUpsideDownRotation()310 public int getUpsideDownRotation() { 311 boolean displayHardwareIsLandscape = mWidth > mHeight; 312 if ((mRotation % 2) != 0) { 313 displayHardwareIsLandscape = !displayHardwareIsLandscape; 314 } 315 if (displayHardwareIsLandscape) { 316 return mReverseDefaultRotation ? Surface.ROTATION_270 : Surface.ROTATION_90; 317 } 318 return Surface.ROTATION_180; 319 } 320 321 /** Gets the orientation of this layout */ getOrientation()322 public int getOrientation() { 323 return (mWidth > mHeight) ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; 324 } 325 326 /** Gets the calculated stable-bounds for this layout */ getStableBounds(Rect outBounds)327 public void getStableBounds(Rect outBounds) { 328 outBounds.set(0, 0, mWidth, mHeight); 329 outBounds.inset(mStableInsets); 330 } 331 332 /** 333 * Gets navigation bar position for this layout 334 * @return Navigation bar position for this layout. 335 */ getNavigationBarPosition(Resources res)336 public @NavBarPosition int getNavigationBarPosition(Resources res) { 337 return navigationBarPosition(res, mWidth, mHeight, mRotation); 338 } 339 340 /** @return {@link DisplayCutout} instance. */ 341 @Nullable getDisplayCutout()342 public DisplayCutout getDisplayCutout() { 343 return mCutout; 344 } 345 346 /** 347 * Calculates the stable insets if we already have the non-decor insets. 348 */ convertNonDecorInsetsToStableInsets(Resources res, Rect inOutInsets, DisplayCutout cutout, boolean hasStatusBar)349 private void convertNonDecorInsetsToStableInsets(Resources res, Rect inOutInsets, 350 DisplayCutout cutout, boolean hasStatusBar) { 351 if (!hasStatusBar) { 352 return; 353 } 354 int statusBarHeight = SystemBarUtils.getStatusBarHeight(res, cutout); 355 inOutInsets.top = Math.max(inOutInsets.top, statusBarHeight); 356 } 357 358 /** 359 * Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system 360 * bar or button bar. 361 * 362 * @param displayRotation the current display rotation 363 * @param displayWidth the current display width 364 * @param displayHeight the current display height 365 * @param displayCutout the current display cutout 366 * @param outInsets the insets to return 367 */ computeNonDecorInsets(Resources res, int displayRotation, int displayWidth, int displayHeight, DisplayCutout displayCutout, InsetsState insetsState, int uiMode, Rect outInsets, boolean hasNavigationBar)368 static void computeNonDecorInsets(Resources res, int displayRotation, int displayWidth, 369 int displayHeight, DisplayCutout displayCutout, InsetsState insetsState, int uiMode, 370 Rect outInsets, boolean hasNavigationBar) { 371 outInsets.setEmpty(); 372 373 // Only navigation bar 374 if (hasNavigationBar) { 375 final InsetsSource extraNavBar = insetsState.getSource(ITYPE_EXTRA_NAVIGATION_BAR); 376 final boolean hasExtraNav = extraNavBar != null && extraNavBar.isVisible(); 377 int position = navigationBarPosition(res, displayWidth, displayHeight, displayRotation); 378 int navBarSize = 379 getNavigationBarSize(res, position, displayWidth > displayHeight, uiMode); 380 if (position == NAV_BAR_BOTTOM) { 381 outInsets.bottom = hasExtraNav 382 ? Math.max(navBarSize, extraNavBar.getFrame().height()) 383 : navBarSize; 384 } else if (position == NAV_BAR_RIGHT) { 385 outInsets.right = hasExtraNav 386 ? Math.max(navBarSize, extraNavBar.getFrame().width()) 387 : navBarSize; 388 } else if (position == NAV_BAR_LEFT) { 389 outInsets.left = hasExtraNav 390 ? Math.max(navBarSize, extraNavBar.getFrame().width()) 391 : navBarSize; 392 } 393 } 394 395 if (displayCutout != null) { 396 outInsets.left += displayCutout.getSafeInsetLeft(); 397 outInsets.top += displayCutout.getSafeInsetTop(); 398 outInsets.right += displayCutout.getSafeInsetRight(); 399 outInsets.bottom += displayCutout.getSafeInsetBottom(); 400 } 401 } 402 403 /** Calculate the DisplayCutout for a particular display size/rotation. */ calculateDisplayCutoutForRotation( DisplayCutout cutout, int rotation, int displayWidth, int displayHeight)404 public static DisplayCutout calculateDisplayCutoutForRotation( 405 DisplayCutout cutout, int rotation, int displayWidth, int displayHeight) { 406 if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) { 407 return null; 408 } 409 if (rotation == ROTATION_0) { 410 return computeSafeInsets(cutout, displayWidth, displayHeight); 411 } 412 final Insets waterfallInsets = rotateInsets(cutout.getWaterfallInsets(), rotation); 413 final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); 414 Rect[] cutoutRects = cutout.getBoundingRectsAll(); 415 final Rect[] newBounds = new Rect[cutoutRects.length]; 416 final Rect displayBounds = new Rect(0, 0, displayWidth, displayHeight); 417 for (int i = 0; i < cutoutRects.length; ++i) { 418 final Rect rect = new Rect(cutoutRects[i]); 419 if (!rect.isEmpty()) { 420 rotateBounds(rect, displayBounds, rotation); 421 } 422 newBounds[getBoundIndexFromRotation(i, rotation)] = rect; 423 } 424 final DisplayCutout.CutoutPathParserInfo info = cutout.getCutoutPathParserInfo(); 425 final DisplayCutout.CutoutPathParserInfo newInfo = new DisplayCutout.CutoutPathParserInfo( 426 info.getDisplayWidth(), info.getDisplayHeight(), info.getPhysicalDisplayWidth(), 427 info.getPhysicalDisplayHeight(), info.getDensity(), info.getCutoutSpec(), rotation, 428 info.getScale(), info.getPhysicalPixelDisplaySizeRatio()); 429 return computeSafeInsets( 430 DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo), 431 rotated ? displayHeight : displayWidth, 432 rotated ? displayWidth : displayHeight); 433 } 434 getBoundIndexFromRotation(int index, int rotation)435 private static int getBoundIndexFromRotation(int index, int rotation) { 436 return (index - rotation) < 0 437 ? index - rotation + DisplayCutout.BOUNDS_POSITION_LENGTH 438 : index - rotation; 439 } 440 441 /** Calculate safe insets. */ computeSafeInsets(DisplayCutout inner, int displayWidth, int displayHeight)442 public static DisplayCutout computeSafeInsets(DisplayCutout inner, 443 int displayWidth, int displayHeight) { 444 if (inner == DisplayCutout.NO_CUTOUT) { 445 return null; 446 } 447 448 final Size displaySize = new Size(displayWidth, displayHeight); 449 final Rect safeInsets = computeSafeInsets(displaySize, inner); 450 return inner.replaceSafeInsets(safeInsets); 451 } 452 computeSafeInsets( Size displaySize, DisplayCutout cutout)453 private static Rect computeSafeInsets( 454 Size displaySize, DisplayCutout cutout) { 455 if (displaySize.getWidth() == displaySize.getHeight()) { 456 throw new UnsupportedOperationException("not implemented: display=" + displaySize 457 + " cutout=" + cutout); 458 } 459 460 int leftInset = Math.max(cutout.getWaterfallInsets().left, 461 findCutoutInsetForSide(displaySize, cutout.getBoundingRectLeft(), Gravity.LEFT)); 462 int topInset = Math.max(cutout.getWaterfallInsets().top, 463 findCutoutInsetForSide(displaySize, cutout.getBoundingRectTop(), Gravity.TOP)); 464 int rightInset = Math.max(cutout.getWaterfallInsets().right, 465 findCutoutInsetForSide(displaySize, cutout.getBoundingRectRight(), Gravity.RIGHT)); 466 int bottomInset = Math.max(cutout.getWaterfallInsets().bottom, 467 findCutoutInsetForSide(displaySize, cutout.getBoundingRectBottom(), 468 Gravity.BOTTOM)); 469 470 return new Rect(leftInset, topInset, rightInset, bottomInset); 471 } 472 findCutoutInsetForSide(Size display, Rect boundingRect, int gravity)473 private static int findCutoutInsetForSide(Size display, Rect boundingRect, int gravity) { 474 if (boundingRect.isEmpty()) { 475 return 0; 476 } 477 478 int inset = 0; 479 switch (gravity) { 480 case Gravity.TOP: 481 return Math.max(inset, boundingRect.bottom); 482 case Gravity.BOTTOM: 483 return Math.max(inset, display.getHeight() - boundingRect.top); 484 case Gravity.LEFT: 485 return Math.max(inset, boundingRect.right); 486 case Gravity.RIGHT: 487 return Math.max(inset, display.getWidth() - boundingRect.left); 488 default: 489 throw new IllegalArgumentException("unknown gravity: " + gravity); 490 } 491 } 492 hasNavigationBar(DisplayInfo info, Context context, int displayId)493 static boolean hasNavigationBar(DisplayInfo info, Context context, int displayId) { 494 if (displayId == Display.DEFAULT_DISPLAY) { 495 // Allow a system property to override this. Used by the emulator. 496 final String navBarOverride = SystemProperties.get("qemu.hw.mainkeys"); 497 if ("1".equals(navBarOverride)) { 498 return false; 499 } else if ("0".equals(navBarOverride)) { 500 return true; 501 } 502 return context.getResources().getBoolean(R.bool.config_showNavigationBar); 503 } else { 504 boolean isUntrustedVirtualDisplay = info.type == Display.TYPE_VIRTUAL 505 && info.ownerUid != SYSTEM_UID; 506 final ContentResolver resolver = context.getContentResolver(); 507 boolean forceDesktopOnExternal = Settings.Global.getInt(resolver, 508 DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0; 509 510 return ((info.flags & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0 511 || (forceDesktopOnExternal && !isUntrustedVirtualDisplay)); 512 // TODO(b/142569966): make sure VR2D and DisplayWindowSettings are moved here somehow. 513 } 514 } 515 hasStatusBar(int displayId)516 static boolean hasStatusBar(int displayId) { 517 return displayId == Display.DEFAULT_DISPLAY; 518 } 519 520 /** Retrieve navigation bar position from resources based on rotation and size. */ navigationBarPosition(Resources res, int displayWidth, int displayHeight, int rotation)521 public static @NavBarPosition int navigationBarPosition(Resources res, int displayWidth, 522 int displayHeight, int rotation) { 523 boolean navBarCanMove = displayWidth != displayHeight && res.getBoolean( 524 com.android.internal.R.bool.config_navBarCanMove); 525 if (navBarCanMove && displayWidth > displayHeight) { 526 if (rotation == Surface.ROTATION_90) { 527 return NAV_BAR_RIGHT; 528 } else { 529 return NAV_BAR_LEFT; 530 } 531 } 532 return NAV_BAR_BOTTOM; 533 } 534 535 /** Retrieve navigation bar size from resources based on side/orientation/ui-mode */ getNavigationBarSize(Resources res, int navBarSide, boolean landscape, int uiMode)536 public static int getNavigationBarSize(Resources res, int navBarSide, boolean landscape, 537 int uiMode) { 538 final boolean carMode = (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR; 539 if (carMode) { 540 if (navBarSide == NAV_BAR_BOTTOM) { 541 return res.getDimensionPixelSize(landscape 542 ? R.dimen.navigation_bar_height_landscape_car_mode 543 : R.dimen.navigation_bar_height_car_mode); 544 } else { 545 return res.getDimensionPixelSize(R.dimen.navigation_bar_width_car_mode); 546 } 547 548 } else { 549 if (navBarSide == NAV_BAR_BOTTOM) { 550 return res.getDimensionPixelSize(landscape 551 ? R.dimen.navigation_bar_height_landscape 552 : R.dimen.navigation_bar_height); 553 } else { 554 return res.getDimensionPixelSize(R.dimen.navigation_bar_width); 555 } 556 } 557 } 558 559 /** @see com.android.server.wm.DisplayPolicy#getNavigationBarFrameHeight */ getNavigationBarFrameHeight(Resources res, boolean landscape)560 public static int getNavigationBarFrameHeight(Resources res, boolean landscape) { 561 return res.getDimensionPixelSize(landscape 562 ? R.dimen.navigation_bar_frame_height_landscape 563 : R.dimen.navigation_bar_frame_height); 564 } 565 } 566