Loading packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt +12 −15 Original line number Diff line number Diff line Loading @@ -20,7 +20,7 @@ import android.content.Context import android.content.res.Configuration import android.graphics.PixelFormat import android.os.SystemProperties import android.util.DisplayMetrics import android.view.Surface import android.view.View import android.view.WindowManager import com.android.internal.annotations.VisibleForTesting Loading @@ -36,7 +36,6 @@ import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.leak.RotationUtils import com.android.systemui.util.time.SystemClock import java.io.PrintWriter import javax.inject.Inject Loading Loading @@ -172,30 +171,28 @@ class WiredChargingRippleController @Inject constructor( } private fun layoutRipple() { val displayMetrics = DisplayMetrics() context.display.getRealMetrics(displayMetrics) val width = displayMetrics.widthPixels val height = displayMetrics.heightPixels val bounds = windowManager.currentWindowMetrics.bounds val width = bounds.width() val height = bounds.height() val maxDiameter = Integer.max(width, height) * 2f rippleView.setMaxSize(maxDiameter, maxDiameter) when (RotationUtils.getExactRotation(context)) { RotationUtils.ROTATION_LANDSCAPE -> { when (context.display.rotation) { Surface.ROTATION_0 -> { rippleView.setCenter( width * normalizedPortPosX, height * normalizedPortPosY) } Surface.ROTATION_90 -> { rippleView.setCenter( width * normalizedPortPosY, height * (1 - normalizedPortPosX)) } RotationUtils.ROTATION_UPSIDE_DOWN -> { Surface.ROTATION_180 -> { rippleView.setCenter( width * (1 - normalizedPortPosX), height * (1 - normalizedPortPosY)) } RotationUtils.ROTATION_SEASCAPE -> { Surface.ROTATION_270 -> { rippleView.setCenter( width * (1 - normalizedPortPosY), height * normalizedPortPosX) } else -> { // ROTATION_NONE rippleView.setCenter( width * normalizedPortPosX, height * normalizedPortPosY) } } } Loading packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java +29 −7 Original line number Diff line number Diff line /* * Copyright (C) 2017 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 * 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. * 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.util.leak; Loading @@ -26,7 +28,27 @@ import android.view.Surface; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; public class RotationUtils { /** * Utility class that provides device orientation. * * <p>Consider using {@link Surface.Rotation} or add a function that respects device aspect ratio * and {@code android.internal.R.bool.config_reverseDefaultRotation}. * * <p>If you only care about the rotation, use {@link Surface.Rotation}, as it always gives the * counter clock-wise rotation. (e.g. If you have a device that has a charging port at the bottom, * rotating three times in counter clock direction will give you {@link Surface#ROTATION_270} while * having the charging port on the left side of the device.) * * <p>If you need whether the device is in portrait or landscape (or their opposites), please add a * function here that respects the device aspect ratio and * {@code android.internal.R.bool.config_reverseDefaultRotation} together. * * <p>Note that {@code android.internal.R.bool.config_reverseDefaultRotation} does not change the * winding order. In other words, the rotation order (counter clock-wise) will remain the same. It * only flips the device orientation, such that portrait becomes upside down, landscape becomes * seascape. */ public final class RotationUtils { public static final int ROTATION_NONE = 0; public static final int ROTATION_LANDSCAPE = 1; Loading packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt +70 −2 Original line number Diff line number Diff line Loading @@ -16,18 +16,23 @@ package com.android.systemui.charging import android.graphics.Rect import android.testing.AndroidTestingRunner import android.view.Surface import android.view.View import android.view.WindowManager import android.view.WindowMetrics import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.surfaceeffects.ripple.RippleView import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.surfaceeffects.ripple.RippleView import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import org.junit.Before import org.junit.Test Loading @@ -35,12 +40,12 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.Mockito.any import org.mockito.Mockito.eq import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest Loading @@ -54,6 +59,7 @@ class WiredChargingRippleControllerTest : SysuiTestCase() { @Mock private lateinit var rippleView: RippleView @Mock private lateinit var windowManager: WindowManager @Mock private lateinit var uiEventLogger: UiEventLogger @Mock private lateinit var windowMetrics: WindowMetrics private val systemClock = FakeSystemClock() @Before Loading @@ -66,6 +72,9 @@ class WiredChargingRippleControllerTest : SysuiTestCase() { rippleView.setupShader() controller.rippleView = rippleView // Replace the real ripple view with a mock instance controller.registerCallbacks() `when`(windowMetrics.bounds).thenReturn(Rect(0, 0, 100, 100)) `when`(windowManager.currentWindowMetrics).thenReturn(windowMetrics) } @Test Loading Loading @@ -164,4 +173,63 @@ class WiredChargingRippleControllerTest : SysuiTestCase() { verify(rippleView, never()).addOnAttachStateChangeListener(attachListenerCaptor.capture()) verify(windowManager, never()).addView(eq(rippleView), any<WindowManager.LayoutParams>()) } @Test fun testRipple_layoutsCorrectly() { // Sets the correct ripple size. val width = 100 val height = 200 whenever(windowMetrics.bounds).thenReturn(Rect(0, 0, width, height)) // Trigger ripple. val captor = ArgumentCaptor .forClass(BatteryController.BatteryStateChangeCallback::class.java) verify(batteryController).addCallback(captor.capture()) captor.value.onBatteryLevelChanged( /* unusedBatteryLevel= */ 0, /* plugged in= */ true, /* charging= */ false) val attachListenerCaptor = ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java) verify(rippleView).addOnAttachStateChangeListener(attachListenerCaptor.capture()) verify(windowManager).addView(eq(rippleView), any<WindowManager.LayoutParams>()) val runnableCaptor = ArgumentCaptor.forClass(Runnable::class.java) attachListenerCaptor.value.onViewAttachedToWindow(rippleView) verify(rippleView).startRipple(runnableCaptor.capture()) // Verify size and center position. val maxSize = 400f // Double the max value between width and height. verify(rippleView).setMaxSize(maxWidth = maxSize, maxHeight = maxSize) val normalizedPortPosX = context.resources.getFloat(R.dimen.physical_charger_port_location_normalized_x) val normalizedPortPosY = context.resources.getFloat(R.dimen.physical_charger_port_location_normalized_y) val expectedCenterX: Float val expectedCenterY: Float when (context.display.rotation) { Surface.ROTATION_90 -> { expectedCenterX = width * normalizedPortPosY expectedCenterY = height * (1 - normalizedPortPosX) } Surface.ROTATION_180 -> { expectedCenterX = width * (1 - normalizedPortPosX) expectedCenterY = height * (1 - normalizedPortPosY) } Surface.ROTATION_270 -> { expectedCenterX = width * (1 - normalizedPortPosY) expectedCenterY = height * normalizedPortPosX } else -> { // Surface.ROTATION_0 expectedCenterX = width * normalizedPortPosX expectedCenterY = height * normalizedPortPosY } } verify(rippleView).setCenter(expectedCenterX, expectedCenterY) } } Loading
packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt +12 −15 Original line number Diff line number Diff line Loading @@ -20,7 +20,7 @@ import android.content.Context import android.content.res.Configuration import android.graphics.PixelFormat import android.os.SystemProperties import android.util.DisplayMetrics import android.view.Surface import android.view.View import android.view.WindowManager import com.android.internal.annotations.VisibleForTesting Loading @@ -36,7 +36,6 @@ import com.android.systemui.statusbar.commandline.Command import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.util.leak.RotationUtils import com.android.systemui.util.time.SystemClock import java.io.PrintWriter import javax.inject.Inject Loading Loading @@ -172,30 +171,28 @@ class WiredChargingRippleController @Inject constructor( } private fun layoutRipple() { val displayMetrics = DisplayMetrics() context.display.getRealMetrics(displayMetrics) val width = displayMetrics.widthPixels val height = displayMetrics.heightPixels val bounds = windowManager.currentWindowMetrics.bounds val width = bounds.width() val height = bounds.height() val maxDiameter = Integer.max(width, height) * 2f rippleView.setMaxSize(maxDiameter, maxDiameter) when (RotationUtils.getExactRotation(context)) { RotationUtils.ROTATION_LANDSCAPE -> { when (context.display.rotation) { Surface.ROTATION_0 -> { rippleView.setCenter( width * normalizedPortPosX, height * normalizedPortPosY) } Surface.ROTATION_90 -> { rippleView.setCenter( width * normalizedPortPosY, height * (1 - normalizedPortPosX)) } RotationUtils.ROTATION_UPSIDE_DOWN -> { Surface.ROTATION_180 -> { rippleView.setCenter( width * (1 - normalizedPortPosX), height * (1 - normalizedPortPosY)) } RotationUtils.ROTATION_SEASCAPE -> { Surface.ROTATION_270 -> { rippleView.setCenter( width * (1 - normalizedPortPosY), height * normalizedPortPosX) } else -> { // ROTATION_NONE rippleView.setCenter( width * normalizedPortPosX, height * normalizedPortPosY) } } } Loading
packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java +29 −7 Original line number Diff line number Diff line /* * Copyright (C) 2017 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 * 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. * 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.util.leak; Loading @@ -26,7 +28,27 @@ import android.view.Surface; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; public class RotationUtils { /** * Utility class that provides device orientation. * * <p>Consider using {@link Surface.Rotation} or add a function that respects device aspect ratio * and {@code android.internal.R.bool.config_reverseDefaultRotation}. * * <p>If you only care about the rotation, use {@link Surface.Rotation}, as it always gives the * counter clock-wise rotation. (e.g. If you have a device that has a charging port at the bottom, * rotating three times in counter clock direction will give you {@link Surface#ROTATION_270} while * having the charging port on the left side of the device.) * * <p>If you need whether the device is in portrait or landscape (or their opposites), please add a * function here that respects the device aspect ratio and * {@code android.internal.R.bool.config_reverseDefaultRotation} together. * * <p>Note that {@code android.internal.R.bool.config_reverseDefaultRotation} does not change the * winding order. In other words, the rotation order (counter clock-wise) will remain the same. It * only flips the device orientation, such that portrait becomes upside down, landscape becomes * seascape. */ public final class RotationUtils { public static final int ROTATION_NONE = 0; public static final int ROTATION_LANDSCAPE = 1; Loading
packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt +70 −2 Original line number Diff line number Diff line Loading @@ -16,18 +16,23 @@ package com.android.systemui.charging import android.graphics.Rect import android.testing.AndroidTestingRunner import android.view.Surface import android.view.View import android.view.WindowManager import android.view.WindowMetrics import androidx.test.filters.SmallTest import com.android.internal.logging.UiEventLogger import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.surfaceeffects.ripple.RippleView import com.android.systemui.statusbar.commandline.CommandRegistry import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.surfaceeffects.ripple.RippleView import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import org.junit.Before import org.junit.Test Loading @@ -35,12 +40,12 @@ import org.junit.runner.RunWith import org.mockito.ArgumentCaptor import org.mockito.ArgumentMatchers import org.mockito.Mock import org.mockito.Mockito.`when` import org.mockito.Mockito.any import org.mockito.Mockito.eq import org.mockito.Mockito.never import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.MockitoAnnotations @SmallTest Loading @@ -54,6 +59,7 @@ class WiredChargingRippleControllerTest : SysuiTestCase() { @Mock private lateinit var rippleView: RippleView @Mock private lateinit var windowManager: WindowManager @Mock private lateinit var uiEventLogger: UiEventLogger @Mock private lateinit var windowMetrics: WindowMetrics private val systemClock = FakeSystemClock() @Before Loading @@ -66,6 +72,9 @@ class WiredChargingRippleControllerTest : SysuiTestCase() { rippleView.setupShader() controller.rippleView = rippleView // Replace the real ripple view with a mock instance controller.registerCallbacks() `when`(windowMetrics.bounds).thenReturn(Rect(0, 0, 100, 100)) `when`(windowManager.currentWindowMetrics).thenReturn(windowMetrics) } @Test Loading Loading @@ -164,4 +173,63 @@ class WiredChargingRippleControllerTest : SysuiTestCase() { verify(rippleView, never()).addOnAttachStateChangeListener(attachListenerCaptor.capture()) verify(windowManager, never()).addView(eq(rippleView), any<WindowManager.LayoutParams>()) } @Test fun testRipple_layoutsCorrectly() { // Sets the correct ripple size. val width = 100 val height = 200 whenever(windowMetrics.bounds).thenReturn(Rect(0, 0, width, height)) // Trigger ripple. val captor = ArgumentCaptor .forClass(BatteryController.BatteryStateChangeCallback::class.java) verify(batteryController).addCallback(captor.capture()) captor.value.onBatteryLevelChanged( /* unusedBatteryLevel= */ 0, /* plugged in= */ true, /* charging= */ false) val attachListenerCaptor = ArgumentCaptor.forClass(View.OnAttachStateChangeListener::class.java) verify(rippleView).addOnAttachStateChangeListener(attachListenerCaptor.capture()) verify(windowManager).addView(eq(rippleView), any<WindowManager.LayoutParams>()) val runnableCaptor = ArgumentCaptor.forClass(Runnable::class.java) attachListenerCaptor.value.onViewAttachedToWindow(rippleView) verify(rippleView).startRipple(runnableCaptor.capture()) // Verify size and center position. val maxSize = 400f // Double the max value between width and height. verify(rippleView).setMaxSize(maxWidth = maxSize, maxHeight = maxSize) val normalizedPortPosX = context.resources.getFloat(R.dimen.physical_charger_port_location_normalized_x) val normalizedPortPosY = context.resources.getFloat(R.dimen.physical_charger_port_location_normalized_y) val expectedCenterX: Float val expectedCenterY: Float when (context.display.rotation) { Surface.ROTATION_90 -> { expectedCenterX = width * normalizedPortPosY expectedCenterY = height * (1 - normalizedPortPosX) } Surface.ROTATION_180 -> { expectedCenterX = width * (1 - normalizedPortPosX) expectedCenterY = height * (1 - normalizedPortPosY) } Surface.ROTATION_270 -> { expectedCenterX = width * (1 - normalizedPortPosY) expectedCenterY = height * normalizedPortPosX } else -> { // Surface.ROTATION_0 expectedCenterX = width * normalizedPortPosX expectedCenterY = height * normalizedPortPosY } } verify(rippleView).setCenter(expectedCenterX, expectedCenterY) } }