Merge "Rotation-invariant status bar layout calculator" into sc-dev
This commit is contained in:
@@ -20,7 +20,7 @@
|
||||
<solid
|
||||
android:color="@color/privacy_chip_background"/>
|
||||
<size
|
||||
android:width="6dp"
|
||||
android:height="6dp"
|
||||
android:width="@dimen/ongoing_appops_dot_diameter"
|
||||
android:height="@dimen/ongoing_appops_dot_diameter"
|
||||
/>
|
||||
</shape>
|
||||
@@ -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"
|
||||
/>
|
||||
</com.android.systemui.privacy.OngoingPrivacyChip>
|
||||
@@ -1243,6 +1243,11 @@
|
||||
<dimen name="ongoing_appops_chip_icon_size">16dp</dimen>
|
||||
<!-- Radius of Ongoing App Ops chip corners -->
|
||||
<dimen name="ongoing_appops_chip_bg_corner_radius">28dp</dimen>
|
||||
<!-- One or two privacy items -->
|
||||
<dimen name="ongoing_appops_chip_min_width">56dp</dimen>
|
||||
<!-- Three privacy items. This value must not be exceeded -->
|
||||
<dimen name="ongoing_appops_chip_max_width">76dp</dimen>
|
||||
<dimen name="ongoing_appops_dot_diameter">6dp</dimen>
|
||||
|
||||
<dimen name="ongoing_appops_dialog_side_margins">@dimen/notification_shade_content_margin_horizontal</dimen>
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<StatusBarContentInsetsChangedListener>,
|
||||
ConfigurationController.ConfigurationListener,
|
||||
Dumpable {
|
||||
// Indexed by @Rotation
|
||||
private val insetsByCorner = arrayOfNulls<Rect>(4)
|
||||
private val listeners = mutableSetOf<StatusBarContentInsetsChangedListener>()
|
||||
|
||||
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<out String>) {
|
||||
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<Int, Int>
|
||||
): 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
|
||||
}
|
||||
@@ -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. */
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user