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