Merge "Replace RotationUtils#Rotation with Surface#Rotation for wired charging animation." into tm-qpr-dev

This commit is contained in:
Yein Jo
2023-01-20 05:39:06 +00:00
committed by Android (Google) Code Review
3 changed files with 111 additions and 24 deletions

View File

@@ -20,7 +20,7 @@ import android.content.Context
import android.content.res.Configuration import android.content.res.Configuration
import android.graphics.PixelFormat import android.graphics.PixelFormat
import android.os.SystemProperties import android.os.SystemProperties
import android.util.DisplayMetrics import android.view.Surface
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import com.android.internal.annotations.VisibleForTesting 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.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.leak.RotationUtils
import com.android.systemui.util.time.SystemClock import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter import java.io.PrintWriter
import javax.inject.Inject import javax.inject.Inject
@@ -172,30 +171,28 @@ class WiredChargingRippleController @Inject constructor(
} }
private fun layoutRipple() { private fun layoutRipple() {
val displayMetrics = DisplayMetrics() val bounds = windowManager.currentWindowMetrics.bounds
context.display.getRealMetrics(displayMetrics) val width = bounds.width()
val width = displayMetrics.widthPixels val height = bounds.height()
val height = displayMetrics.heightPixels
val maxDiameter = Integer.max(width, height) * 2f val maxDiameter = Integer.max(width, height) * 2f
rippleView.setMaxSize(maxDiameter, maxDiameter) rippleView.setMaxSize(maxDiameter, maxDiameter)
when (RotationUtils.getExactRotation(context)) { when (context.display.rotation) {
RotationUtils.ROTATION_LANDSCAPE -> { Surface.ROTATION_0 -> {
rippleView.setCenter(
width * normalizedPortPosX, height * normalizedPortPosY)
}
Surface.ROTATION_90 -> {
rippleView.setCenter( rippleView.setCenter(
width * normalizedPortPosY, height * (1 - normalizedPortPosX)) width * normalizedPortPosY, height * (1 - normalizedPortPosX))
} }
RotationUtils.ROTATION_UPSIDE_DOWN -> { Surface.ROTATION_180 -> {
rippleView.setCenter( rippleView.setCenter(
width * (1 - normalizedPortPosX), height * (1 - normalizedPortPosY)) width * (1 - normalizedPortPosX), height * (1 - normalizedPortPosY))
} }
RotationUtils.ROTATION_SEASCAPE -> { Surface.ROTATION_270 -> {
rippleView.setCenter( rippleView.setCenter(
width * (1 - normalizedPortPosY), height * normalizedPortPosX) width * (1 - normalizedPortPosY), height * normalizedPortPosX)
} }
else -> {
// ROTATION_NONE
rippleView.setCenter(
width * normalizedPortPosX, height * normalizedPortPosY)
}
} }
} }

View File

@@ -1,15 +1,17 @@
/* /*
* Copyright (C) 2017 The Android Open Source Project * Copyright (C) 2017 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file * Licensed under the Apache License, Version 2.0 (the "License");
* except in compliance with the License. You may obtain a copy of the License at * 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 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software distributed under the * Unless required by applicable law or agreed to in writing, software
* License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * distributed under the License is distributed on an "AS IS" BASIS,
* KIND, either express or implied. See the License for the specific language governing * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* permissions and limitations under the License. * See the License for the specific language governing permissions and
* limitations under the License.
*/ */
package com.android.systemui.util.leak; package com.android.systemui.util.leak;
@@ -26,7 +28,27 @@ import android.view.Surface;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; 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_NONE = 0;
public static final int ROTATION_LANDSCAPE = 1; public static final int ROTATION_LANDSCAPE = 1;

View File

@@ -16,18 +16,23 @@
package com.android.systemui.charging package com.android.systemui.charging
import android.graphics.Rect
import android.testing.AndroidTestingRunner import android.testing.AndroidTestingRunner
import android.view.Surface
import android.view.View import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.view.WindowMetrics
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.internal.logging.UiEventLogger import com.android.internal.logging.UiEventLogger
import com.android.systemui.R
import com.android.systemui.SysuiTestCase import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags 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.commandline.CommandRegistry
import com.android.systemui.statusbar.policy.BatteryController import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController 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 com.android.systemui.util.time.FakeSystemClock
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@@ -35,12 +40,12 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers import org.mockito.ArgumentMatchers
import org.mockito.Mock import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.any import org.mockito.Mockito.any
import org.mockito.Mockito.eq import org.mockito.Mockito.eq
import org.mockito.Mockito.never import org.mockito.Mockito.never
import org.mockito.Mockito.reset import org.mockito.Mockito.reset
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations import org.mockito.MockitoAnnotations
@SmallTest @SmallTest
@@ -54,6 +59,7 @@ class WiredChargingRippleControllerTest : SysuiTestCase() {
@Mock private lateinit var rippleView: RippleView @Mock private lateinit var rippleView: RippleView
@Mock private lateinit var windowManager: WindowManager @Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var uiEventLogger: UiEventLogger @Mock private lateinit var uiEventLogger: UiEventLogger
@Mock private lateinit var windowMetrics: WindowMetrics
private val systemClock = FakeSystemClock() private val systemClock = FakeSystemClock()
@Before @Before
@@ -66,6 +72,9 @@ class WiredChargingRippleControllerTest : SysuiTestCase() {
rippleView.setupShader() rippleView.setupShader()
controller.rippleView = rippleView // Replace the real ripple view with a mock instance controller.rippleView = rippleView // Replace the real ripple view with a mock instance
controller.registerCallbacks() controller.registerCallbacks()
`when`(windowMetrics.bounds).thenReturn(Rect(0, 0, 100, 100))
`when`(windowManager.currentWindowMetrics).thenReturn(windowMetrics)
} }
@Test @Test
@@ -164,4 +173,63 @@ class WiredChargingRippleControllerTest : SysuiTestCase() {
verify(rippleView, never()).addOnAttachStateChangeListener(attachListenerCaptor.capture()) verify(rippleView, never()).addOnAttachStateChangeListener(attachListenerCaptor.capture())
verify(windowManager, never()).addView(eq(rippleView), any<WindowManager.LayoutParams>()) 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)
}
} }