Merge "Prevent autorotation during hinge movement" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
74b7ca81aa
@@ -696,6 +696,20 @@
|
|||||||
rotation. -->
|
rotation. -->
|
||||||
<bool name="config_windowManagerHalfFoldAutoRotateOverride">false</bool>
|
<bool name="config_windowManagerHalfFoldAutoRotateOverride">false</bool>
|
||||||
|
|
||||||
|
<!-- Indicates whether the window manager pauses autorotation when folding or unfolding
|
||||||
|
a foldable device based on hinge angle sensor events and physical display switch events. -->
|
||||||
|
<bool name="config_windowManagerPauseRotationWhenUnfolding">false</bool>
|
||||||
|
|
||||||
|
<!-- Amount of time during which autorotation will be disabled since last hinge angle event -->
|
||||||
|
<integer name="config_pauseRotationWhenUnfolding_maxHingeAngle">0</integer>
|
||||||
|
|
||||||
|
<!-- Maximum hinge angle event to be considered to disable autorotation when folding or
|
||||||
|
unfolding -->
|
||||||
|
<integer name="config_pauseRotationWhenUnfolding_hingeEventTimeout">0</integer>
|
||||||
|
|
||||||
|
<!-- Amount of time during which autorotation will be disabled since last display switch -->
|
||||||
|
<integer name="config_pauseRotationWhenUnfolding_displaySwitchTimeout">0</integer>
|
||||||
|
|
||||||
<!-- When a device enters any of these states, it should be woken up. States are defined in
|
<!-- When a device enters any of these states, it should be woken up. States are defined in
|
||||||
device_state_configuration.xml. -->
|
device_state_configuration.xml. -->
|
||||||
<integer-array name="config_deviceStatesOnWhichToWakeUp">
|
<integer-array name="config_deviceStatesOnWhichToWakeUp">
|
||||||
|
|||||||
@@ -4036,6 +4036,10 @@
|
|||||||
<java-symbol type="array" name="config_halfFoldedDeviceStates" />
|
<java-symbol type="array" name="config_halfFoldedDeviceStates" />
|
||||||
<java-symbol type="array" name="config_rearDisplayDeviceStates" />
|
<java-symbol type="array" name="config_rearDisplayDeviceStates" />
|
||||||
<java-symbol type="bool" name="config_windowManagerHalfFoldAutoRotateOverride" />
|
<java-symbol type="bool" name="config_windowManagerHalfFoldAutoRotateOverride" />
|
||||||
|
<java-symbol type="bool" name="config_windowManagerPauseRotationWhenUnfolding" />
|
||||||
|
<java-symbol type="integer" name="config_pauseRotationWhenUnfolding_hingeEventTimeout" />
|
||||||
|
<java-symbol type="integer" name="config_pauseRotationWhenUnfolding_maxHingeAngle" />
|
||||||
|
<java-symbol type="integer" name="config_pauseRotationWhenUnfolding_displaySwitchTimeout" />
|
||||||
<java-symbol type="array" name="config_deviceStatesOnWhichToWakeUp" />
|
<java-symbol type="array" name="config_deviceStatesOnWhichToWakeUp" />
|
||||||
<java-symbol type="array" name="config_deviceStatesOnWhichToSleep" />
|
<java-symbol type="array" name="config_deviceStatesOnWhichToSleep" />
|
||||||
<java-symbol type="string" name="config_foldedArea" />
|
<java-symbol type="string" name="config_foldedArea" />
|
||||||
|
|||||||
@@ -2885,6 +2885,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
|||||||
/* includeRotationSettings */ false);
|
/* includeRotationSettings */ false);
|
||||||
mDisplaySwitchTransitionLauncher.requestDisplaySwitchTransitionIfNeeded(mDisplayId,
|
mDisplaySwitchTransitionLauncher.requestDisplaySwitchTransitionIfNeeded(mDisplayId,
|
||||||
mInitialDisplayWidth, mInitialDisplayHeight, newWidth, newHeight);
|
mInitialDisplayWidth, mInitialDisplayHeight, newWidth, newHeight);
|
||||||
|
mDisplayRotation.physicalDisplayChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is an override set for base values - use it, otherwise use new values.
|
// If there is an override set for base values - use it, otherwise use new values.
|
||||||
@@ -3262,6 +3263,7 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
|||||||
mWindowingLayer.release();
|
mWindowingLayer.release();
|
||||||
mInputMonitor.onDisplayRemoved();
|
mInputMonitor.onDisplayRemoved();
|
||||||
mWmService.mDisplayNotificationController.dispatchDisplayRemoved(this);
|
mWmService.mDisplayNotificationController.dispatchDisplayRemoved(this);
|
||||||
|
mDisplayRotation.onDisplayRemoved();
|
||||||
mWmService.mAccessibilityController.onDisplayRemoved(mDisplayId);
|
mWmService.mAccessibilityController.onDisplayRemoved(mDisplayId);
|
||||||
mRootWindowContainer.mTaskSupervisor
|
mRootWindowContainer.mTaskSupervisor
|
||||||
.getKeyguardController().onDisplayRemoved(mDisplayId);
|
.getKeyguardController().onDisplayRemoved(mDisplayId);
|
||||||
|
|||||||
@@ -52,8 +52,13 @@ import android.content.pm.ActivityInfo.ScreenOrientation;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.database.ContentObserver;
|
import android.database.ContentObserver;
|
||||||
|
import android.hardware.Sensor;
|
||||||
|
import android.hardware.SensorEvent;
|
||||||
|
import android.hardware.SensorEventListener;
|
||||||
|
import android.hardware.SensorManager;
|
||||||
import android.hardware.power.Boost;
|
import android.hardware.power.Boost;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
import android.os.SystemClock;
|
||||||
import android.os.SystemProperties;
|
import android.os.SystemProperties;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
@@ -1052,6 +1057,10 @@ public class DisplayRotation {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mFoldController != null && mFoldController.shouldDisableRotationSensor()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (mSupportAutoRotation) {
|
if (mSupportAutoRotation) {
|
||||||
if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
|
if (mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR
|
||||||
|| mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
|
|| mCurrentAppOrientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR
|
||||||
@@ -1149,6 +1158,9 @@ public class DisplayRotation {
|
|||||||
int sensorRotation = mOrientationListener != null
|
int sensorRotation = mOrientationListener != null
|
||||||
? mOrientationListener.getProposedRotation() // may be -1
|
? mOrientationListener.getProposedRotation() // may be -1
|
||||||
: -1;
|
: -1;
|
||||||
|
if (mFoldController != null && mFoldController.shouldIgnoreSensorRotation()) {
|
||||||
|
sensorRotation = -1;
|
||||||
|
}
|
||||||
if (mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()) {
|
if (mDeviceStateController.shouldReverseRotationDirectionAroundZAxis()) {
|
||||||
// Flipping 270 and 90 has the same effect as changing the direction which rotation is
|
// Flipping 270 and 90 has the same effect as changing the direction which rotation is
|
||||||
// applied.
|
// applied.
|
||||||
@@ -1396,6 +1408,11 @@ public class DisplayRotation {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do not show rotation choice when fold controller blocks rotation sensor
|
||||||
|
if (mFoldController != null && mFoldController.shouldIgnoreSensorRotation()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Don't show rotation choice if we are in tabletop or book modes.
|
// Don't show rotation choice if we are in tabletop or book modes.
|
||||||
if (isTabletopAutoRotateOverrideEnabled()) return false;
|
if (isTabletopAutoRotateOverrideEnabled()) return false;
|
||||||
|
|
||||||
@@ -1489,6 +1506,12 @@ public class DisplayRotation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void onDisplayRemoved() {
|
||||||
|
if (mFoldController != null) {
|
||||||
|
mFoldController.onDisplayRemoved();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Return whether the rotation settings has changed. */
|
/** Return whether the rotation settings has changed. */
|
||||||
private boolean updateSettings() {
|
private boolean updateSettings() {
|
||||||
final ContentResolver resolver = mContext.getContentResolver();
|
final ContentResolver resolver = mContext.getContentResolver();
|
||||||
@@ -1578,6 +1601,22 @@ public class DisplayRotation {
|
|||||||
pw.println(prefix + " mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation));
|
pw.println(prefix + " mLidOpenRotation=" + Surface.rotationToString(mLidOpenRotation));
|
||||||
pw.println(prefix + " mFixedToUserRotation=" + isFixedToUserRotation());
|
pw.println(prefix + " mFixedToUserRotation=" + isFixedToUserRotation());
|
||||||
|
|
||||||
|
if (mFoldController != null) {
|
||||||
|
pw.println(prefix + "FoldController");
|
||||||
|
pw.println(prefix + " mPauseAutorotationDuringUnfolding="
|
||||||
|
+ mFoldController.mPauseAutorotationDuringUnfolding);
|
||||||
|
pw.println(prefix + " mShouldDisableRotationSensor="
|
||||||
|
+ mFoldController.mShouldDisableRotationSensor);
|
||||||
|
pw.println(prefix + " mShouldIgnoreSensorRotation="
|
||||||
|
+ mFoldController.mShouldIgnoreSensorRotation);
|
||||||
|
pw.println(prefix + " mLastDisplaySwitchTime="
|
||||||
|
+ mFoldController.mLastDisplaySwitchTime);
|
||||||
|
pw.println(prefix + " mLastHingeAngleEventTime="
|
||||||
|
+ mFoldController.mLastHingeAngleEventTime);
|
||||||
|
pw.println(prefix + " mDeviceState="
|
||||||
|
+ mFoldController.mDeviceState);
|
||||||
|
}
|
||||||
|
|
||||||
if (!mRotationHistory.mRecords.isEmpty()) {
|
if (!mRotationHistory.mRecords.isEmpty()) {
|
||||||
pw.println();
|
pw.println();
|
||||||
pw.println(prefix + " RotationHistory");
|
pw.println(prefix + " RotationHistory");
|
||||||
@@ -1619,13 +1658,37 @@ public class DisplayRotation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FoldController {
|
/**
|
||||||
|
* Called by the DisplayContent when the physical display changes
|
||||||
|
*/
|
||||||
|
void physicalDisplayChanged() {
|
||||||
|
if (mFoldController != null) {
|
||||||
|
mFoldController.onPhysicalDisplayChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
long uptimeMillis() {
|
||||||
|
return SystemClock.uptimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
class FoldController {
|
||||||
|
private final boolean mPauseAutorotationDuringUnfolding;
|
||||||
@Surface.Rotation
|
@Surface.Rotation
|
||||||
private int mHalfFoldSavedRotation = -1; // No saved rotation
|
private int mHalfFoldSavedRotation = -1; // No saved rotation
|
||||||
private DeviceStateController.DeviceState mDeviceState =
|
private DeviceStateController.DeviceState mDeviceState =
|
||||||
DeviceStateController.DeviceState.UNKNOWN;
|
DeviceStateController.DeviceState.UNKNOWN;
|
||||||
|
private long mLastHingeAngleEventTime = 0;
|
||||||
|
private long mLastDisplaySwitchTime = 0;
|
||||||
|
private boolean mShouldIgnoreSensorRotation;
|
||||||
|
private boolean mShouldDisableRotationSensor;
|
||||||
private boolean mInHalfFoldTransition = false;
|
private boolean mInHalfFoldTransition = false;
|
||||||
|
private int mDisplaySwitchRotationBlockTimeMs;
|
||||||
|
private int mHingeAngleRotationBlockTimeMs;
|
||||||
|
private int mMaxHingeAngle;
|
||||||
private final boolean mIsDisplayAlwaysSeparatingHinge;
|
private final boolean mIsDisplayAlwaysSeparatingHinge;
|
||||||
|
private SensorManager mSensorManager;
|
||||||
|
private SensorEventListener mHingeAngleSensorEventListener;
|
||||||
private final Set<Integer> mTabletopRotations;
|
private final Set<Integer> mTabletopRotations;
|
||||||
private final Runnable mActivityBoundsUpdateCallback;
|
private final Runnable mActivityBoundsUpdateCallback;
|
||||||
|
|
||||||
@@ -1682,6 +1745,48 @@ public class DisplayRotation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
mPauseAutorotationDuringUnfolding = mContext.getResources().getBoolean(
|
||||||
|
R.bool.config_windowManagerPauseRotationWhenUnfolding);
|
||||||
|
|
||||||
|
if (mPauseAutorotationDuringUnfolding) {
|
||||||
|
mDisplaySwitchRotationBlockTimeMs = mContext.getResources().getInteger(
|
||||||
|
R.integer.config_pauseRotationWhenUnfolding_displaySwitchTimeout);
|
||||||
|
mHingeAngleRotationBlockTimeMs = mContext.getResources().getInteger(
|
||||||
|
R.integer.config_pauseRotationWhenUnfolding_hingeEventTimeout);
|
||||||
|
mMaxHingeAngle = mContext.getResources().getInteger(
|
||||||
|
R.integer.config_pauseRotationWhenUnfolding_maxHingeAngle);
|
||||||
|
registerSensorManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerSensorManager() {
|
||||||
|
mSensorManager = mContext.getSystemService(SensorManager.class);
|
||||||
|
if (mSensorManager != null) {
|
||||||
|
final Sensor hingeAngleSensor = mSensorManager
|
||||||
|
.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE);
|
||||||
|
|
||||||
|
if (hingeAngleSensor != null) {
|
||||||
|
mHingeAngleSensorEventListener = new SensorEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onSensorChanged(SensorEvent event) {
|
||||||
|
onHingeAngleChanged(event.values[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mSensorManager.registerListener(mHingeAngleSensorEventListener,
|
||||||
|
hingeAngleSensor, SensorManager.SENSOR_DELAY_FASTEST, getHandler());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onDisplayRemoved() {
|
||||||
|
if (mSensorManager != null && mHingeAngleSensorEventListener != null) {
|
||||||
|
mSensorManager.unregisterListener(mHingeAngleSensorEventListener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop) {
|
boolean isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop) {
|
||||||
@@ -1711,6 +1816,7 @@ public class DisplayRotation {
|
|||||||
boolean shouldRevertOverriddenRotation() {
|
boolean shouldRevertOverriddenRotation() {
|
||||||
// When transitioning to open.
|
// When transitioning to open.
|
||||||
return mDeviceState == DeviceStateController.DeviceState.OPEN
|
return mDeviceState == DeviceStateController.DeviceState.OPEN
|
||||||
|
&& !mShouldIgnoreSensorRotation // Ignore if the hinge angle still moving
|
||||||
&& mInHalfFoldTransition
|
&& mInHalfFoldTransition
|
||||||
&& mHalfFoldSavedRotation != -1 // Ignore if we've already reverted.
|
&& mHalfFoldSavedRotation != -1 // Ignore if we've already reverted.
|
||||||
&& mUserRotationMode
|
&& mUserRotationMode
|
||||||
@@ -1757,6 +1863,80 @@ public class DisplayRotation {
|
|||||||
UiThread.getHandler().postDelayed(mActivityBoundsUpdateCallback,
|
UiThread.getHandler().postDelayed(mActivityBoundsUpdateCallback,
|
||||||
FOLDING_RECOMPUTE_CONFIG_DELAY_MS);
|
FOLDING_RECOMPUTE_CONFIG_DELAY_MS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean shouldIgnoreSensorRotation() {
|
||||||
|
return mShouldIgnoreSensorRotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean shouldDisableRotationSensor() {
|
||||||
|
return mShouldDisableRotationSensor;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateSensorRotationBlockIfNeeded() {
|
||||||
|
final long currentTime = uptimeMillis();
|
||||||
|
final boolean newShouldIgnoreRotation =
|
||||||
|
currentTime - mLastDisplaySwitchTime < mDisplaySwitchRotationBlockTimeMs
|
||||||
|
|| currentTime - mLastHingeAngleEventTime < mHingeAngleRotationBlockTimeMs;
|
||||||
|
|
||||||
|
if (newShouldIgnoreRotation != mShouldIgnoreSensorRotation) {
|
||||||
|
mShouldIgnoreSensorRotation = newShouldIgnoreRotation;
|
||||||
|
|
||||||
|
// Resuming the autorotation
|
||||||
|
if (!mShouldIgnoreSensorRotation) {
|
||||||
|
if (mShouldDisableRotationSensor) {
|
||||||
|
// Sensor was disabled, let's re-enable it
|
||||||
|
mShouldDisableRotationSensor = false;
|
||||||
|
updateOrientationListenerLw();
|
||||||
|
} else {
|
||||||
|
// Sensor was not disabled, let's update the rotation in case if we received
|
||||||
|
// some rotation sensor updates when autorotate was disabled
|
||||||
|
updateRotationAndSendNewConfigIfChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onPhysicalDisplayChanged() {
|
||||||
|
if (!mPauseAutorotationDuringUnfolding) return;
|
||||||
|
|
||||||
|
mLastDisplaySwitchTime = uptimeMillis();
|
||||||
|
|
||||||
|
final boolean isUnfolding =
|
||||||
|
mDeviceState == DeviceStateController.DeviceState.OPEN
|
||||||
|
|| mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED;
|
||||||
|
|
||||||
|
if (isUnfolding) {
|
||||||
|
// Temporary disable rotation sensor updates when unfolding
|
||||||
|
mShouldDisableRotationSensor = true;
|
||||||
|
updateOrientationListenerLw();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSensorRotationBlockIfNeeded();
|
||||||
|
getHandler().postDelayed(() -> {
|
||||||
|
synchronized (mLock) {
|
||||||
|
updateSensorRotationBlockIfNeeded();
|
||||||
|
};
|
||||||
|
}, mDisplaySwitchRotationBlockTimeMs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onHingeAngleChanged(float hingeAngle) {
|
||||||
|
if (hingeAngle < mMaxHingeAngle) {
|
||||||
|
mLastHingeAngleEventTime = uptimeMillis();
|
||||||
|
|
||||||
|
updateSensorRotationBlockIfNeeded();
|
||||||
|
|
||||||
|
getHandler().postDelayed(() -> {
|
||||||
|
synchronized (mLock) {
|
||||||
|
updateSensorRotationBlockIfNeeded();
|
||||||
|
};
|
||||||
|
}, mHingeAngleRotationBlockTimeMs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
Handler getHandler() {
|
||||||
|
return mService.mH;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class OrientationListener extends WindowOrientationListener implements Runnable {
|
private class OrientationListener extends WindowOrientationListener implements Runnable {
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ import android.hardware.SensorManager;
|
|||||||
import android.hardware.devicestate.DeviceStateManager;
|
import android.hardware.devicestate.DeviceStateManager;
|
||||||
import android.os.PowerManagerInternal;
|
import android.os.PowerManagerInternal;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import android.os.Handler;
|
||||||
import android.platform.test.annotations.Presubmit;
|
import android.platform.test.annotations.Presubmit;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.view.DisplayAddress;
|
import android.view.DisplayAddress;
|
||||||
@@ -74,6 +75,9 @@ import com.android.server.LocalServices;
|
|||||||
import com.android.server.UiThread;
|
import com.android.server.UiThread;
|
||||||
import com.android.server.policy.WindowManagerPolicy;
|
import com.android.server.policy.WindowManagerPolicy;
|
||||||
import com.android.server.statusbar.StatusBarManagerInternal;
|
import com.android.server.statusbar.StatusBarManagerInternal;
|
||||||
|
import com.android.server.testutils.OffsettableClock;
|
||||||
|
import com.android.server.testutils.TestHandler;
|
||||||
|
import com.android.server.wm.DisplayContent.FixedRotationTransitionListener;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
@@ -101,6 +105,9 @@ public class DisplayRotationTests {
|
|||||||
private static final long UI_HANDLER_WAIT_TIMEOUT_MS = 50;
|
private static final long UI_HANDLER_WAIT_TIMEOUT_MS = 50;
|
||||||
|
|
||||||
private StatusBarManagerInternal mPreviousStatusBarManagerInternal;
|
private StatusBarManagerInternal mPreviousStatusBarManagerInternal;
|
||||||
|
private static final OffsettableClock sClock = new OffsettableClock.Stopped();
|
||||||
|
private static TestHandler sHandler;
|
||||||
|
private static long sCurrentUptimeMillis = 10_000;
|
||||||
|
|
||||||
private static WindowManagerService sMockWm;
|
private static WindowManagerService sMockWm;
|
||||||
private DisplayContent mMockDisplayContent;
|
private DisplayContent mMockDisplayContent;
|
||||||
@@ -110,6 +117,7 @@ public class DisplayRotationTests {
|
|||||||
private Resources mMockRes;
|
private Resources mMockRes;
|
||||||
private SensorManager mMockSensorManager;
|
private SensorManager mMockSensorManager;
|
||||||
private Sensor mFakeOrientationSensor;
|
private Sensor mFakeOrientationSensor;
|
||||||
|
private Sensor mFakeHingeAngleSensor;
|
||||||
private DisplayWindowSettings mMockDisplayWindowSettings;
|
private DisplayWindowSettings mMockDisplayWindowSettings;
|
||||||
private ContentResolver mMockResolver;
|
private ContentResolver mMockResolver;
|
||||||
private FakeSettingsProvider mFakeSettingsProvider;
|
private FakeSettingsProvider mFakeSettingsProvider;
|
||||||
@@ -122,6 +130,9 @@ public class DisplayRotationTests {
|
|||||||
private ContentObserver mUserRotationObserver;
|
private ContentObserver mUserRotationObserver;
|
||||||
private SensorEventListener mOrientationSensorListener;
|
private SensorEventListener mOrientationSensorListener;
|
||||||
|
|
||||||
|
ArgumentCaptor<SensorEventListener> mHingeAngleSensorListenerCaptor = ArgumentCaptor.forClass(
|
||||||
|
SensorEventListener.class);
|
||||||
|
|
||||||
private DisplayRotationBuilder mBuilder;
|
private DisplayRotationBuilder mBuilder;
|
||||||
|
|
||||||
private DeviceStateController mDeviceStateController;
|
private DeviceStateController mDeviceStateController;
|
||||||
@@ -132,6 +143,7 @@ public class DisplayRotationTests {
|
|||||||
sMockWm = mock(WindowManagerService.class);
|
sMockWm = mock(WindowManagerService.class);
|
||||||
sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
|
sMockWm.mPowerManagerInternal = mock(PowerManagerInternal.class);
|
||||||
sMockWm.mPolicy = mock(WindowManagerPolicy.class);
|
sMockWm.mPolicy = mock(WindowManagerPolicy.class);
|
||||||
|
sHandler = new TestHandler(null, sClock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
@@ -464,12 +476,16 @@ public class DisplayRotationTests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SensorEvent createSensorEvent(int rotation) throws Exception {
|
private SensorEvent createSensorEvent(int rotation) throws Exception {
|
||||||
|
return createSensorEvent(mFakeOrientationSensor, rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SensorEvent createSensorEvent(Sensor sensor, int value) throws Exception {
|
||||||
final Constructor<SensorEvent> constructor =
|
final Constructor<SensorEvent> constructor =
|
||||||
SensorEvent.class.getDeclaredConstructor(int.class);
|
SensorEvent.class.getDeclaredConstructor(int.class);
|
||||||
constructor.setAccessible(true);
|
constructor.setAccessible(true);
|
||||||
final SensorEvent event = constructor.newInstance(1);
|
final SensorEvent event = constructor.newInstance(1);
|
||||||
event.sensor = mFakeOrientationSensor;
|
event.sensor = sensor;
|
||||||
event.values[0] = rotation;
|
event.values[0] = value;
|
||||||
event.timestamp = SystemClock.elapsedRealtimeNanos();
|
event.timestamp = SystemClock.elapsedRealtimeNanos();
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
@@ -901,6 +917,120 @@ public class DisplayRotationTests {
|
|||||||
Surface.ROTATION_0, Surface.ROTATION_90, false /* forceUpdate */));
|
Surface.ROTATION_0, Surface.ROTATION_90, false /* forceUpdate */));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSensorRotationAfterDisplayChangeBeforeTimeout_ignoresSensor() throws Exception {
|
||||||
|
mBuilder.setSupportHalfFoldAutoRotateOverride(true)
|
||||||
|
.setPauseRotationWhenUnfolding(true)
|
||||||
|
.setDisplaySwitchRotationBlockTimeMs(1000)
|
||||||
|
.build();
|
||||||
|
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
|
||||||
|
thawRotation();
|
||||||
|
enableOrientationSensor();
|
||||||
|
|
||||||
|
mTarget.physicalDisplayChanged();
|
||||||
|
|
||||||
|
moveTimeForward(900);
|
||||||
|
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
|
||||||
|
assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
|
||||||
|
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSensorRotationAfterDisplayChangeAfterTimeout_usesSensor() throws Exception {
|
||||||
|
mBuilder.setSupportHalfFoldAutoRotateOverride(true)
|
||||||
|
.setPauseRotationWhenUnfolding(true)
|
||||||
|
.setDisplaySwitchRotationBlockTimeMs(1000)
|
||||||
|
.build();
|
||||||
|
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
|
||||||
|
thawRotation();
|
||||||
|
enableOrientationSensor();
|
||||||
|
|
||||||
|
mTarget.physicalDisplayChanged();
|
||||||
|
|
||||||
|
moveTimeForward(1100);
|
||||||
|
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
|
||||||
|
assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
|
||||||
|
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSensorRotationAfterHingeEventBeforeTimeout_ignoresSensor() throws Exception {
|
||||||
|
mBuilder.setSupportHalfFoldAutoRotateOverride(true)
|
||||||
|
.setPauseRotationWhenUnfolding(true)
|
||||||
|
.setMaxHingeAngle(165)
|
||||||
|
.setHingeAngleRotationBlockTimeMs(400)
|
||||||
|
.build();
|
||||||
|
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
|
||||||
|
thawRotation();
|
||||||
|
enableOrientationSensor();
|
||||||
|
|
||||||
|
sendHingeAngleEvent(130);
|
||||||
|
|
||||||
|
moveTimeForward( 300);
|
||||||
|
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
|
||||||
|
assertEquals(Surface.ROTATION_0, mTarget.rotationForOrientation(
|
||||||
|
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSensorRotationAfterHingeEventBeforeTimeoutFlagDisabled_usesSensorData()
|
||||||
|
throws Exception {
|
||||||
|
mBuilder.setSupportHalfFoldAutoRotateOverride(true)
|
||||||
|
.setPauseRotationWhenUnfolding(false)
|
||||||
|
.setMaxHingeAngle(165)
|
||||||
|
.setHingeAngleRotationBlockTimeMs(400)
|
||||||
|
.build();
|
||||||
|
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
|
||||||
|
thawRotation();
|
||||||
|
enableOrientationSensor();
|
||||||
|
|
||||||
|
sendHingeAngleEvent(130);
|
||||||
|
|
||||||
|
moveTimeForward( 300);
|
||||||
|
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
|
||||||
|
assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
|
||||||
|
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSensorRotationAfterHingeEventAfterTimeout_usesSensorData() throws Exception {
|
||||||
|
mBuilder.setSupportHalfFoldAutoRotateOverride(true)
|
||||||
|
.setPauseRotationWhenUnfolding(true)
|
||||||
|
.setMaxHingeAngle(165)
|
||||||
|
.setHingeAngleRotationBlockTimeMs(400)
|
||||||
|
.build();
|
||||||
|
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
|
||||||
|
thawRotation();
|
||||||
|
enableOrientationSensor();
|
||||||
|
|
||||||
|
sendHingeAngleEvent(180);
|
||||||
|
|
||||||
|
moveTimeForward(1010);
|
||||||
|
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
|
||||||
|
assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
|
||||||
|
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSensorRotationAfterLargeHingeEventBeforeTimeout_usesSensor() throws Exception {
|
||||||
|
mBuilder.setSupportHalfFoldAutoRotateOverride(true)
|
||||||
|
.setPauseRotationWhenUnfolding(true)
|
||||||
|
.setMaxHingeAngle(165)
|
||||||
|
.setHingeAngleRotationBlockTimeMs(400)
|
||||||
|
.build();
|
||||||
|
configureDisplayRotation(SCREEN_ORIENTATION_PORTRAIT, false, false);
|
||||||
|
thawRotation();
|
||||||
|
enableOrientationSensor();
|
||||||
|
|
||||||
|
sendHingeAngleEvent(180);
|
||||||
|
|
||||||
|
moveTimeForward(300);
|
||||||
|
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_90));
|
||||||
|
assertEquals(Surface.ROTATION_90, mTarget.rotationForOrientation(
|
||||||
|
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
|
||||||
|
}
|
||||||
|
|
||||||
// ========================
|
// ========================
|
||||||
// Non-rotation API Tests
|
// Non-rotation API Tests
|
||||||
// ========================
|
// ========================
|
||||||
@@ -921,6 +1051,12 @@ public class DisplayRotationTests {
|
|||||||
+ " fixed to user rotation.", mTarget.isFixedToUserRotation());
|
+ " fixed to user rotation.", mTarget.isFixedToUserRotation());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void moveTimeForward(long timeMillis) {
|
||||||
|
sCurrentUptimeMillis += timeMillis;
|
||||||
|
sClock.fastForward(timeMillis);
|
||||||
|
sHandler.timeAdvance();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call {@link DisplayRotation#configure(int, int)} to configure {@link #mTarget}
|
* Call {@link DisplayRotation#configure(int, int)} to configure {@link #mTarget}
|
||||||
* according to given parameters.
|
* according to given parameters.
|
||||||
@@ -953,6 +1089,17 @@ public class DisplayRotationTests {
|
|||||||
mTarget.configure(width, height);
|
mTarget.configure(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendHingeAngleEvent(int hingeAngle) {
|
||||||
|
mHingeAngleSensorListenerCaptor.getAllValues().forEach(sensorEventListener -> {
|
||||||
|
try {
|
||||||
|
sensorEventListener.onSensorChanged(createSensorEvent(mFakeHingeAngleSensor,
|
||||||
|
hingeAngle));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void freezeRotation(int rotation) {
|
private void freezeRotation(int rotation) {
|
||||||
mTarget.freezeRotation(rotation);
|
mTarget.freezeRotation(rotation);
|
||||||
|
|
||||||
@@ -974,7 +1121,11 @@ public class DisplayRotationTests {
|
|||||||
private class DisplayRotationBuilder {
|
private class DisplayRotationBuilder {
|
||||||
private boolean mIsDefaultDisplay = true;
|
private boolean mIsDefaultDisplay = true;
|
||||||
private boolean mSupportAutoRotation = true;
|
private boolean mSupportAutoRotation = true;
|
||||||
|
private boolean mPauseRotationWhenUnfolding = false;
|
||||||
private boolean mSupportHalfFoldAutoRotateOverride = false;
|
private boolean mSupportHalfFoldAutoRotateOverride = false;
|
||||||
|
private int mDisplaySwitchRotationBlockTimeMs;
|
||||||
|
private int mHingeAngleRotationBlockTimeMs;
|
||||||
|
private int mMaxHingeAngle;
|
||||||
|
|
||||||
private int mLidOpenRotation = WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
|
private int mLidOpenRotation = WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
|
||||||
private int mCarDockRotation;
|
private int mCarDockRotation;
|
||||||
@@ -986,6 +1137,29 @@ public class DisplayRotationTests {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DisplayRotationBuilder setPauseRotationWhenUnfolding(
|
||||||
|
boolean pauseRotationWhenUnfolding) {
|
||||||
|
mPauseRotationWhenUnfolding = pauseRotationWhenUnfolding;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DisplayRotationBuilder setDisplaySwitchRotationBlockTimeMs(
|
||||||
|
int displaySwitchRotationBlockTimeMs) {
|
||||||
|
mDisplaySwitchRotationBlockTimeMs = displaySwitchRotationBlockTimeMs;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DisplayRotationBuilder setHingeAngleRotationBlockTimeMs(
|
||||||
|
int hingeAngleRotationBlockTimeMs) {
|
||||||
|
mHingeAngleRotationBlockTimeMs = hingeAngleRotationBlockTimeMs;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DisplayRotationBuilder setMaxHingeAngle(int maxHingeAngle) {
|
||||||
|
mMaxHingeAngle = maxHingeAngle;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
private DisplayRotationBuilder setSupportAutoRotation(boolean supportAutoRotation) {
|
private DisplayRotationBuilder setSupportAutoRotation(boolean supportAutoRotation) {
|
||||||
mSupportAutoRotation = supportAutoRotation;
|
mSupportAutoRotation = supportAutoRotation;
|
||||||
return this;
|
return this;
|
||||||
@@ -1111,10 +1285,27 @@ public class DisplayRotationTests {
|
|||||||
when(mMockDisplayContent.getWindowConfiguration())
|
when(mMockDisplayContent.getWindowConfiguration())
|
||||||
.thenReturn(new WindowConfiguration());
|
.thenReturn(new WindowConfiguration());
|
||||||
|
|
||||||
|
Field field = DisplayContent.class
|
||||||
|
.getDeclaredField("mFixedRotationTransitionListener");
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(mMockDisplayContent, mock(FixedRotationTransitionListener.class));
|
||||||
|
|
||||||
mMockDisplayPolicy = mock(DisplayPolicy.class);
|
mMockDisplayPolicy = mock(DisplayPolicy.class);
|
||||||
|
|
||||||
mMockRes = mock(Resources.class);
|
mMockRes = mock(Resources.class);
|
||||||
when(mMockContext.getResources()).thenReturn((mMockRes));
|
when(mMockContext.getResources()).thenReturn((mMockRes));
|
||||||
|
when(mMockRes.getBoolean(com.android.internal.R.bool
|
||||||
|
.config_windowManagerPauseRotationWhenUnfolding))
|
||||||
|
.thenReturn(mPauseRotationWhenUnfolding);
|
||||||
|
when(mMockRes.getInteger(com.android.internal.R.integer
|
||||||
|
.config_pauseRotationWhenUnfolding_displaySwitchTimeout))
|
||||||
|
.thenReturn(mDisplaySwitchRotationBlockTimeMs);
|
||||||
|
when(mMockRes.getInteger(com.android.internal.R.integer
|
||||||
|
.config_pauseRotationWhenUnfolding_hingeEventTimeout))
|
||||||
|
.thenReturn(mHingeAngleRotationBlockTimeMs);
|
||||||
|
when(mMockRes.getInteger(com.android.internal.R.integer
|
||||||
|
.config_pauseRotationWhenUnfolding_maxHingeAngle))
|
||||||
|
.thenReturn(mMaxHingeAngle);
|
||||||
when(mMockRes.getBoolean(com.android.internal.R.bool.config_supportAutoRotation))
|
when(mMockRes.getBoolean(com.android.internal.R.bool.config_supportAutoRotation))
|
||||||
.thenReturn(mSupportAutoRotation);
|
.thenReturn(mSupportAutoRotation);
|
||||||
when(mMockRes.getInteger(com.android.internal.R.integer.config_lidOpenRotation))
|
when(mMockRes.getInteger(com.android.internal.R.integer.config_lidOpenRotation))
|
||||||
@@ -1127,11 +1318,16 @@ public class DisplayRotationTests {
|
|||||||
.thenReturn(convertRotationToDegrees(mUndockedHdmiRotation));
|
.thenReturn(convertRotationToDegrees(mUndockedHdmiRotation));
|
||||||
|
|
||||||
mMockSensorManager = mock(SensorManager.class);
|
mMockSensorManager = mock(SensorManager.class);
|
||||||
|
when(mMockContext.getSystemService(SensorManager.class))
|
||||||
|
.thenReturn(mMockSensorManager);
|
||||||
when(mMockContext.getSystemService(Context.SENSOR_SERVICE))
|
when(mMockContext.getSystemService(Context.SENSOR_SERVICE))
|
||||||
.thenReturn(mMockSensorManager);
|
.thenReturn(mMockSensorManager);
|
||||||
mFakeOrientationSensor = createSensor(Sensor.TYPE_DEVICE_ORIENTATION);
|
mFakeOrientationSensor = createSensor(Sensor.TYPE_DEVICE_ORIENTATION);
|
||||||
when(mMockSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION)).thenReturn(
|
when(mMockSensorManager.getSensorList(Sensor.TYPE_DEVICE_ORIENTATION)).thenReturn(
|
||||||
Collections.singletonList(mFakeOrientationSensor));
|
Collections.singletonList(mFakeOrientationSensor));
|
||||||
|
mFakeHingeAngleSensor = mock(Sensor.class);
|
||||||
|
when(mMockSensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE)).thenReturn(
|
||||||
|
mFakeHingeAngleSensor);
|
||||||
|
|
||||||
when(mMockContext.getResources().getBoolean(
|
when(mMockContext.getResources().getBoolean(
|
||||||
com.android.internal.R.bool.config_windowManagerHalfFoldAutoRotateOverride))
|
com.android.internal.R.bool.config_windowManagerHalfFoldAutoRotateOverride))
|
||||||
@@ -1160,10 +1356,24 @@ public class DisplayRotationTests {
|
|||||||
WindowManagerService service, DisplayContent displayContent) {
|
WindowManagerService service, DisplayContent displayContent) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Handler getHandler() {
|
||||||
|
return sHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
long uptimeMillis() {
|
||||||
|
return sCurrentUptimeMillis;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
reset(sMockWm);
|
reset(sMockWm);
|
||||||
|
|
||||||
|
verify(mMockSensorManager, atLeast(0)).registerListener(
|
||||||
|
mHingeAngleSensorListenerCaptor.capture(), eq(mFakeHingeAngleSensor), anyInt(),
|
||||||
|
any());
|
||||||
|
|
||||||
captureObservers();
|
captureObservers();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user