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