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