diff --git a/packages/SystemUI/res/drawable/system_animation_ongoing_dot.xml b/packages/SystemUI/res/drawable/system_animation_ongoing_dot.xml
index 9f81b0d59973f..09af6d2dace7e 100644
--- a/packages/SystemUI/res/drawable/system_animation_ongoing_dot.xml
+++ b/packages/SystemUI/res/drawable/system_animation_ongoing_dot.xml
@@ -20,7 +20,7 @@
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
index 676e49298cefe..812277634d094 100644
--- a/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
+++ b/packages/SystemUI/res/layout/ongoing_privacy_chip.xml
@@ -32,6 +32,7 @@
android:paddingEnd="10dp"
android:gravity="center"
android:layout_gravity="center"
- android:minWidth="56dp"
+ android:minWidth="@dimen/ongoing_appops_chip_min_width"
+ android:maxWidth="@dimen/ongoing_appops_chip_max_width"
/>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 915b62791f585..7b821eb7e2bbe 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1239,6 +1239,11 @@
16dp
28dp
+
+ 56dp
+
+ 76dp
+ 6dp
@dimen/notification_shade_content_margin_horizontal
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
index 684760e6c3a3d..69360b290118a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java
@@ -30,7 +30,6 @@ import android.annotation.Nullable;
import android.app.Fragment;
import android.os.Bundle;
import android.os.Parcelable;
-import android.util.Log;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
@@ -230,7 +229,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue
if (displayId != getContext().getDisplayId()) {
return;
}
- Log.d(TAG, "disable: ");
state1 = adjustDisableFlags(state1);
final int old1 = mDisabled1;
final int diff1 = state1 ^ old1;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
new file mode 100644
index 0000000000000..8befe800b4e1d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2021 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.statusbar.phone
+
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Rect
+import android.util.Pair
+import android.view.DisplayCutout
+import android.view.View.LAYOUT_DIRECTION_RTL
+import android.view.WindowManager
+import com.android.systemui.Dumpable
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.leak.RotationUtils
+import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE
+import com.android.systemui.util.leak.RotationUtils.ROTATION_NONE
+import com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE
+import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN
+import com.android.systemui.util.leak.RotationUtils.Rotation
+import java.io.FileDescriptor
+import java.io.PrintWriter
+import java.lang.Math.max
+import javax.inject.Inject
+
+/**
+ * Encapsulates logic that can solve for the left/right insets required for the status bar contents.
+ * Takes into account:
+ * 1. rounded_corner_content_padding
+ * 2. status_bar_padding_start, status_bar_padding_end
+ * 2. display cutout insets from left or right
+ * 3. waterfall insets
+ *
+ *
+ * Importantly, these functions can determine status bar content left/right insets for any rotation
+ * before having done a layout pass in that rotation.
+ *
+ * NOTE: This class is not threadsafe
+ */
+@SysUISingleton
+class StatusBarContentInsetsProvider @Inject constructor(
+ val context: Context,
+ val configurationController: ConfigurationController,
+ val windowManager: WindowManager,
+ val dumpManager: DumpManager
+) : CallbackController,
+ ConfigurationController.ConfigurationListener,
+ Dumpable {
+ // Indexed by @Rotation
+ private val insetsByCorner = arrayOfNulls(4)
+ private val listeners = mutableSetOf()
+
+ init {
+ configurationController.addCallback(this)
+ dumpManager.registerDumpable(TAG, this)
+ }
+
+ override fun addCallback(listener: StatusBarContentInsetsChangedListener) {
+ listeners.add(listener)
+ }
+
+ override fun removeCallback(listener: StatusBarContentInsetsChangedListener) {
+ listeners.remove(listener)
+ }
+
+ override fun onDensityOrFontScaleChanged() {
+ clearCachedInsets()
+ }
+
+ override fun onOverlayChanged() {
+ clearCachedInsets()
+ }
+
+ private fun clearCachedInsets() {
+ insetsByCorner[0] = null
+ insetsByCorner[1] = null
+ insetsByCorner[2] = null
+ insetsByCorner[3] = null
+
+ notifyInsetsChanged()
+ }
+
+ private fun notifyInsetsChanged() {
+ listeners.forEach {
+ it.onStatusBarContentInsetsChanged()
+ }
+ }
+
+ /**
+ * Calculates the maximum bounding rectangle for the privacy chip animation + ongoing privacy
+ * dot in the coordinates relative to the given rotation.
+ */
+ fun getBoundingRectForPrivacyChipForRotation(@Rotation rotation: Int): Rect {
+ var insets = insetsByCorner[rotation]
+ val rotatedResources = RotationUtils.getResourcesForRotation(rotation, context)
+ if (insets == null) {
+ insets = getAndSetInsetsForRotation(rotation, rotatedResources)
+ }
+
+ val dotWidth = rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_diameter)
+ val chipWidth = rotatedResources.getDimensionPixelSize(
+ R.dimen.ongoing_appops_chip_max_width)
+
+ return if (context.resources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL) {
+ Rect(insets.left - dotWidth,
+ insets.top,
+ insets.left + chipWidth,
+ insets.bottom)
+ } else {
+ Rect(insets.right - chipWidth,
+ insets.top,
+ insets.right + dotWidth,
+ insets.bottom)
+ }
+ }
+
+ /**
+ * Calculates the necessary left and right locations for the status bar contents invariant of
+ * the current device rotation, in the target rotation's coordinates
+ */
+ fun getStatusBarContentInsetsForRotation(@Rotation rotation: Int): Rect {
+ var insets = insetsByCorner[rotation]
+ if (insets == null) {
+ val rotatedResources = RotationUtils.getResourcesForRotation(rotation, context)
+ insets = getCalculatedInsetsForRotation(rotation, rotatedResources)
+ insetsByCorner[rotation] = insets
+ }
+
+ return insets
+ }
+
+ private fun getAndSetInsetsForRotation(
+ @Rotation rot: Int,
+ rotatedResources: Resources
+ ): Rect {
+ val insets = getCalculatedInsetsForRotation(rot, rotatedResources)
+ insetsByCorner[rot] = insets
+
+ return insets
+ }
+
+ private fun getCalculatedInsetsForRotation(
+ @Rotation rotation: Int,
+ rotatedResources: Resources
+ ): Rect {
+ val dc = context.display.cutout
+
+ return calculateInsetsForRotationWithRotatedResources(
+ rotation, rotatedResources, dc, windowManager, context)
+ }
+
+ override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array) {
+ insetsByCorner.forEachIndexed { index, rect ->
+ pw.println("${RotationUtils.toString(index)} -> $rect")
+ }
+ }
+}
+
+interface StatusBarContentInsetsChangedListener {
+ fun onStatusBarContentInsetsChanged()
+}
+
+private const val TAG = "StatusBarInsetsProvider"
+
+private fun getRotationZeroDisplayBounds(wm: WindowManager, @Rotation exactRotation: Int): Rect {
+ val bounds = wm.maximumWindowMetrics.bounds
+
+ if (exactRotation == ROTATION_NONE || exactRotation == ROTATION_UPSIDE_DOWN) {
+ return bounds
+ }
+
+ // bounds are horizontal, swap height and width
+ return Rect(0, 0, bounds.bottom, bounds.right)
+}
+
+private fun getCurrentDisplayBounds(wm: WindowManager): Rect {
+ val bounds = wm.maximumWindowMetrics.bounds
+ return bounds
+}
+
+/**
+ * Calculates the exact left and right positions for the status bar contents for the given
+ * rotation
+ *
+ * @param rot rotation for which to query the margins
+ * @param context systemui context
+ * @param rotatedResources resources constructed with the proper orientation set
+ *
+ * @see [RotationUtils#getResourcesForRotation]
+ */
+fun calculateInsetsForRotationWithRotatedResources(
+ @Rotation targetRotation: Int,
+ rotatedResources: Resources,
+ displayCutout: DisplayCutout?,
+ windowmanager: WindowManager,
+ context: Context
+): Rect {
+ val rtl = rotatedResources.configuration.layoutDirection == LAYOUT_DIRECTION_RTL
+
+ val exactRotation = RotationUtils.getExactRotation(context)
+ val height = rotatedResources.getDimensionPixelSize(R.dimen.status_bar_height)
+
+ /*
+ TODO: Check if this is ever used for devices with no rounded corners
+ val paddingStart = rotatedResources.getDimensionPixelSize(R.dimen.status_bar_padding_start)
+ val paddingEnd = rotatedResources.getDimensionPixelSize(R.dimen.status_bar_padding_end)
+ val left = if (rtl) paddingEnd else paddingStart
+ val right = if(rtl) paddingStart else paddingEnd
+ */
+
+ val roundedCornerPadding = rotatedResources.getDimensionPixelSize(
+ R.dimen.rounded_corner_content_padding)
+
+ val rotZeroBounds = getRotationZeroDisplayBounds(windowmanager, exactRotation)
+ val currentBounds = getCurrentDisplayBounds(windowmanager)
+
+ val sbLeftRight = getStatusBarLeftRight(
+ displayCutout,
+ height,
+ rotZeroBounds.right,
+ rotZeroBounds.bottom,
+ currentBounds.width(),
+ currentBounds.height(),
+ roundedCornerPadding,
+ targetRotation,
+ exactRotation)
+
+ return sbLeftRight
+}
+
+/**
+ * Calculate the insets needed from the left and right edges for the given rotation.
+ *
+ * @param dc Device display cutout
+ * @param sbHeight appropriate status bar height for this rotation
+ * @param width display width calculated for ROTATION_NONE
+ * @param height display height calculated for ROTATION_NONE
+ * @param roundedCornerPadding rounded_corner_content_padding dimension
+ * @param targetRotation the rotation for which to calculate margins
+ * @param currentRotation the rotation from which the display cutout was generated
+ *
+ * @return a Rect which exactly calculates the Status Bar's content rect relative to the target
+ * rotation
+ */
+private fun getStatusBarLeftRight(
+ dc: DisplayCutout?,
+ sbHeight: Int,
+ width: Int,
+ height: Int,
+ cWidth: Int,
+ cHeight: Int,
+ roundedCornerPadding: Int,
+ @Rotation targetRotation: Int,
+ @Rotation currentRotation: Int
+): Rect {
+
+ val logicalDisplayWidth = if (targetRotation.isHorizontal()) height else width
+
+ val cutoutRects = dc?.boundingRects
+ if (cutoutRects == null || cutoutRects.isEmpty()) {
+ return Rect(roundedCornerPadding,
+ 0,
+ logicalDisplayWidth - roundedCornerPadding,
+ sbHeight)
+ }
+
+ val relativeRotation = if (currentRotation - targetRotation < 0) {
+ currentRotation - targetRotation + 4
+ } else {
+ currentRotation - targetRotation
+ }
+
+ // Size of the status bar window for the given rotation relative to our exact rotation
+ val sbRect = sbRect(relativeRotation, sbHeight, Pair(cWidth, cHeight))
+
+ var leftMargin = roundedCornerPadding
+ var rightMargin = roundedCornerPadding
+ for (cutoutRect in cutoutRects) {
+ // There is at most one non-functional area per short edge of the device. So if the status
+ // bar doesn't share a short edge with the cutout, we can ignore its insets because there
+ // will be no letter-boxing to worry about
+ if (!shareShortEdge(sbRect, cutoutRect, cWidth, cHeight)) {
+ continue
+ }
+
+ if (cutoutRect.touchesLeftEdge(relativeRotation, cWidth, cHeight)) {
+
+ val l = max(roundedCornerPadding, cutoutRect.logicalWidth(relativeRotation))
+ leftMargin = max(l, leftMargin)
+ } else if (cutoutRect.touchesRightEdge(relativeRotation, cWidth, cHeight)) {
+ val logicalWidth = cutoutRect.logicalWidth(relativeRotation)
+ rightMargin = max(roundedCornerPadding, logicalWidth)
+ }
+ }
+
+ return Rect(leftMargin, 0, logicalDisplayWidth - rightMargin, sbHeight)
+}
+
+private fun sbRect(
+ @Rotation relativeRotation: Int,
+ sbHeight: Int,
+ displaySize: Pair
+): Rect {
+ val w = displaySize.first
+ val h = displaySize.second
+ return when (relativeRotation) {
+ ROTATION_NONE -> Rect(0, 0, w, sbHeight)
+ ROTATION_LANDSCAPE -> Rect(0, 0, sbHeight, h)
+ ROTATION_UPSIDE_DOWN -> Rect(0, h - sbHeight, w, h)
+ else -> Rect(w - sbHeight, 0, w, h)
+ }
+}
+
+private fun shareShortEdge(
+ sbRect: Rect,
+ cutoutRect: Rect,
+ currentWidth: Int,
+ currentHeight: Int
+): Boolean {
+ if (currentWidth < currentHeight) {
+ // Check top/bottom edges by extending the width of the display cutout rect and checking
+ // for intersections
+ return sbRect.intersects(0, cutoutRect.top, currentWidth, cutoutRect.bottom)
+ } else if (currentWidth > currentHeight) {
+ // Short edge is the height, extend that one this time
+ return sbRect.intersects(cutoutRect.left, 0, cutoutRect.right, currentHeight)
+ }
+
+ return false
+}
+
+private fun Rect.touchesRightEdge(@Rotation rot: Int, width: Int, height: Int): Boolean {
+ return when (rot) {
+ ROTATION_NONE -> right >= width
+ ROTATION_LANDSCAPE -> top <= 0
+ ROTATION_UPSIDE_DOWN -> left <= 0
+ else /* SEASCAPE */ -> bottom >= height
+ }
+}
+
+private fun Rect.touchesLeftEdge(@Rotation rot: Int, width: Int, height: Int): Boolean {
+ return when (rot) {
+ ROTATION_NONE -> left <= 0
+ ROTATION_LANDSCAPE -> bottom >= height
+ ROTATION_UPSIDE_DOWN -> right >= width
+ else /* SEASCAPE */ -> top <= 0
+ }
+}
+
+private fun Rect.logicalTop(@Rotation rot: Int): Int {
+ return when (rot) {
+ ROTATION_NONE -> top
+ ROTATION_LANDSCAPE -> left
+ ROTATION_UPSIDE_DOWN -> bottom
+ else /* SEASCAPE */ -> right
+ }
+}
+
+private fun Rect.logicalRight(@Rotation rot: Int): Int {
+ return when (rot) {
+ ROTATION_NONE -> right
+ ROTATION_LANDSCAPE -> top
+ ROTATION_UPSIDE_DOWN -> left
+ else /* SEASCAPE */ -> bottom
+ }
+}
+
+private fun Rect.logicalLeft(@Rotation rot: Int): Int {
+ return when (rot) {
+ ROTATION_NONE -> left
+ ROTATION_LANDSCAPE -> bottom
+ ROTATION_UPSIDE_DOWN -> right
+ else /* SEASCAPE */ -> top
+ }
+}
+
+private fun Rect.logicalWidth(@Rotation rot: Int): Int {
+ return when (rot) {
+ ROTATION_NONE, ROTATION_UPSIDE_DOWN -> width()
+ else /* LANDSCAPE, SEASCAPE */ -> height()
+ }
+}
+
+private fun Int.isHorizontal(): Boolean {
+ return this == ROTATION_LANDSCAPE || this == ROTATION_SEASCAPE
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
index 30b8c5c0d8d34..9a25a70788592 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java
@@ -20,12 +20,20 @@ import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_M
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN;
+
import android.content.Context;
import android.content.res.Resources;
import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.os.Binder;
+import android.os.RemoteException;
import android.util.Log;
import android.view.Gravity;
+import android.view.IWindowManager;
import android.view.ViewGroup;
import android.view.WindowManager;
@@ -46,7 +54,9 @@ public class StatusBarWindowController {
private final Context mContext;
private final WindowManager mWindowManager;
+ private final IWindowManager mIWindowManager;
private final SuperStatusBarViewFactory mSuperStatusBarViewFactory;
+ private final StatusBarContentInsetsProvider mContentInsetsProvider;
private final Resources mResources;
private int mBarHeight = -1;
private final State mCurrentState = new State();
@@ -57,11 +67,17 @@ public class StatusBarWindowController {
private final WindowManager.LayoutParams mLpChanged;
@Inject
- public StatusBarWindowController(Context context, WindowManager windowManager,
+ public StatusBarWindowController(
+ Context context,
+ WindowManager windowManager,
+ IWindowManager iWindowManager,
SuperStatusBarViewFactory superStatusBarViewFactory,
+ StatusBarContentInsetsProvider contentInsetsProvider,
@Main Resources resources) {
mContext = context;
mWindowManager = windowManager;
+ mIWindowManager = iWindowManager;
+ mContentInsetsProvider = contentInsetsProvider;
mSuperStatusBarViewFactory = superStatusBarViewFactory;
mStatusBarView = mSuperStatusBarViewFactory.getStatusBarWindowView();
mLaunchAnimationContainer = mStatusBarView.findViewById(
@@ -120,6 +136,27 @@ public class StatusBarWindowController {
mWindowManager.addView(mStatusBarView, mLp);
mLpChanged.copyFrom(mLp);
+
+ mContentInsetsProvider.addCallback(this::calculateStatusBarLocationsForAllRotations);
+ calculateStatusBarLocationsForAllRotations();
+ }
+
+ private void calculateStatusBarLocationsForAllRotations() {
+ Rect[] bounds = new Rect[4];
+ bounds[0] = mContentInsetsProvider
+ .getBoundingRectForPrivacyChipForRotation(ROTATION_NONE);
+ bounds[1] = mContentInsetsProvider
+ .getBoundingRectForPrivacyChipForRotation(ROTATION_LANDSCAPE);
+ bounds[2] = mContentInsetsProvider
+ .getBoundingRectForPrivacyChipForRotation(ROTATION_UPSIDE_DOWN);
+ bounds[3] = mContentInsetsProvider
+ .getBoundingRectForPrivacyChipForRotation(ROTATION_SEASCAPE);
+
+ try {
+ mIWindowManager.updateStaticPrivacyIndicatorBounds(mContext.getDisplayId(), bounds);
+ } catch (RemoteException e) {
+ //Swallow
+ }
}
/** Set force status bar visible. */
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
index e44981e249064..b9b7730c67f3b 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
@@ -14,16 +14,40 @@
package com.android.systemui.util.leak;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+
+import android.annotation.IntDef;
import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
import android.view.Surface;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
public class RotationUtils {
public static final int ROTATION_NONE = 0;
public static final int ROTATION_LANDSCAPE = 1;
- public static final int ROTATION_SEASCAPE = 2;
- public static final int ROTATION_UPSIDE_DOWN = 3;
+ public static final int ROTATION_UPSIDE_DOWN = 2;
+ public static final int ROTATION_SEASCAPE = 3;
+ // Not to be confused with Surface.Rotation
+ @IntDef(prefix = { "ROTATION_" }, value = {
+ ROTATION_NONE,
+ ROTATION_LANDSCAPE,
+ ROTATION_SEASCAPE,
+ ROTATION_UPSIDE_DOWN,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Rotation {};
+
+ /**
+ * @return the current rotation, differentiating between none (rot_0), landscape (rot_90), and
+ * seascape (rot_180). upside down is not distinguished here
+ */
+ @Rotation
public static int getRotation(Context context) {
int rot = context.getDisplay().getRotation();
if (rot == Surface.ROTATION_90) {
@@ -35,6 +59,11 @@ public class RotationUtils {
}
}
+ /**
+ * @return the current rotation, differentiating between landscape (rot_90), seascape
+ * (rot_270), and upside down (rot_180)
+ */
+ @Rotation
public static int getExactRotation(Context context) {
int rot = context.getDisplay().getRotation();
if (rot == Surface.ROTATION_90) {
@@ -47,4 +76,49 @@ public class RotationUtils {
return ROTATION_NONE;
}
}
+
+ /** * To string */
+ public static String toString(@Rotation int rot) {
+ switch (rot) {
+ case ROTATION_NONE:
+ return "None (0)";
+ case ROTATION_LANDSCAPE:
+ return "Landscape (1)";
+ case ROTATION_UPSIDE_DOWN:
+ return "Upside down (2)";
+ case ROTATION_SEASCAPE:
+ return "Seascape (3)";
+ default:
+ return "Unknown (" + rot + ")";
+ }
+ }
+
+ /**
+ * Create a Resources using the specified rotation for the configuration. Use this to retrieve
+ * resources in values or values-land without needing an actual rotation to happen.
+ *
+ * @param rot the target rotation for which to create the resources
+ * @param context a context
+ * @return a Resources object configured for the given orientation
+ */
+ public static Resources getResourcesForRotation(@Rotation int rot, Context context) {
+ int orientation;
+ switch (rot) {
+ case ROTATION_NONE:
+ case ROTATION_UPSIDE_DOWN:
+ orientation = ORIENTATION_PORTRAIT;
+ break;
+ case ROTATION_LANDSCAPE:
+ case ROTATION_SEASCAPE:
+ orientation = ORIENTATION_LANDSCAPE;
+ break;
+
+ default:
+ throw new IllegalArgumentException("Unknown rotation: " + rot);
+ }
+ Configuration c = context.getResources().getConfiguration();
+ c.orientation = orientation;
+ Context rotated = context.createConfigurationContext(c);
+ return rotated.getResources();
+ }
}