summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/res/CompatibilityInfo.java3
-rw-r--r--core/java/android/view/OrientationEventListener.java59
-rw-r--r--core/tests/coretests/src/android/view/OrientationEventListenerFrameworkTest.java201
3 files changed, 254 insertions, 9 deletions
diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java
index afcdcb0fbcad..5727fcf87144 100644
--- a/core/java/android/content/res/CompatibilityInfo.java
+++ b/core/java/android/content/res/CompatibilityInfo.java
@@ -40,8 +40,6 @@ import android.view.Surface;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
-import com.android.aconfig.annotations.VisibleForTesting;
-
/**
* CompatibilityInfo class keeps the information about the screen compatibility mode that the
* application is running under.
@@ -763,7 +761,6 @@ public class CompatibilityInfo implements Parcelable {
}
/** @see #sOverrideDisplayRotation */
- @VisibleForTesting
public static int getOverrideDisplayRotation() {
return sOverrideDisplayRotation;
}
diff --git a/core/java/android/view/OrientationEventListener.java b/core/java/android/view/OrientationEventListener.java
index cd48a4f884a8..2feb44ffc3ae 100644
--- a/core/java/android/view/OrientationEventListener.java
+++ b/core/java/android/view/OrientationEventListener.java
@@ -16,13 +16,19 @@
package android.view;
+import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
+
+import android.annotation.NonNull;
import android.content.Context;
+import android.content.res.CompatibilityInfo;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;
+import com.android.window.flags.Flags;
+
/**
* Helper class for receiving notifications from the SensorManager when
* the orientation of the device has changed.
@@ -70,8 +76,10 @@ public abstract class OrientationEventListener {
mRate = rate;
mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
if (mSensor != null) {
- // Create listener only if sensors do exist
- mSensorEventListener = new SensorEventListenerImpl();
+ // Create listener only if sensors do exist.
+ mSensorEventListener = Flags.enableCameraCompatForDesktopWindowing()
+ ? new CompatSensorEventListenerImpl(new SensorEventListenerImpl())
+ : new SensorEventListenerImpl();
}
}
@@ -114,13 +122,13 @@ public abstract class OrientationEventListener {
private static final int _DATA_X = 0;
private static final int _DATA_Y = 1;
private static final int _DATA_Z = 2;
-
+
public void onSensorChanged(SensorEvent event) {
float[] values = event.values;
int orientation = ORIENTATION_UNKNOWN;
float X = -values[_DATA_X];
float Y = -values[_DATA_Y];
- float Z = -values[_DATA_Z];
+ float Z = -values[_DATA_Z];
float magnitude = X*X + Y*Y;
// Don't trust the angle if the magnitude is small compared to the y value
if (magnitude * 4 >= Z*Z) {
@@ -130,7 +138,7 @@ public abstract class OrientationEventListener {
// normalize to 0 - 359 range
while (orientation >= 360) {
orientation -= 360;
- }
+ }
while (orientation < 0) {
orientation += 360;
}
@@ -148,7 +156,46 @@ public abstract class OrientationEventListener {
}
}
-
+
+ /** Decorator to the {@link SensorEventListenerImpl}, which provides compat values if needed. */
+ class CompatSensorEventListenerImpl implements SensorEventListener {
+ // SensorEventListener without compatibility capabilities.
+ final SensorEventListenerImpl mSensorEventListener;
+
+ CompatSensorEventListenerImpl(@NonNull SensorEventListenerImpl sensorEventListener) {
+ mSensorEventListener = sensorEventListener;
+ }
+
+ public void onSensorChanged(SensorEvent event) {
+ // If the display rotation override is set, the same override should be applied to
+ // this orientation too. This rotation override will only be set when an app has a
+ // camera open and it is in camera compat mode for desktop windowing (freeform mode).
+ // Values of this override is Surface.ROTATION_0/90/180/270, or
+ // WindowConfiguration.ROTATION_UNDEFINED when not set.
+ if (CompatibilityInfo.getOverrideDisplayRotation() != ROTATION_UNDEFINED) {
+ // SensorEventListener reports the rotation in the opposite direction from the
+ // display rotation.
+ int orientation = (360 - CompatibilityInfo.getOverrideDisplayRotation() * 90) % 360;
+ if (orientation != mOrientation) {
+ mOrientation = orientation;
+ onOrientationChanged(orientation);
+ }
+ // `mOldListener` is deprecated and returns 3D values, which are highly unlikely to
+ // be used for orienting camera image. Thus this listener is not called here, as
+ // opposed to extrapolating values from display rotation, from 1D->3D.
+ } else {
+ // Use the default implementation: calculate the orientation from event coordinates.
+ // This method will call OrientationEventListener.onOrientationChanged(orientation)
+ // if the orientation has changed.
+ mSensorEventListener.onSensorChanged(event);
+ }
+ }
+
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+
+ }
+ }
+
/*
* Returns true if sensor is enabled and false otherwise
*/
diff --git a/core/tests/coretests/src/android/view/OrientationEventListenerFrameworkTest.java b/core/tests/coretests/src/android/view/OrientationEventListenerFrameworkTest.java
new file mode 100644
index 000000000000..5f06a706b1c8
--- /dev/null
+++ b/core/tests/coretests/src/android/view/OrientationEventListenerFrameworkTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2025 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 android.view;
+
+import static com.android.window.flags.Flags.FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import android.annotation.NonNull;
+import android.app.WindowConfiguration;
+import android.content.Context;
+import android.content.res.CompatibilityInfo;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.input.InputSensorInfo;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+
+import java.util.ArrayList;
+
+/**
+ * Test {@link OrientationEventListener}.
+ *
+ * <p>Build/Install/Run:
+ * atest FrameworksCoreTests:OrientationEventListenerFrameworkTest
+ */
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class OrientationEventListenerFrameworkTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+
+ private Context mContext;
+ private SensorManager mSensorManager;
+
+ @Before
+ public void setup() {
+ mContext = mock(Context.class);
+ mSensorManager = mock(SensorManager.class);
+ doReturn(mSensorManager).when(mContext).getSystemService(Context.SENSOR_SERVICE);
+ }
+
+ @After
+ public void tearDown() {
+ // Reset the override rotation for tests that use the default value.
+ CompatibilityInfo.setOverrideDisplayRotation(WindowConfiguration.ROTATION_UNDEFINED);
+ }
+
+ @Test
+ public void testConstructor() {
+ new TestOrientationEventListener(mContext);
+
+ new TestOrientationEventListener(mContext, SensorManager.SENSOR_DELAY_UI);
+ }
+
+ @Test
+ public void testEnableAndDisable() {
+ final TestOrientationEventListener listener = new TestOrientationEventListener(mContext);
+ listener.enable();
+ listener.disable();
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ public void testSensorOrientationUpdate() {
+ final Sensor mockSensor = setupMockAccelerometerSensor();
+ final TestOrientationEventListener listener = new TestOrientationEventListener(mContext);
+
+ listener.enable();
+
+ sendSensorEventWithOrientation270(mockSensor);
+
+ assertEquals(1, listener.mReportedOrientations.size());
+ assertEquals(270, (int) listener.mReportedOrientations.get(0));
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ public void testSensorOrientationUpdate_overriddenDisplayRotationReportedWhenSet() {
+ final Sensor mockSensor = setupMockAccelerometerSensor();
+ final TestOrientationEventListener listener = new TestOrientationEventListener(mContext);
+
+ listener.enable();
+
+ // This should change the reported sensor rotation.
+ CompatibilityInfo.setOverrideDisplayRotation(Surface.ROTATION_180);
+
+ sendSensorEventWithOrientation270(mockSensor);
+
+ assertEquals(1, listener.mReportedOrientations.size());
+ assertEquals(180, (int) listener.mReportedOrientations.get(0));
+ }
+
+ @Test
+ @EnableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ public void testSensorOrientationUpdate_overriddenDisplayRotationIsNegativeFromSensor() {
+ final Sensor mockSensor = setupMockAccelerometerSensor();
+ final TestOrientationEventListener listener = new TestOrientationEventListener(mContext);
+
+ listener.enable();
+
+ // Display rotation is counted in the opposite direction from the sensor orientation, thus
+ // this call should change the reported sensor rotation to 90, as 360 - 270 = 90.
+ CompatibilityInfo.setOverrideDisplayRotation(Surface.ROTATION_270);
+
+ sendSensorEventWithOrientation270(mockSensor);
+
+ assertEquals(1, listener.mReportedOrientations.size());
+ assertEquals(90, (int) listener.mReportedOrientations.get(0));
+ }
+
+ @Test
+ @DisableFlags(FLAG_ENABLE_CAMERA_COMPAT_FOR_DESKTOP_WINDOWING)
+ public void testSensorOrientationUpdate_notOverriddenWhenCameraFeatureDisabled() {
+ final Sensor mockSensor = setupMockAccelerometerSensor();
+ final TestOrientationEventListener listener = new TestOrientationEventListener(mContext);
+
+ listener.enable();
+
+ CompatibilityInfo.setOverrideDisplayRotation(Surface.ROTATION_180);
+
+ sendSensorEventWithOrientation270(mockSensor);
+
+ assertEquals(1, listener.mReportedOrientations.size());
+ // Sensor unchanged by override because the feature is disabled.
+ assertEquals(270, (int) listener.mReportedOrientations.get(0));
+ }
+
+ @NonNull
+ private Sensor setupMockAccelerometerSensor() {
+ final Sensor mockSensor = new Sensor(mock(InputSensorInfo.class));
+ doReturn(mockSensor).when(mSensorManager).getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ return mockSensor;
+ }
+
+ @NonNull
+ private SensorEventListener getRegisteredEventListener() {
+ // Get the SensorEventListenerImpl that has subscribed in `listener.enable()`.
+ final ArgumentCaptor<SensorEventListener> listenerArgCaptor = ArgumentCaptor
+ .forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerArgCaptor.capture(), any(), anyInt());
+ return listenerArgCaptor.getValue();
+ }
+
+ private void sendSensorEventWithOrientation270(@NonNull Sensor sensor) {
+ final SensorEventListener sensorEventListener = getRegisteredEventListener();
+ // Arbitrary values that return orientation 270.
+ final SensorEvent sensorEvent = new SensorEvent(sensor, 1, 1L,
+ new float[]{1.0f, 0.0f, 0.0f});
+ sensorEventListener.onSensorChanged(sensorEvent);
+ }
+
+ private static class TestOrientationEventListener extends OrientationEventListener {
+ final ArrayList<Integer> mReportedOrientations = new ArrayList<>();
+
+ TestOrientationEventListener(Context context) {
+ super(context);
+ }
+
+ TestOrientationEventListener(Context context, int rate) {
+ super(context, rate);
+ }
+
+ @Override
+ public void onOrientationChanged(int orientation) {
+ mReportedOrientations.add(orientation);
+ }
+ }
+}