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.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.InsetsSource; 49 import android.view.InsetsState; 50 import android.view.Surface; 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 and 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 /** @return whether the navigation bar will change sides during rotation. */ navigationBarCanMove()304 public boolean navigationBarCanMove() { 305 return mNavigationBarCanMove; 306 } 307 308 /** @return the rotation that would make the physical display "upside down". */ getUpsideDownRotation()309 public int getUpsideDownRotation() { 310 boolean displayHardwareIsLandscape = mWidth > mHeight; 311 if ((mRotation % 2) != 0) { 312 displayHardwareIsLandscape = !displayHardwareIsLandscape; 313 } 314 if (displayHardwareIsLandscape) { 315 return mReverseDefaultRotation ? Surface.ROTATION_270 : Surface.ROTATION_90; 316 } 317 return Surface.ROTATION_180; 318 } 319 320 /** Gets the orientation of this layout */ getOrientation()321 public int getOrientation() { 322 return (mWidth > mHeight) ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; 323 } 324 325 /** Gets the calculated stable-bounds for this layout */ getStableBounds(Rect outBounds)326 public void getStableBounds(Rect outBounds) { 327 outBounds.set(0, 0, mWidth, mHeight); 328 outBounds.inset(mStableInsets); 329 } 330 331 /** 332 * Gets navigation bar position for this layout 333 * @return Navigation bar position for this layout. 334 */ getNavigationBarPosition(Resources res)335 public @NavBarPosition int getNavigationBarPosition(Resources res) { 336 return navigationBarPosition(res, mWidth, mHeight, mRotation); 337 } 338 339 /** 340 * Calculates the stable insets if we already have the non-decor insets. 341 */ convertNonDecorInsetsToStableInsets(Resources res, Rect inOutInsets, DisplayCutout cutout, boolean hasStatusBar)342 private void convertNonDecorInsetsToStableInsets(Resources res, Rect inOutInsets, 343 DisplayCutout cutout, boolean hasStatusBar) { 344 if (!hasStatusBar) { 345 return; 346 } 347 int statusBarHeight = SystemBarUtils.getStatusBarHeight(res, cutout); 348 inOutInsets.top = Math.max(inOutInsets.top, statusBarHeight); 349 } 350 351 /** 352 * Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system 353 * bar or button bar. 354 * 355 * @param displayRotation the current display rotation 356 * @param displayWidth the current display width 357 * @param displayHeight the current display height 358 * @param displayCutout the current display cutout 359 * @param outInsets the insets to return 360 */ computeNonDecorInsets(Resources res, int displayRotation, int displayWidth, int displayHeight, DisplayCutout displayCutout, InsetsState insetsState, int uiMode, Rect outInsets, boolean hasNavigationBar)361 static void computeNonDecorInsets(Resources res, int displayRotation, int displayWidth, 362 int displayHeight, DisplayCutout displayCutout, InsetsState insetsState, int uiMode, 363 Rect outInsets, boolean hasNavigationBar) { 364 outInsets.setEmpty(); 365 366 // Only navigation bar 367 if (hasNavigationBar) { 368 final InsetsSource extraNavBar = insetsState.getSource(ITYPE_EXTRA_NAVIGATION_BAR); 369 final boolean hasExtraNav = extraNavBar != null && extraNavBar.isVisible(); 370 int position = navigationBarPosition(res, displayWidth, displayHeight, displayRotation); 371 int navBarSize = 372 getNavigationBarSize(res, position, displayWidth > displayHeight, uiMode); 373 if (position == NAV_BAR_BOTTOM) { 374 outInsets.bottom = hasExtraNav 375 ? Math.max(navBarSize, extraNavBar.getFrame().height()) 376 : navBarSize; 377 } else if (position == NAV_BAR_RIGHT) { 378 outInsets.right = hasExtraNav 379 ? Math.max(navBarSize, extraNavBar.getFrame().width()) 380 : navBarSize; 381 } else if (position == NAV_BAR_LEFT) { 382 outInsets.left = hasExtraNav 383 ? Math.max(navBarSize, extraNavBar.getFrame().width()) 384 : navBarSize; 385 } 386 } 387 388 if (displayCutout != null) { 389 outInsets.left += displayCutout.getSafeInsetLeft(); 390 outInsets.top += displayCutout.getSafeInsetTop(); 391 outInsets.right += displayCutout.getSafeInsetRight(); 392 outInsets.bottom += displayCutout.getSafeInsetBottom(); 393 } 394 } 395 396 /** Calculate the DisplayCutout for a particular display size/rotation. */ calculateDisplayCutoutForRotation( DisplayCutout cutout, int rotation, int displayWidth, int displayHeight)397 public static DisplayCutout calculateDisplayCutoutForRotation( 398 DisplayCutout cutout, int rotation, int displayWidth, int displayHeight) { 399 if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) { 400 return null; 401 } 402 if (rotation == ROTATION_0) { 403 return computeSafeInsets(cutout, displayWidth, displayHeight); 404 } 405 final Insets waterfallInsets = rotateInsets(cutout.getWaterfallInsets(), rotation); 406 final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); 407 Rect[] cutoutRects = cutout.getBoundingRectsAll(); 408 final Rect[] newBounds = new Rect[cutoutRects.length]; 409 final Rect displayBounds = new Rect(0, 0, displayWidth, displayHeight); 410 for (int i = 0; i < cutoutRects.length; ++i) { 411 final Rect rect = new Rect(cutoutRects[i]); 412 if (!rect.isEmpty()) { 413 rotateBounds(rect, displayBounds, rotation); 414 } 415 newBounds[getBoundIndexFromRotation(i, rotation)] = rect; 416 } 417 final DisplayCutout.CutoutPathParserInfo info = cutout.getCutoutPathParserInfo(); 418 final DisplayCutout.CutoutPathParserInfo newInfo = new DisplayCutout.CutoutPathParserInfo( 419 info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(), 420 info.getCutoutSpec(), rotation, info.getScale()); 421 return computeSafeInsets( 422 DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo), 423 rotated ? displayHeight : displayWidth, 424 rotated ? displayWidth : displayHeight); 425 } 426 getBoundIndexFromRotation(int index, int rotation)427 private static int getBoundIndexFromRotation(int index, int rotation) { 428 return (index - rotation) < 0 429 ? index - rotation + DisplayCutout.BOUNDS_POSITION_LENGTH 430 : index - rotation; 431 } 432 433 /** Calculate safe insets. */ computeSafeInsets(DisplayCutout inner, int displayWidth, int displayHeight)434 public static DisplayCutout computeSafeInsets(DisplayCutout inner, 435 int displayWidth, int displayHeight) { 436 if (inner == DisplayCutout.NO_CUTOUT) { 437 return null; 438 } 439 440 final Size displaySize = new Size(displayWidth, displayHeight); 441 final Rect safeInsets = computeSafeInsets(displaySize, inner); 442 return inner.replaceSafeInsets(safeInsets); 443 } 444 computeSafeInsets( Size displaySize, DisplayCutout cutout)445 private static Rect computeSafeInsets( 446 Size displaySize, DisplayCutout cutout) { 447 if (displaySize.getWidth() == displaySize.getHeight()) { 448 throw new UnsupportedOperationException("not implemented: display=" + displaySize 449 + " cutout=" + cutout); 450 } 451 452 int leftInset = Math.max(cutout.getWaterfallInsets().left, 453 findCutoutInsetForSide(displaySize, cutout.getBoundingRectLeft(), Gravity.LEFT)); 454 int topInset = Math.max(cutout.getWaterfallInsets().top, 455 findCutoutInsetForSide(displaySize, cutout.getBoundingRectTop(), Gravity.TOP)); 456 int rightInset = Math.max(cutout.getWaterfallInsets().right, 457 findCutoutInsetForSide(displaySize, cutout.getBoundingRectRight(), Gravity.RIGHT)); 458 int bottomInset = Math.max(cutout.getWaterfallInsets().bottom, 459 findCutoutInsetForSide(displaySize, cutout.getBoundingRectBottom(), 460 Gravity.BOTTOM)); 461 462 return new Rect(leftInset, topInset, rightInset, bottomInset); 463 } 464 findCutoutInsetForSide(Size display, Rect boundingRect, int gravity)465 private static int findCutoutInsetForSide(Size display, Rect boundingRect, int gravity) { 466 if (boundingRect.isEmpty()) { 467 return 0; 468 } 469 470 int inset = 0; 471 switch (gravity) { 472 case Gravity.TOP: 473 return Math.max(inset, boundingRect.bottom); 474 case Gravity.BOTTOM: 475 return Math.max(inset, display.getHeight() - boundingRect.top); 476 case Gravity.LEFT: 477 return Math.max(inset, boundingRect.right); 478 case Gravity.RIGHT: 479 return Math.max(inset, display.getWidth() - boundingRect.left); 480 default: 481 throw new IllegalArgumentException("unknown gravity: " + gravity); 482 } 483 } 484 hasNavigationBar(DisplayInfo info, Context context, int displayId)485 static boolean hasNavigationBar(DisplayInfo info, Context context, int displayId) { 486 if (displayId == Display.DEFAULT_DISPLAY) { 487 // Allow a system property to override this. Used by the emulator. 488 final String navBarOverride = SystemProperties.get("qemu.hw.mainkeys"); 489 if ("1".equals(navBarOverride)) { 490 return false; 491 } else if ("0".equals(navBarOverride)) { 492 return true; 493 } 494 return context.getResources().getBoolean(R.bool.config_showNavigationBar); 495 } else { 496 boolean isUntrustedVirtualDisplay = info.type == Display.TYPE_VIRTUAL 497 && info.ownerUid != SYSTEM_UID; 498 final ContentResolver resolver = context.getContentResolver(); 499 boolean forceDesktopOnExternal = Settings.Global.getInt(resolver, 500 DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0; 501 502 return ((info.flags & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0 503 || (forceDesktopOnExternal && !isUntrustedVirtualDisplay)); 504 // TODO(b/142569966): make sure VR2D and DisplayWindowSettings are moved here somehow. 505 } 506 } 507 hasStatusBar(int displayId)508 static boolean hasStatusBar(int displayId) { 509 return displayId == Display.DEFAULT_DISPLAY; 510 } 511 512 /** Retrieve navigation bar position from resources based on rotation and size. */ navigationBarPosition(Resources res, int displayWidth, int displayHeight, int rotation)513 public static @NavBarPosition int navigationBarPosition(Resources res, int displayWidth, 514 int displayHeight, int rotation) { 515 boolean navBarCanMove = displayWidth != displayHeight && res.getBoolean( 516 com.android.internal.R.bool.config_navBarCanMove); 517 if (navBarCanMove && displayWidth > displayHeight) { 518 if (rotation == Surface.ROTATION_90) { 519 return NAV_BAR_RIGHT; 520 } else { 521 return NAV_BAR_LEFT; 522 } 523 } 524 return NAV_BAR_BOTTOM; 525 } 526 527 /** Retrieve navigation bar size from resources based on side/orientation/ui-mode */ getNavigationBarSize(Resources res, int navBarSide, boolean landscape, int uiMode)528 public static int getNavigationBarSize(Resources res, int navBarSide, boolean landscape, 529 int uiMode) { 530 final boolean carMode = (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR; 531 if (carMode) { 532 if (navBarSide == NAV_BAR_BOTTOM) { 533 return res.getDimensionPixelSize(landscape 534 ? R.dimen.navigation_bar_height_landscape_car_mode 535 : R.dimen.navigation_bar_height_car_mode); 536 } else { 537 return res.getDimensionPixelSize(R.dimen.navigation_bar_width_car_mode); 538 } 539 540 } else { 541 if (navBarSide == NAV_BAR_BOTTOM) { 542 return res.getDimensionPixelSize(landscape 543 ? R.dimen.navigation_bar_height_landscape 544 : R.dimen.navigation_bar_height); 545 } else { 546 return res.getDimensionPixelSize(R.dimen.navigation_bar_width); 547 } 548 } 549 } 550 551 /** @see com.android.server.wm.DisplayPolicy#getNavigationBarFrameHeight */ getNavigationBarFrameHeight(Resources res, boolean landscape)552 public static int getNavigationBarFrameHeight(Resources res, boolean landscape) { 553 return res.getDimensionPixelSize(landscape 554 ? R.dimen.navigation_bar_frame_height_landscape 555 : R.dimen.navigation_bar_frame_height); 556 } 557 } 558