Loading packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt +262 −178 File changed.Preview size limit exceeded, changes collapsed. Show changes packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt +147 −129 Original line number Diff line number Diff line Loading @@ -27,11 +27,17 @@ import android.util.Pair import android.view.DisplayCutout import android.view.Surface import androidx.annotation.VisibleForTesting import com.android.app.tracing.traceSection import com.android.internal.policy.SystemBarUtils import com.android.systemui.BottomMarginCommand import com.android.systemui.Dumpable import com.android.systemui.StatusBarInsetsCommand import com.android.systemui.SysUICutoutInformation import com.android.systemui.SysUICutoutProvider import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.res.R import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.statusbar.policy.CallbackController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE Loading @@ -41,12 +47,6 @@ import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN import com.android.systemui.util.leak.RotationUtils.Rotation import com.android.systemui.util.leak.RotationUtils.getExactRotation import com.android.systemui.util.leak.RotationUtils.getResourcesForRotation import com.android.app.tracing.traceSection import com.android.systemui.BottomMarginCommand import com.android.systemui.StatusBarInsetsCommand import com.android.systemui.SysUICutoutInformation import com.android.systemui.SysUICutoutProvider import com.android.systemui.statusbar.commandline.CommandRegistry import java.io.PrintWriter import java.lang.Math.max import javax.inject.Inject Loading @@ -59,20 +59,22 @@ import javax.inject.Inject * 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( class StatusBarContentInsetsProvider @Inject constructor( val context: Context, val configurationController: ConfigurationController, val dumpManager: DumpManager, val commandRegistry: CommandRegistry, val sysUICutoutProvider: SysUICutoutProvider, ) : CallbackController<StatusBarContentInsetsChangedListener>, ) : CallbackController<StatusBarContentInsetsChangedListener>, ConfigurationController.ConfigurationListener, Dumpable { Loading @@ -80,7 +82,8 @@ class StatusBarContentInsetsProvider @Inject constructor( // (e.g. network displays) private val insetsCache = LruCache<CacheKey, Rect>(MAX_CACHE_SIZE) private val listeners = mutableSetOf<StatusBarContentInsetsChangedListener>() private val isPrivacyDotEnabled: Boolean by lazy(LazyThreadSafetyMode.PUBLICATION) { private val isPrivacyDotEnabled: Boolean by lazy(LazyThreadSafetyMode.PUBLICATION) { context.resources.getBoolean(R.bool.config_enablePrivacyDot) } Loading @@ -88,11 +91,16 @@ class StatusBarContentInsetsProvider @Inject constructor( configurationController.addCallback(this) dumpManager.registerDumpable(TAG, this) commandRegistry.registerCommand(StatusBarInsetsCommand.NAME) { StatusBarInsetsCommand(object : StatusBarInsetsCommand.Callback { override fun onExecute(command: StatusBarInsetsCommand, printWriter: PrintWriter) { StatusBarInsetsCommand( object : StatusBarInsetsCommand.Callback { override fun onExecute( command: StatusBarInsetsCommand, printWriter: PrintWriter ) { executeCommand(command, printWriter) } }) } ) } } Loading Loading @@ -122,15 +130,13 @@ class StatusBarContentInsetsProvider @Inject constructor( } private fun notifyInsetsChanged() { listeners.forEach { it.onStatusBarContentInsetsChanged() } listeners.forEach { it.onStatusBarContentInsetsChanged() } } /** * Some views may need to care about whether or not the current top display cutout is located * in the corner rather than somewhere in the center. In the case of a corner cutout, the * status bar area is contiguous. * Some views may need to care about whether or not the current top display cutout is located in * the corner rather than somewhere in the center. In the case of a corner cutout, the status * bar area is contiguous. */ fun currentRotationHasCornerCutout(): Boolean { val cutout = checkNotNull(context.display).cutout ?: return false Loading @@ -147,8 +153,8 @@ class StatusBarContentInsetsProvider @Inject constructor( * dot in the coordinates relative to the given rotation. * * @param rotation the rotation for which the bounds are required. This is an absolute value * (i.e., ROTATION_NONE will always return the same bounds regardless of the context * from which this method is called) * (i.e., ROTATION_NONE will always return the same bounds regardless of the context from * which this method is called) */ fun getBoundingRectForPrivacyChipForRotation( @Rotation rotation: Int, Loading @@ -163,8 +169,8 @@ class StatusBarContentInsetsProvider @Inject constructor( val rotatedResources = getResourcesForRotation(rotation, context) val dotWidth = rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_diameter) val chipWidth = rotatedResources.getDimensionPixelSize( R.dimen.ongoing_appops_chip_max_width) val chipWidth = rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_max_width) val isRtl = configurationController.isLayoutRtl return getPrivacyChipBoundingRectForInsets(insets, dotWidth, chipWidth, isRtl) Loading @@ -190,14 +196,21 @@ class StatusBarContentInsetsProvider @Inject constructor( point.orientToRotZero(getExactRotation(context)) val width = point.logicalWidth(rotation) val area = insetsCache[key] ?: getAndSetCalculatedAreaForRotation( rotation, sysUICutout, getResourcesForRotation(rotation, context), key) val area = insetsCache[key] ?: getAndSetCalculatedAreaForRotation( rotation, sysUICutout, getResourcesForRotation(rotation, context), key ) Insets.of(area.left, area.top, /* right= */ width - area.right, /* bottom= */ 0) } /** * Calculate the insets for the status bar content in the device's current rotation * * @see getStatusBarContentAreaForRotation */ fun getStatusBarContentInsetsForCurrentRotation(): Insets { Loading @@ -205,27 +218,28 @@ class StatusBarContentInsetsProvider @Inject constructor( } /** * Calculates the area of the status bar contents invariant of the current device rotation, * in the target rotation's coordinates * Calculates the area of the status bar contents invariant of the current device rotation, in * the target rotation's coordinates * * @param rotation the rotation for which the bounds are required. This is an absolute value * (i.e., ROTATION_NONE will always return the same bounds regardless of the context * from which this method is called) * (i.e., ROTATION_NONE will always return the same bounds regardless of the context from * which this method is called) */ @JvmOverloads fun getStatusBarContentAreaForRotation( @Rotation rotation: Int ): Rect { fun getStatusBarContentAreaForRotation(@Rotation rotation: Int): Rect { val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplay() val displayCutout = sysUICutout?.cutout val key = getCacheKey(rotation, displayCutout) return insetsCache[key] ?: getAndSetCalculatedAreaForRotation( rotation, sysUICutout, getResourcesForRotation(rotation, context), key) return insetsCache[key] ?: getAndSetCalculatedAreaForRotation( rotation, sysUICutout, getResourcesForRotation(rotation, context), key ) } /** * Get the status bar content area for the given rotation, in absolute bounds */ /** Get the status bar content area for the given rotation, in absolute bounds */ fun getStatusBarContentAreaForCurrentRotation(): Rect { val rotation = getExactRotation(context) return getStatusBarContentAreaForRotation(rotation) Loading @@ -237,8 +251,7 @@ class StatusBarContentInsetsProvider @Inject constructor( rotatedResources: Resources, key: CacheKey ): Rect { return getCalculatedAreaForRotation(sysUICutout, targetRotation, rotatedResources) .also { return getCalculatedAreaForRotation(sysUICutout, targetRotation, rotatedResources).also { insetsCache.put(key, it) } } Loading @@ -250,12 +263,14 @@ class StatusBarContentInsetsProvider @Inject constructor( ): Rect { val currentRotation = getExactRotation(context) val roundedCornerPadding = rotatedResources .getDimensionPixelSize(R.dimen.rounded_corner_content_padding) val minDotPadding = if (isPrivacyDotEnabled) val roundedCornerPadding = rotatedResources.getDimensionPixelSize(R.dimen.rounded_corner_content_padding) val minDotPadding = if (isPrivacyDotEnabled) rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_min_padding) else 0 val dotWidth = if (isPrivacyDotEnabled) val dotWidth = if (isPrivacyDotEnabled) rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_diameter) else 0 Loading Loading @@ -284,7 +299,8 @@ class StatusBarContentInsetsProvider @Inject constructor( configurationController.isLayoutRtl, dotWidth, bottomAlignedMargin, statusBarContentHeight) statusBarContentHeight ) } private fun executeCommand(command: StatusBarInsetsCommand, printWriter: PrintWriter) { Loading Loading @@ -339,17 +355,12 @@ class StatusBarContentInsetsProvider @Inject constructor( } override fun dump(pw: PrintWriter, args: Array<out String>) { insetsCache.snapshot().forEach { (key, rect) -> pw.println("$key -> $rect") } insetsCache.snapshot().forEach { (key, rect) -> pw.println("$key -> $rect") } pw.println(insetsCache) pw.println("Bottom margin overrides: $marginBottomOverrides") } private fun getCacheKey( @Rotation rotation: Int, displayCutout: DisplayCutout? ): CacheKey = private fun getCacheKey(@Rotation rotation: Int, displayCutout: DisplayCutout?): CacheKey = CacheKey( rotation = rotation, displaySize = Rect(context.resources.configuration.windowConfiguration.maxBounds), Loading Loading @@ -387,15 +398,19 @@ fun getPrivacyChipBoundingRectForInsets( isRtl: Boolean ): Rect { return if (isRtl) { Rect(contentRect.left - dotWidth, Rect( contentRect.left - dotWidth, contentRect.top, contentRect.left + chipWidth, contentRect.bottom) contentRect.bottom ) } else { Rect(contentRect.right - chipWidth, Rect( contentRect.right - chipWidth, contentRect.top, contentRect.right + dotWidth, contentRect.bottom) contentRect.bottom ) } } Loading Loading @@ -452,7 +467,8 @@ fun calculateInsetsForRotationWithRotatedResources( targetRotation, currentRotation, bottomAlignedMargin, statusBarContentHeight) statusBarContentHeight ) } /** Loading @@ -470,7 +486,6 @@ fun calculateInsetsForRotationWithRotatedResources( * @param dotWidth privacy dot image width (0 if privacy dot is disabled) * @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 */ Loading Loading @@ -672,7 +687,8 @@ private fun Rect.logicalLeft(@Rotation rot: Int): Int { private fun Rect.logicalWidth(@Rotation rot: Int): Int { return when (rot) { ROTATION_NONE, ROTATION_UPSIDE_DOWN -> width() ROTATION_NONE, ROTATION_UPSIDE_DOWN -> width() else /* LANDSCAPE, SEASCAPE */ -> height() } } Loading @@ -683,7 +699,8 @@ private fun Int.isHorizontal(): Boolean { private fun Point.orientToRotZero(@Rotation rot: Int) { when (rot) { ROTATION_NONE, ROTATION_UPSIDE_DOWN -> return ROTATION_NONE, ROTATION_UPSIDE_DOWN -> return else -> { // swap width and height to zero-orient bounds val yTmp = y Loading @@ -695,7 +712,8 @@ private fun Point.orientToRotZero(@Rotation rot: Int) { private fun Point.logicalWidth(@Rotation rot: Int): Int { return when (rot) { ROTATION_NONE, ROTATION_UPSIDE_DOWN -> x ROTATION_NONE, ROTATION_UPSIDE_DOWN -> x else -> y } } Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProviderTest.kt +262 −178 File changed.Preview size limit exceeded, changes collapsed. Show changes
packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarContentInsetsProvider.kt +147 −129 Original line number Diff line number Diff line Loading @@ -27,11 +27,17 @@ import android.util.Pair import android.view.DisplayCutout import android.view.Surface import androidx.annotation.VisibleForTesting import com.android.app.tracing.traceSection import com.android.internal.policy.SystemBarUtils import com.android.systemui.BottomMarginCommand import com.android.systemui.Dumpable import com.android.systemui.StatusBarInsetsCommand import com.android.systemui.SysUICutoutInformation import com.android.systemui.SysUICutoutProvider import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager import com.android.systemui.res.R import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.statusbar.policy.CallbackController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE Loading @@ -41,12 +47,6 @@ import com.android.systemui.util.leak.RotationUtils.ROTATION_UPSIDE_DOWN import com.android.systemui.util.leak.RotationUtils.Rotation import com.android.systemui.util.leak.RotationUtils.getExactRotation import com.android.systemui.util.leak.RotationUtils.getResourcesForRotation import com.android.app.tracing.traceSection import com.android.systemui.BottomMarginCommand import com.android.systemui.StatusBarInsetsCommand import com.android.systemui.SysUICutoutInformation import com.android.systemui.SysUICutoutProvider import com.android.systemui.statusbar.commandline.CommandRegistry import java.io.PrintWriter import java.lang.Math.max import javax.inject.Inject Loading @@ -59,20 +59,22 @@ import javax.inject.Inject * 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( class StatusBarContentInsetsProvider @Inject constructor( val context: Context, val configurationController: ConfigurationController, val dumpManager: DumpManager, val commandRegistry: CommandRegistry, val sysUICutoutProvider: SysUICutoutProvider, ) : CallbackController<StatusBarContentInsetsChangedListener>, ) : CallbackController<StatusBarContentInsetsChangedListener>, ConfigurationController.ConfigurationListener, Dumpable { Loading @@ -80,7 +82,8 @@ class StatusBarContentInsetsProvider @Inject constructor( // (e.g. network displays) private val insetsCache = LruCache<CacheKey, Rect>(MAX_CACHE_SIZE) private val listeners = mutableSetOf<StatusBarContentInsetsChangedListener>() private val isPrivacyDotEnabled: Boolean by lazy(LazyThreadSafetyMode.PUBLICATION) { private val isPrivacyDotEnabled: Boolean by lazy(LazyThreadSafetyMode.PUBLICATION) { context.resources.getBoolean(R.bool.config_enablePrivacyDot) } Loading @@ -88,11 +91,16 @@ class StatusBarContentInsetsProvider @Inject constructor( configurationController.addCallback(this) dumpManager.registerDumpable(TAG, this) commandRegistry.registerCommand(StatusBarInsetsCommand.NAME) { StatusBarInsetsCommand(object : StatusBarInsetsCommand.Callback { override fun onExecute(command: StatusBarInsetsCommand, printWriter: PrintWriter) { StatusBarInsetsCommand( object : StatusBarInsetsCommand.Callback { override fun onExecute( command: StatusBarInsetsCommand, printWriter: PrintWriter ) { executeCommand(command, printWriter) } }) } ) } } Loading Loading @@ -122,15 +130,13 @@ class StatusBarContentInsetsProvider @Inject constructor( } private fun notifyInsetsChanged() { listeners.forEach { it.onStatusBarContentInsetsChanged() } listeners.forEach { it.onStatusBarContentInsetsChanged() } } /** * Some views may need to care about whether or not the current top display cutout is located * in the corner rather than somewhere in the center. In the case of a corner cutout, the * status bar area is contiguous. * Some views may need to care about whether or not the current top display cutout is located in * the corner rather than somewhere in the center. In the case of a corner cutout, the status * bar area is contiguous. */ fun currentRotationHasCornerCutout(): Boolean { val cutout = checkNotNull(context.display).cutout ?: return false Loading @@ -147,8 +153,8 @@ class StatusBarContentInsetsProvider @Inject constructor( * dot in the coordinates relative to the given rotation. * * @param rotation the rotation for which the bounds are required. This is an absolute value * (i.e., ROTATION_NONE will always return the same bounds regardless of the context * from which this method is called) * (i.e., ROTATION_NONE will always return the same bounds regardless of the context from * which this method is called) */ fun getBoundingRectForPrivacyChipForRotation( @Rotation rotation: Int, Loading @@ -163,8 +169,8 @@ class StatusBarContentInsetsProvider @Inject constructor( val rotatedResources = getResourcesForRotation(rotation, context) val dotWidth = rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_diameter) val chipWidth = rotatedResources.getDimensionPixelSize( R.dimen.ongoing_appops_chip_max_width) val chipWidth = rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_chip_max_width) val isRtl = configurationController.isLayoutRtl return getPrivacyChipBoundingRectForInsets(insets, dotWidth, chipWidth, isRtl) Loading @@ -190,14 +196,21 @@ class StatusBarContentInsetsProvider @Inject constructor( point.orientToRotZero(getExactRotation(context)) val width = point.logicalWidth(rotation) val area = insetsCache[key] ?: getAndSetCalculatedAreaForRotation( rotation, sysUICutout, getResourcesForRotation(rotation, context), key) val area = insetsCache[key] ?: getAndSetCalculatedAreaForRotation( rotation, sysUICutout, getResourcesForRotation(rotation, context), key ) Insets.of(area.left, area.top, /* right= */ width - area.right, /* bottom= */ 0) } /** * Calculate the insets for the status bar content in the device's current rotation * * @see getStatusBarContentAreaForRotation */ fun getStatusBarContentInsetsForCurrentRotation(): Insets { Loading @@ -205,27 +218,28 @@ class StatusBarContentInsetsProvider @Inject constructor( } /** * Calculates the area of the status bar contents invariant of the current device rotation, * in the target rotation's coordinates * Calculates the area of the status bar contents invariant of the current device rotation, in * the target rotation's coordinates * * @param rotation the rotation for which the bounds are required. This is an absolute value * (i.e., ROTATION_NONE will always return the same bounds regardless of the context * from which this method is called) * (i.e., ROTATION_NONE will always return the same bounds regardless of the context from * which this method is called) */ @JvmOverloads fun getStatusBarContentAreaForRotation( @Rotation rotation: Int ): Rect { fun getStatusBarContentAreaForRotation(@Rotation rotation: Int): Rect { val sysUICutout = sysUICutoutProvider.cutoutInfoForCurrentDisplay() val displayCutout = sysUICutout?.cutout val key = getCacheKey(rotation, displayCutout) return insetsCache[key] ?: getAndSetCalculatedAreaForRotation( rotation, sysUICutout, getResourcesForRotation(rotation, context), key) return insetsCache[key] ?: getAndSetCalculatedAreaForRotation( rotation, sysUICutout, getResourcesForRotation(rotation, context), key ) } /** * Get the status bar content area for the given rotation, in absolute bounds */ /** Get the status bar content area for the given rotation, in absolute bounds */ fun getStatusBarContentAreaForCurrentRotation(): Rect { val rotation = getExactRotation(context) return getStatusBarContentAreaForRotation(rotation) Loading @@ -237,8 +251,7 @@ class StatusBarContentInsetsProvider @Inject constructor( rotatedResources: Resources, key: CacheKey ): Rect { return getCalculatedAreaForRotation(sysUICutout, targetRotation, rotatedResources) .also { return getCalculatedAreaForRotation(sysUICutout, targetRotation, rotatedResources).also { insetsCache.put(key, it) } } Loading @@ -250,12 +263,14 @@ class StatusBarContentInsetsProvider @Inject constructor( ): Rect { val currentRotation = getExactRotation(context) val roundedCornerPadding = rotatedResources .getDimensionPixelSize(R.dimen.rounded_corner_content_padding) val minDotPadding = if (isPrivacyDotEnabled) val roundedCornerPadding = rotatedResources.getDimensionPixelSize(R.dimen.rounded_corner_content_padding) val minDotPadding = if (isPrivacyDotEnabled) rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_min_padding) else 0 val dotWidth = if (isPrivacyDotEnabled) val dotWidth = if (isPrivacyDotEnabled) rotatedResources.getDimensionPixelSize(R.dimen.ongoing_appops_dot_diameter) else 0 Loading Loading @@ -284,7 +299,8 @@ class StatusBarContentInsetsProvider @Inject constructor( configurationController.isLayoutRtl, dotWidth, bottomAlignedMargin, statusBarContentHeight) statusBarContentHeight ) } private fun executeCommand(command: StatusBarInsetsCommand, printWriter: PrintWriter) { Loading Loading @@ -339,17 +355,12 @@ class StatusBarContentInsetsProvider @Inject constructor( } override fun dump(pw: PrintWriter, args: Array<out String>) { insetsCache.snapshot().forEach { (key, rect) -> pw.println("$key -> $rect") } insetsCache.snapshot().forEach { (key, rect) -> pw.println("$key -> $rect") } pw.println(insetsCache) pw.println("Bottom margin overrides: $marginBottomOverrides") } private fun getCacheKey( @Rotation rotation: Int, displayCutout: DisplayCutout? ): CacheKey = private fun getCacheKey(@Rotation rotation: Int, displayCutout: DisplayCutout?): CacheKey = CacheKey( rotation = rotation, displaySize = Rect(context.resources.configuration.windowConfiguration.maxBounds), Loading Loading @@ -387,15 +398,19 @@ fun getPrivacyChipBoundingRectForInsets( isRtl: Boolean ): Rect { return if (isRtl) { Rect(contentRect.left - dotWidth, Rect( contentRect.left - dotWidth, contentRect.top, contentRect.left + chipWidth, contentRect.bottom) contentRect.bottom ) } else { Rect(contentRect.right - chipWidth, Rect( contentRect.right - chipWidth, contentRect.top, contentRect.right + dotWidth, contentRect.bottom) contentRect.bottom ) } } Loading Loading @@ -452,7 +467,8 @@ fun calculateInsetsForRotationWithRotatedResources( targetRotation, currentRotation, bottomAlignedMargin, statusBarContentHeight) statusBarContentHeight ) } /** Loading @@ -470,7 +486,6 @@ fun calculateInsetsForRotationWithRotatedResources( * @param dotWidth privacy dot image width (0 if privacy dot is disabled) * @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 */ Loading Loading @@ -672,7 +687,8 @@ private fun Rect.logicalLeft(@Rotation rot: Int): Int { private fun Rect.logicalWidth(@Rotation rot: Int): Int { return when (rot) { ROTATION_NONE, ROTATION_UPSIDE_DOWN -> width() ROTATION_NONE, ROTATION_UPSIDE_DOWN -> width() else /* LANDSCAPE, SEASCAPE */ -> height() } } Loading @@ -683,7 +699,8 @@ private fun Int.isHorizontal(): Boolean { private fun Point.orientToRotZero(@Rotation rot: Int) { when (rot) { ROTATION_NONE, ROTATION_UPSIDE_DOWN -> return ROTATION_NONE, ROTATION_UPSIDE_DOWN -> return else -> { // swap width and height to zero-orient bounds val yTmp = y Loading @@ -695,7 +712,8 @@ private fun Point.orientToRotZero(@Rotation rot: Int) { private fun Point.logicalWidth(@Rotation rot: Int): Int { return when (rot) { ROTATION_NONE, ROTATION_UPSIDE_DOWN -> x ROTATION_NONE, ROTATION_UPSIDE_DOWN -> x else -> y } }