Loading packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java 0 → 100644 +474 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.wm; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.content.res.Configuration.UI_MODE_TYPE_CAR; import static android.content.res.Configuration.UI_MODE_TYPE_MASK; import static android.os.Process.SYSTEM_UID; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import android.annotation.NonNull; import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.graphics.Rect; import android.os.SystemProperties; import android.provider.Settings; import android.util.Size; import android.view.Display; import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.Gravity; import android.view.Surface; import com.android.internal.R; import java.util.List; /** * Contains information about the layout-properties of a display. This refers to internal layout * like insets/cutout/rotation. In general, this can be thought of as the System-UI analog to * DisplayPolicy. */ public class DisplayLayout { // Navigation bar position values private static final int NAV_BAR_LEFT = 1 << 0; private static final int NAV_BAR_RIGHT = 1 << 1; private static final int NAV_BAR_BOTTOM = 1 << 2; private int mUiMode; private int mWidth; private int mHeight; private DisplayCutout mCutout; private int mRotation; private int mDensityDpi; private final Rect mNonDecorInsets = new Rect(); private final Rect mStableInsets = new Rect(); private boolean mHasNavigationBar = false; private boolean mHasStatusBar = false; /** * Create empty layout. */ public DisplayLayout() { } /** * Construct a custom display layout using a DisplayInfo. * @param info * @param res */ public DisplayLayout(DisplayInfo info, Resources res, boolean hasNavigationBar, boolean hasStatusBar) { init(info, res, hasNavigationBar, hasStatusBar); } /** * Construct a display layout based on a live display. * @param context Used for resources. */ public DisplayLayout(@NonNull Context context, @NonNull Display rawDisplay) { final int displayId = rawDisplay.getDisplayId(); DisplayInfo info = new DisplayInfo(); rawDisplay.getDisplayInfo(info); init(info, context.getResources(), hasNavigationBar(info, context, displayId), hasStatusBar(displayId)); } public DisplayLayout(DisplayLayout dl) { set(dl); } /** sets this DisplayLayout to a copy of another on. */ public void set(DisplayLayout dl) { mUiMode = dl.mUiMode; mWidth = dl.mWidth; mHeight = dl.mHeight; mCutout = dl.mCutout; mRotation = dl.mRotation; mDensityDpi = dl.mDensityDpi; mHasNavigationBar = dl.mHasNavigationBar; mHasStatusBar = dl.mHasStatusBar; mNonDecorInsets.set(dl.mNonDecorInsets); mStableInsets.set(dl.mStableInsets); } private void init(DisplayInfo info, Resources res, boolean hasNavigationBar, boolean hasStatusBar) { mUiMode = res.getConfiguration().uiMode; mWidth = info.logicalWidth; mHeight = info.logicalHeight; mRotation = info.rotation; mCutout = info.displayCutout; mDensityDpi = info.logicalDensityDpi; mHasNavigationBar = hasNavigationBar; mHasStatusBar = hasStatusBar; recalcInsets(res); } private void recalcInsets(Resources res) { computeNonDecorInsets(res, mRotation, mWidth, mHeight, mCutout, mUiMode, mNonDecorInsets, mHasNavigationBar); mStableInsets.set(mNonDecorInsets); if (mHasStatusBar) { convertNonDecorInsetsToStableInsets(res, mStableInsets, mWidth, mHeight, mHasStatusBar); } } /** * Apply a rotation to this layout and its parameters. * @param res * @param targetRotation */ public void rotateTo(Resources res, @Surface.Rotation int targetRotation) { final int rotationDelta = (targetRotation - mRotation + 4) % 4; final boolean changeOrient = (rotationDelta % 2) != 0; final int origWidth = mWidth; final int origHeight = mHeight; mRotation = targetRotation; if (changeOrient) { mWidth = origHeight; mHeight = origWidth; } if (mCutout != null && !mCutout.isEmpty()) { mCutout = calculateDisplayCutoutForRotation(mCutout, rotationDelta, origWidth, origHeight); } recalcInsets(res); } /** Get this layout's non-decor insets. */ public Rect nonDecorInsets() { return mNonDecorInsets; } /** Get this layout's stable insets. */ public Rect stableInsets() { return mStableInsets; } /** Get this layout's width. */ public int width() { return mWidth; } /** Get this layout's height. */ public int height() { return mHeight; } /** Get this layout's display rotation. */ public int rotation() { return mRotation; } /** Get this layout's display density. */ public int densityDpi() { return mDensityDpi; } /** Get whether this layout is landscape. */ public boolean isLandscape() { return mWidth > mHeight; } /** Gets the orientation of this layout */ public int getOrientation() { return (mWidth > mHeight) ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; } /** Gets the calculated stable-bounds for this layout */ public void getStableBounds(Rect outBounds) { outBounds.set(0, 0, mWidth, mHeight); outBounds.inset(mStableInsets); } /** * Rotates bounds as if parentBounds and bounds are a group. The group is rotated by `delta` * 90-degree counter-clockwise increments. This assumes that parentBounds is at 0,0 and * remains at 0,0 after rotation. * * Only 'bounds' is mutated. */ public static void rotateBounds(Rect inOutBounds, Rect parentBounds, int delta) { int rdelta = ((delta % 4) + 4) % 4; int origLeft = inOutBounds.left; switch (rdelta) { case 0: return; case 1: inOutBounds.left = inOutBounds.top; inOutBounds.top = parentBounds.right - inOutBounds.right; inOutBounds.right = inOutBounds.bottom; inOutBounds.bottom = parentBounds.right - origLeft; return; case 2: inOutBounds.left = parentBounds.right - inOutBounds.right; inOutBounds.right = parentBounds.right - origLeft; return; case 3: inOutBounds.left = parentBounds.bottom - inOutBounds.bottom; inOutBounds.bottom = inOutBounds.right; inOutBounds.right = parentBounds.bottom - inOutBounds.top; inOutBounds.top = origLeft; return; } } /** * Calculates the stable insets if we already have the non-decor insets. */ private static void convertNonDecorInsetsToStableInsets(Resources res, Rect inOutInsets, int displayWidth, int displayHeight, boolean hasStatusBar) { if (!hasStatusBar) { return; } int statusBarHeight = getStatusBarHeight(displayWidth > displayHeight, res); inOutInsets.top = Math.max(inOutInsets.top, statusBarHeight); } /** * Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system * bar or button bar. * * @param displayRotation the current display rotation * @param displayWidth the current display width * @param displayHeight the current display height * @param displayCutout the current display cutout * @param outInsets the insets to return */ static void computeNonDecorInsets(Resources res, int displayRotation, int displayWidth, int displayHeight, DisplayCutout displayCutout, int uiMode, Rect outInsets, boolean hasNavigationBar) { outInsets.setEmpty(); // Only navigation bar if (hasNavigationBar) { int position = navigationBarPosition(res, displayWidth, displayHeight, displayRotation); int navBarSize = getNavigationBarSize(res, position, displayWidth > displayHeight, uiMode); if (position == NAV_BAR_BOTTOM) { outInsets.bottom = navBarSize; } else if (position == NAV_BAR_RIGHT) { outInsets.right = navBarSize; } else if (position == NAV_BAR_LEFT) { outInsets.left = navBarSize; } } if (displayCutout != null) { outInsets.left += displayCutout.getSafeInsetLeft(); outInsets.top += displayCutout.getSafeInsetTop(); outInsets.right += displayCutout.getSafeInsetRight(); outInsets.bottom += displayCutout.getSafeInsetBottom(); } } /** * Calculates the stable insets without running a layout. * * @param displayRotation the current display rotation * @param displayWidth the current display width * @param displayHeight the current display height * @param displayCutout the current display cutout * @param outInsets the insets to return */ static void computeStableInsets(Resources res, int displayRotation, int displayWidth, int displayHeight, DisplayCutout displayCutout, int uiMode, Rect outInsets, boolean hasNavigationBar, boolean hasStatusBar) { outInsets.setEmpty(); // Navigation bar and status bar. computeNonDecorInsets(res, displayRotation, displayWidth, displayHeight, displayCutout, uiMode, outInsets, hasNavigationBar); convertNonDecorInsetsToStableInsets(res, outInsets, displayWidth, displayHeight, hasStatusBar); } /** Retrieve the statusbar height from resources. */ static int getStatusBarHeight(boolean landscape, Resources res) { return landscape ? res.getDimensionPixelSize( com.android.internal.R.dimen.status_bar_height_landscape) : res.getDimensionPixelSize( com.android.internal.R.dimen.status_bar_height_portrait); } /** Calculate the DisplayCutout for a particular display size/rotation. */ public static DisplayCutout calculateDisplayCutoutForRotation( DisplayCutout cutout, int rotation, int displayWidth, int displayHeight) { if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) { return null; } if (rotation == ROTATION_0) { return computeSafeInsets( cutout, displayWidth, displayHeight); } final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); Rect[] cutoutRects = computeSafeInsets(cutout, displayWidth, displayHeight) .getBoundingRectsAll(); final Rect[] newBounds = new Rect[cutoutRects.length]; final Rect displayBounds = new Rect(0, 0, displayWidth, displayHeight); for (int i = 0; i < cutoutRects.length; ++i) { newBounds[i] = new Rect(cutoutRects[i]); rotateBounds(newBounds[i], displayBounds, rotation); } return computeSafeInsets(DisplayCutout.fromBounds(newBounds), rotated ? displayHeight : displayWidth, rotated ? displayWidth : displayHeight); } /** Calculate safe insets. */ public static DisplayCutout computeSafeInsets(DisplayCutout inner, int displayWidth, int displayHeight) { if (inner == DisplayCutout.NO_CUTOUT || inner.isBoundsEmpty()) { return null; } final Size displaySize = new Size(displayWidth, displayHeight); final Rect safeInsets = computeSafeInsets(displaySize, inner); return inner.replaceSafeInsets(safeInsets); } private static Rect computeSafeInsets(Size displaySize, DisplayCutout cutout) { if (displaySize.getWidth() < displaySize.getHeight()) { final List<Rect> boundingRects = cutout.replaceSafeInsets( new Rect(0, displaySize.getHeight() / 2, 0, displaySize.getHeight() / 2)) .getBoundingRects(); int topInset = findInsetForSide(displaySize, boundingRects, Gravity.TOP); int bottomInset = findInsetForSide(displaySize, boundingRects, Gravity.BOTTOM); return new Rect(0, topInset, 0, bottomInset); } else if (displaySize.getWidth() > displaySize.getHeight()) { final List<Rect> boundingRects = cutout.replaceSafeInsets( new Rect(displaySize.getWidth() / 2, 0, displaySize.getWidth() / 2, 0)) .getBoundingRects(); int leftInset = findInsetForSide(displaySize, boundingRects, Gravity.LEFT); int right = findInsetForSide(displaySize, boundingRects, Gravity.RIGHT); return new Rect(leftInset, 0, right, 0); } else { throw new UnsupportedOperationException("not implemented: display=" + displaySize + " cutout=" + cutout); } } private static int findInsetForSide(Size display, List<Rect> boundingRects, int gravity) { int inset = 0; final int size = boundingRects.size(); for (int i = 0; i < size; i++) { Rect boundingRect = boundingRects.get(i); switch (gravity) { case Gravity.TOP: if (boundingRect.top == 0) { inset = Math.max(inset, boundingRect.bottom); } break; case Gravity.BOTTOM: if (boundingRect.bottom == display.getHeight()) { inset = Math.max(inset, display.getHeight() - boundingRect.top); } break; case Gravity.LEFT: if (boundingRect.left == 0) { inset = Math.max(inset, boundingRect.right); } break; case Gravity.RIGHT: if (boundingRect.right == display.getWidth()) { inset = Math.max(inset, display.getWidth() - boundingRect.left); } break; default: throw new IllegalArgumentException("unknown gravity: " + gravity); } } return inset; } static boolean hasNavigationBar(DisplayInfo info, Context context, int displayId) { if (displayId == Display.DEFAULT_DISPLAY) { // Allow a system property to override this. Used by the emulator. final String navBarOverride = SystemProperties.get("qemu.hw.mainkeys"); if ("1".equals(navBarOverride)) { return false; } else if ("0".equals(navBarOverride)) { return true; } return context.getResources().getBoolean(R.bool.config_showNavigationBar); } else { boolean isUntrustedVirtualDisplay = info.type == Display.TYPE_VIRTUAL && info.ownerUid != SYSTEM_UID; final ContentResolver resolver = context.getContentResolver(); boolean forceDesktopOnExternal = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0; return ((info.flags & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0 || (forceDesktopOnExternal && !isUntrustedVirtualDisplay)); // TODO(b/142569966): make sure VR2D and DisplayWindowSettings are moved here somehow. } } static boolean hasStatusBar(int displayId) { return displayId == Display.DEFAULT_DISPLAY; } /** Retrieve navigation bar position from resources based on rotation and size. */ public static int navigationBarPosition(Resources res, int displayWidth, int displayHeight, int rotation) { boolean navBarCanMove = displayWidth != displayHeight && res.getBoolean( com.android.internal.R.bool.config_navBarCanMove); if (navBarCanMove && displayWidth > displayHeight) { if (rotation == Surface.ROTATION_90) { return NAV_BAR_RIGHT; } else { return NAV_BAR_LEFT; } } return NAV_BAR_BOTTOM; } /** Retrieve navigation bar size from resources based on side/orientation/ui-mode */ public static int getNavigationBarSize(Resources res, int navBarSide, boolean landscape, int uiMode) { final boolean carMode = (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR; if (carMode) { if (navBarSide == NAV_BAR_BOTTOM) { return res.getDimensionPixelSize(landscape ? R.dimen.navigation_bar_height_landscape_car_mode : R.dimen.navigation_bar_height_car_mode); } else { return res.getDimensionPixelSize(R.dimen.navigation_bar_width_car_mode); } } else { if (navBarSide == NAV_BAR_BOTTOM) { return res.getDimensionPixelSize(landscape ? R.dimen.navigation_bar_height_landscape : R.dimen.navigation_bar_height); } else { return res.getDimensionPixelSize(R.dimen.navigation_bar_width); } } } } packages/SystemUI/src/com/android/systemui/wm/DisplayWindowController.java +50 −6 Original line number Diff line number Diff line Loading @@ -16,16 +16,20 @@ package com.android.systemui.wm; import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; import android.hardware.display.DisplayManager; import android.os.Handler; import android.os.RemoteException; import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.view.IDisplayWindowListener; import android.view.IDisplayWindowRotationCallback; import android.view.IDisplayWindowRotationController; import android.view.IWindowManager; import android.view.WindowContainerTransaction; import android.view.WindowManagerGlobal; import com.android.systemui.dagger.qualifiers.MainHandler; Loading @@ -45,6 +49,8 @@ public class DisplayWindowController { private static final String TAG = "DisplayWindowController"; private final Handler mHandler; private final Context mContext; private final IWindowManager mWmService; private final ArrayList<OnDisplayWindowRotationController> mRotationControllers = new ArrayList<>(); Loading Loading @@ -76,6 +82,14 @@ public class DisplayWindowController { } }; /** * Get's a display by id from DisplayManager. */ public Display getDisplay(int displayId) { final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); return displayManager.getDisplay(displayId); } private final IDisplayWindowListener mDisplayContainerListener = new IDisplayWindowListener.Stub() { @Override Loading @@ -87,6 +101,10 @@ public class DisplayWindowController { } DisplayRecord record = new DisplayRecord(); record.mDisplayId = displayId; Display display = getDisplay(displayId); record.mContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext : mContext.createDisplayContext(display); record.mDisplayLayout = new DisplayLayout(record.mContext, display); mDisplays.put(displayId, record); for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { mDisplayChangedListeners.get(i).onDisplayAdded(displayId); Loading @@ -105,6 +123,13 @@ public class DisplayWindowController { + " display."); return; } Display display = getDisplay(displayId); Context perDisplayContext = mContext; if (displayId != Display.DEFAULT_DISPLAY) { perDisplayContext = mContext.createDisplayContext(display); } dr.mContext = perDisplayContext.createConfigurationContext(newConfig); dr.mDisplayLayout = new DisplayLayout(dr.mContext, display); for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { mDisplayChangedListeners.get(i).onDisplayConfigurationChanged( displayId, newConfig); Loading @@ -127,18 +152,35 @@ public class DisplayWindowController { }; @Inject public DisplayWindowController(@MainHandler Handler mainHandler) { public DisplayWindowController(Context context, @MainHandler Handler mainHandler, IWindowManager wmService) { mHandler = mainHandler; mContext = context; mWmService = wmService; try { WindowManagerGlobal.getWindowManagerService().registerDisplayWindowListener( mDisplayContainerListener); WindowManagerGlobal.getWindowManagerService().setDisplayWindowRotationController( mDisplayRotationController); mWmService.registerDisplayWindowListener(mDisplayContainerListener); mWmService.setDisplayWindowRotationController(mDisplayRotationController); } catch (RemoteException e) { throw new RuntimeException("Unable to register hierarchy listener"); } } /** * Gets the DisplayLayout associated with a display. */ public @Nullable DisplayLayout getDisplayLayout(int displayId) { final DisplayRecord r = mDisplays.get(displayId); return r != null ? r.mDisplayLayout : null; } /** * Gets a display-specific context for a display. */ public @Nullable Context getDisplayContext(int displayId) { final DisplayRecord r = mDisplays.get(displayId); return r != null ? r.mContext : null; } /** * Add a display window-container listener. It will get notified whenever a display's * configuration changes or when displays are added/removed from the WM hierarchy. Loading Loading @@ -184,6 +226,8 @@ public class DisplayWindowController { private static class DisplayRecord { int mDisplayId; Context mContext; DisplayLayout mDisplayLayout; } /** Loading packages/SystemUI/tests/src/com/android/systemui/wm/DisplayLayoutTest.java 0 → 100644 +141 −0 File added.Preview size limit exceeded, changes collapsed. Show changes Loading
packages/SystemUI/src/com/android/systemui/wm/DisplayLayout.java 0 → 100644 +474 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.wm; import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.content.res.Configuration.UI_MODE_TYPE_CAR; import static android.content.res.Configuration.UI_MODE_TYPE_MASK; import static android.os.Process.SYSTEM_UID; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; import static android.view.Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; import static android.view.Surface.ROTATION_0; import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_90; import android.annotation.NonNull; import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources; import android.graphics.Rect; import android.os.SystemProperties; import android.provider.Settings; import android.util.Size; import android.view.Display; import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.Gravity; import android.view.Surface; import com.android.internal.R; import java.util.List; /** * Contains information about the layout-properties of a display. This refers to internal layout * like insets/cutout/rotation. In general, this can be thought of as the System-UI analog to * DisplayPolicy. */ public class DisplayLayout { // Navigation bar position values private static final int NAV_BAR_LEFT = 1 << 0; private static final int NAV_BAR_RIGHT = 1 << 1; private static final int NAV_BAR_BOTTOM = 1 << 2; private int mUiMode; private int mWidth; private int mHeight; private DisplayCutout mCutout; private int mRotation; private int mDensityDpi; private final Rect mNonDecorInsets = new Rect(); private final Rect mStableInsets = new Rect(); private boolean mHasNavigationBar = false; private boolean mHasStatusBar = false; /** * Create empty layout. */ public DisplayLayout() { } /** * Construct a custom display layout using a DisplayInfo. * @param info * @param res */ public DisplayLayout(DisplayInfo info, Resources res, boolean hasNavigationBar, boolean hasStatusBar) { init(info, res, hasNavigationBar, hasStatusBar); } /** * Construct a display layout based on a live display. * @param context Used for resources. */ public DisplayLayout(@NonNull Context context, @NonNull Display rawDisplay) { final int displayId = rawDisplay.getDisplayId(); DisplayInfo info = new DisplayInfo(); rawDisplay.getDisplayInfo(info); init(info, context.getResources(), hasNavigationBar(info, context, displayId), hasStatusBar(displayId)); } public DisplayLayout(DisplayLayout dl) { set(dl); } /** sets this DisplayLayout to a copy of another on. */ public void set(DisplayLayout dl) { mUiMode = dl.mUiMode; mWidth = dl.mWidth; mHeight = dl.mHeight; mCutout = dl.mCutout; mRotation = dl.mRotation; mDensityDpi = dl.mDensityDpi; mHasNavigationBar = dl.mHasNavigationBar; mHasStatusBar = dl.mHasStatusBar; mNonDecorInsets.set(dl.mNonDecorInsets); mStableInsets.set(dl.mStableInsets); } private void init(DisplayInfo info, Resources res, boolean hasNavigationBar, boolean hasStatusBar) { mUiMode = res.getConfiguration().uiMode; mWidth = info.logicalWidth; mHeight = info.logicalHeight; mRotation = info.rotation; mCutout = info.displayCutout; mDensityDpi = info.logicalDensityDpi; mHasNavigationBar = hasNavigationBar; mHasStatusBar = hasStatusBar; recalcInsets(res); } private void recalcInsets(Resources res) { computeNonDecorInsets(res, mRotation, mWidth, mHeight, mCutout, mUiMode, mNonDecorInsets, mHasNavigationBar); mStableInsets.set(mNonDecorInsets); if (mHasStatusBar) { convertNonDecorInsetsToStableInsets(res, mStableInsets, mWidth, mHeight, mHasStatusBar); } } /** * Apply a rotation to this layout and its parameters. * @param res * @param targetRotation */ public void rotateTo(Resources res, @Surface.Rotation int targetRotation) { final int rotationDelta = (targetRotation - mRotation + 4) % 4; final boolean changeOrient = (rotationDelta % 2) != 0; final int origWidth = mWidth; final int origHeight = mHeight; mRotation = targetRotation; if (changeOrient) { mWidth = origHeight; mHeight = origWidth; } if (mCutout != null && !mCutout.isEmpty()) { mCutout = calculateDisplayCutoutForRotation(mCutout, rotationDelta, origWidth, origHeight); } recalcInsets(res); } /** Get this layout's non-decor insets. */ public Rect nonDecorInsets() { return mNonDecorInsets; } /** Get this layout's stable insets. */ public Rect stableInsets() { return mStableInsets; } /** Get this layout's width. */ public int width() { return mWidth; } /** Get this layout's height. */ public int height() { return mHeight; } /** Get this layout's display rotation. */ public int rotation() { return mRotation; } /** Get this layout's display density. */ public int densityDpi() { return mDensityDpi; } /** Get whether this layout is landscape. */ public boolean isLandscape() { return mWidth > mHeight; } /** Gets the orientation of this layout */ public int getOrientation() { return (mWidth > mHeight) ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT; } /** Gets the calculated stable-bounds for this layout */ public void getStableBounds(Rect outBounds) { outBounds.set(0, 0, mWidth, mHeight); outBounds.inset(mStableInsets); } /** * Rotates bounds as if parentBounds and bounds are a group. The group is rotated by `delta` * 90-degree counter-clockwise increments. This assumes that parentBounds is at 0,0 and * remains at 0,0 after rotation. * * Only 'bounds' is mutated. */ public static void rotateBounds(Rect inOutBounds, Rect parentBounds, int delta) { int rdelta = ((delta % 4) + 4) % 4; int origLeft = inOutBounds.left; switch (rdelta) { case 0: return; case 1: inOutBounds.left = inOutBounds.top; inOutBounds.top = parentBounds.right - inOutBounds.right; inOutBounds.right = inOutBounds.bottom; inOutBounds.bottom = parentBounds.right - origLeft; return; case 2: inOutBounds.left = parentBounds.right - inOutBounds.right; inOutBounds.right = parentBounds.right - origLeft; return; case 3: inOutBounds.left = parentBounds.bottom - inOutBounds.bottom; inOutBounds.bottom = inOutBounds.right; inOutBounds.right = parentBounds.bottom - inOutBounds.top; inOutBounds.top = origLeft; return; } } /** * Calculates the stable insets if we already have the non-decor insets. */ private static void convertNonDecorInsetsToStableInsets(Resources res, Rect inOutInsets, int displayWidth, int displayHeight, boolean hasStatusBar) { if (!hasStatusBar) { return; } int statusBarHeight = getStatusBarHeight(displayWidth > displayHeight, res); inOutInsets.top = Math.max(inOutInsets.top, statusBarHeight); } /** * Calculates the insets for the areas that could never be removed in Honeycomb, i.e. system * bar or button bar. * * @param displayRotation the current display rotation * @param displayWidth the current display width * @param displayHeight the current display height * @param displayCutout the current display cutout * @param outInsets the insets to return */ static void computeNonDecorInsets(Resources res, int displayRotation, int displayWidth, int displayHeight, DisplayCutout displayCutout, int uiMode, Rect outInsets, boolean hasNavigationBar) { outInsets.setEmpty(); // Only navigation bar if (hasNavigationBar) { int position = navigationBarPosition(res, displayWidth, displayHeight, displayRotation); int navBarSize = getNavigationBarSize(res, position, displayWidth > displayHeight, uiMode); if (position == NAV_BAR_BOTTOM) { outInsets.bottom = navBarSize; } else if (position == NAV_BAR_RIGHT) { outInsets.right = navBarSize; } else if (position == NAV_BAR_LEFT) { outInsets.left = navBarSize; } } if (displayCutout != null) { outInsets.left += displayCutout.getSafeInsetLeft(); outInsets.top += displayCutout.getSafeInsetTop(); outInsets.right += displayCutout.getSafeInsetRight(); outInsets.bottom += displayCutout.getSafeInsetBottom(); } } /** * Calculates the stable insets without running a layout. * * @param displayRotation the current display rotation * @param displayWidth the current display width * @param displayHeight the current display height * @param displayCutout the current display cutout * @param outInsets the insets to return */ static void computeStableInsets(Resources res, int displayRotation, int displayWidth, int displayHeight, DisplayCutout displayCutout, int uiMode, Rect outInsets, boolean hasNavigationBar, boolean hasStatusBar) { outInsets.setEmpty(); // Navigation bar and status bar. computeNonDecorInsets(res, displayRotation, displayWidth, displayHeight, displayCutout, uiMode, outInsets, hasNavigationBar); convertNonDecorInsetsToStableInsets(res, outInsets, displayWidth, displayHeight, hasStatusBar); } /** Retrieve the statusbar height from resources. */ static int getStatusBarHeight(boolean landscape, Resources res) { return landscape ? res.getDimensionPixelSize( com.android.internal.R.dimen.status_bar_height_landscape) : res.getDimensionPixelSize( com.android.internal.R.dimen.status_bar_height_portrait); } /** Calculate the DisplayCutout for a particular display size/rotation. */ public static DisplayCutout calculateDisplayCutoutForRotation( DisplayCutout cutout, int rotation, int displayWidth, int displayHeight) { if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) { return null; } if (rotation == ROTATION_0) { return computeSafeInsets( cutout, displayWidth, displayHeight); } final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270); Rect[] cutoutRects = computeSafeInsets(cutout, displayWidth, displayHeight) .getBoundingRectsAll(); final Rect[] newBounds = new Rect[cutoutRects.length]; final Rect displayBounds = new Rect(0, 0, displayWidth, displayHeight); for (int i = 0; i < cutoutRects.length; ++i) { newBounds[i] = new Rect(cutoutRects[i]); rotateBounds(newBounds[i], displayBounds, rotation); } return computeSafeInsets(DisplayCutout.fromBounds(newBounds), rotated ? displayHeight : displayWidth, rotated ? displayWidth : displayHeight); } /** Calculate safe insets. */ public static DisplayCutout computeSafeInsets(DisplayCutout inner, int displayWidth, int displayHeight) { if (inner == DisplayCutout.NO_CUTOUT || inner.isBoundsEmpty()) { return null; } final Size displaySize = new Size(displayWidth, displayHeight); final Rect safeInsets = computeSafeInsets(displaySize, inner); return inner.replaceSafeInsets(safeInsets); } private static Rect computeSafeInsets(Size displaySize, DisplayCutout cutout) { if (displaySize.getWidth() < displaySize.getHeight()) { final List<Rect> boundingRects = cutout.replaceSafeInsets( new Rect(0, displaySize.getHeight() / 2, 0, displaySize.getHeight() / 2)) .getBoundingRects(); int topInset = findInsetForSide(displaySize, boundingRects, Gravity.TOP); int bottomInset = findInsetForSide(displaySize, boundingRects, Gravity.BOTTOM); return new Rect(0, topInset, 0, bottomInset); } else if (displaySize.getWidth() > displaySize.getHeight()) { final List<Rect> boundingRects = cutout.replaceSafeInsets( new Rect(displaySize.getWidth() / 2, 0, displaySize.getWidth() / 2, 0)) .getBoundingRects(); int leftInset = findInsetForSide(displaySize, boundingRects, Gravity.LEFT); int right = findInsetForSide(displaySize, boundingRects, Gravity.RIGHT); return new Rect(leftInset, 0, right, 0); } else { throw new UnsupportedOperationException("not implemented: display=" + displaySize + " cutout=" + cutout); } } private static int findInsetForSide(Size display, List<Rect> boundingRects, int gravity) { int inset = 0; final int size = boundingRects.size(); for (int i = 0; i < size; i++) { Rect boundingRect = boundingRects.get(i); switch (gravity) { case Gravity.TOP: if (boundingRect.top == 0) { inset = Math.max(inset, boundingRect.bottom); } break; case Gravity.BOTTOM: if (boundingRect.bottom == display.getHeight()) { inset = Math.max(inset, display.getHeight() - boundingRect.top); } break; case Gravity.LEFT: if (boundingRect.left == 0) { inset = Math.max(inset, boundingRect.right); } break; case Gravity.RIGHT: if (boundingRect.right == display.getWidth()) { inset = Math.max(inset, display.getWidth() - boundingRect.left); } break; default: throw new IllegalArgumentException("unknown gravity: " + gravity); } } return inset; } static boolean hasNavigationBar(DisplayInfo info, Context context, int displayId) { if (displayId == Display.DEFAULT_DISPLAY) { // Allow a system property to override this. Used by the emulator. final String navBarOverride = SystemProperties.get("qemu.hw.mainkeys"); if ("1".equals(navBarOverride)) { return false; } else if ("0".equals(navBarOverride)) { return true; } return context.getResources().getBoolean(R.bool.config_showNavigationBar); } else { boolean isUntrustedVirtualDisplay = info.type == Display.TYPE_VIRTUAL && info.ownerUid != SYSTEM_UID; final ContentResolver resolver = context.getContentResolver(); boolean forceDesktopOnExternal = Settings.Global.getInt(resolver, DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0; return ((info.flags & FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0 || (forceDesktopOnExternal && !isUntrustedVirtualDisplay)); // TODO(b/142569966): make sure VR2D and DisplayWindowSettings are moved here somehow. } } static boolean hasStatusBar(int displayId) { return displayId == Display.DEFAULT_DISPLAY; } /** Retrieve navigation bar position from resources based on rotation and size. */ public static int navigationBarPosition(Resources res, int displayWidth, int displayHeight, int rotation) { boolean navBarCanMove = displayWidth != displayHeight && res.getBoolean( com.android.internal.R.bool.config_navBarCanMove); if (navBarCanMove && displayWidth > displayHeight) { if (rotation == Surface.ROTATION_90) { return NAV_BAR_RIGHT; } else { return NAV_BAR_LEFT; } } return NAV_BAR_BOTTOM; } /** Retrieve navigation bar size from resources based on side/orientation/ui-mode */ public static int getNavigationBarSize(Resources res, int navBarSide, boolean landscape, int uiMode) { final boolean carMode = (uiMode & UI_MODE_TYPE_MASK) == UI_MODE_TYPE_CAR; if (carMode) { if (navBarSide == NAV_BAR_BOTTOM) { return res.getDimensionPixelSize(landscape ? R.dimen.navigation_bar_height_landscape_car_mode : R.dimen.navigation_bar_height_car_mode); } else { return res.getDimensionPixelSize(R.dimen.navigation_bar_width_car_mode); } } else { if (navBarSide == NAV_BAR_BOTTOM) { return res.getDimensionPixelSize(landscape ? R.dimen.navigation_bar_height_landscape : R.dimen.navigation_bar_height); } else { return res.getDimensionPixelSize(R.dimen.navigation_bar_width); } } } }
packages/SystemUI/src/com/android/systemui/wm/DisplayWindowController.java +50 −6 Original line number Diff line number Diff line Loading @@ -16,16 +16,20 @@ package com.android.systemui.wm; import android.annotation.Nullable; import android.content.Context; import android.content.res.Configuration; import android.hardware.display.DisplayManager; import android.os.Handler; import android.os.RemoteException; import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.view.IDisplayWindowListener; import android.view.IDisplayWindowRotationCallback; import android.view.IDisplayWindowRotationController; import android.view.IWindowManager; import android.view.WindowContainerTransaction; import android.view.WindowManagerGlobal; import com.android.systemui.dagger.qualifiers.MainHandler; Loading @@ -45,6 +49,8 @@ public class DisplayWindowController { private static final String TAG = "DisplayWindowController"; private final Handler mHandler; private final Context mContext; private final IWindowManager mWmService; private final ArrayList<OnDisplayWindowRotationController> mRotationControllers = new ArrayList<>(); Loading Loading @@ -76,6 +82,14 @@ public class DisplayWindowController { } }; /** * Get's a display by id from DisplayManager. */ public Display getDisplay(int displayId) { final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); return displayManager.getDisplay(displayId); } private final IDisplayWindowListener mDisplayContainerListener = new IDisplayWindowListener.Stub() { @Override Loading @@ -87,6 +101,10 @@ public class DisplayWindowController { } DisplayRecord record = new DisplayRecord(); record.mDisplayId = displayId; Display display = getDisplay(displayId); record.mContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext : mContext.createDisplayContext(display); record.mDisplayLayout = new DisplayLayout(record.mContext, display); mDisplays.put(displayId, record); for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { mDisplayChangedListeners.get(i).onDisplayAdded(displayId); Loading @@ -105,6 +123,13 @@ public class DisplayWindowController { + " display."); return; } Display display = getDisplay(displayId); Context perDisplayContext = mContext; if (displayId != Display.DEFAULT_DISPLAY) { perDisplayContext = mContext.createDisplayContext(display); } dr.mContext = perDisplayContext.createConfigurationContext(newConfig); dr.mDisplayLayout = new DisplayLayout(dr.mContext, display); for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { mDisplayChangedListeners.get(i).onDisplayConfigurationChanged( displayId, newConfig); Loading @@ -127,18 +152,35 @@ public class DisplayWindowController { }; @Inject public DisplayWindowController(@MainHandler Handler mainHandler) { public DisplayWindowController(Context context, @MainHandler Handler mainHandler, IWindowManager wmService) { mHandler = mainHandler; mContext = context; mWmService = wmService; try { WindowManagerGlobal.getWindowManagerService().registerDisplayWindowListener( mDisplayContainerListener); WindowManagerGlobal.getWindowManagerService().setDisplayWindowRotationController( mDisplayRotationController); mWmService.registerDisplayWindowListener(mDisplayContainerListener); mWmService.setDisplayWindowRotationController(mDisplayRotationController); } catch (RemoteException e) { throw new RuntimeException("Unable to register hierarchy listener"); } } /** * Gets the DisplayLayout associated with a display. */ public @Nullable DisplayLayout getDisplayLayout(int displayId) { final DisplayRecord r = mDisplays.get(displayId); return r != null ? r.mDisplayLayout : null; } /** * Gets a display-specific context for a display. */ public @Nullable Context getDisplayContext(int displayId) { final DisplayRecord r = mDisplays.get(displayId); return r != null ? r.mContext : null; } /** * Add a display window-container listener. It will get notified whenever a display's * configuration changes or when displays are added/removed from the WM hierarchy. Loading Loading @@ -184,6 +226,8 @@ public class DisplayWindowController { private static class DisplayRecord { int mDisplayId; Context mContext; DisplayLayout mDisplayLayout; } /** Loading
packages/SystemUI/tests/src/com/android/systemui/wm/DisplayLayoutTest.java 0 → 100644 +141 −0 File added.Preview size limit exceeded, changes collapsed. Show changes