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