diff --git a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt index 1454210ad7983..fb0c0a69e7c84 100644 --- a/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt +++ b/packages/SystemUI/src/com/android/systemui/charging/WiredChargingRippleController.kt @@ -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 @@ -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 @@ -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) - } } } 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 0b2f004537d69..31f35fc076a1a 100644 --- a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java +++ b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java @@ -1,15 +1,17 @@ /* * 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; @@ -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. + * + *

Consider using {@link Surface.Rotation} or add a function that respects device aspect ratio + * and {@code android.internal.R.bool.config_reverseDefaultRotation}. + * + *

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.) + * + *

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. + * + *

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; diff --git a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt index d1597149ce0b4..d6cafcb6b5ae8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/charging/WiredChargingRippleControllerTest.kt @@ -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 @@ -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 @@ -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 @@ -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 @@ -164,4 +173,63 @@ class WiredChargingRippleControllerTest : SysuiTestCase() { verify(rippleView, never()).addOnAttachStateChangeListener(attachListenerCaptor.capture()) verify(windowManager, never()).addView(eq(rippleView), any()) } + + @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()) + + 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) + } }