Make DeviceStateController generic
Previously it was specific to fold-state, and used to notify a single client of fold-state changes. DeviceStateController is now generic and can be used (or modified) to cache, query, or receive callbacks for other state changes. Note that we are purposely removing the nested FoldStateListener class since it's a very thin layer and makes it unnecessarily complex when we add new functionality to DeviceStateController. A simple example would be DeviceStateController#getCurrentState() needing to access an inner FoldStateListener#getCurrentState(). Bug: 264499484 Test: atest DisplayRotationTests Test: atest DeviceStateControllerTests Change-Id: I81b0a61b52d74718fe1dee213a59509a44a9a920
This commit is contained in:
@@ -652,6 +652,16 @@
|
||||
The default is false. -->
|
||||
<bool name="config_lidControlsSleep">false</bool>
|
||||
|
||||
<!-- The device states (supplied by DeviceStateManager) that should be treated as open by the
|
||||
device fold controller. Default is empty. -->
|
||||
<integer-array name="config_openDeviceStates">
|
||||
<!-- Example:
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
-->
|
||||
</integer-array>
|
||||
|
||||
<!-- The device states (supplied by DeviceStateManager) that should be treated as folded by the
|
||||
display fold controller. Default is empty. -->
|
||||
<integer-array name="config_foldedDeviceStates">
|
||||
@@ -672,6 +682,16 @@
|
||||
-->
|
||||
</integer-array>
|
||||
|
||||
<!-- The device states (supplied by DeviceStateManager) that should be treated as a rear display
|
||||
state. Default is empty. -->
|
||||
<integer-array name="config_rearDisplayDeviceStates">
|
||||
<!-- Example:
|
||||
<item>0</item>
|
||||
<item>1</item>
|
||||
<item>2</item>
|
||||
-->
|
||||
</integer-array>
|
||||
|
||||
<!-- Indicates whether the window manager reacts to half-fold device states by overriding
|
||||
rotation. -->
|
||||
<bool name="config_windowManagerHalfFoldAutoRotateOverride">false</bool>
|
||||
|
||||
@@ -4015,8 +4015,10 @@
|
||||
<java-symbol type="integer" name="config_maxScanTasksForHomeVisibility" />
|
||||
|
||||
<!-- For Foldables -->
|
||||
<java-symbol type="array" name="config_openDeviceStates" />
|
||||
<java-symbol type="array" name="config_foldedDeviceStates" />
|
||||
<java-symbol type="array" name="config_halfFoldedDeviceStates" />
|
||||
<java-symbol type="array" name="config_rearDisplayDeviceStates" />
|
||||
<java-symbol type="bool" name="config_windowManagerHalfFoldAutoRotateOverride" />
|
||||
<java-symbol type="array" name="config_deviceStatesOnWhichToWakeUp" />
|
||||
<java-symbol type="array" name="config_deviceStatesOnWhichToSleep" />
|
||||
|
||||
@@ -16,80 +16,92 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.Context;
|
||||
import android.hardware.devicestate.DeviceStateManager;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerExecutor;
|
||||
|
||||
import com.android.internal.R;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Class that registers callbacks with the {@link DeviceStateManager} and
|
||||
* responds to fold state changes by forwarding such events to a delegate.
|
||||
* Class that registers callbacks with the {@link DeviceStateManager} and responds to device
|
||||
* changes.
|
||||
*/
|
||||
final class DeviceStateController {
|
||||
final class DeviceStateController implements DeviceStateManager.DeviceStateCallback {
|
||||
|
||||
@NonNull
|
||||
private final DeviceStateManager mDeviceStateManager;
|
||||
private final Context mContext;
|
||||
@NonNull
|
||||
private final int[] mOpenDeviceStates;
|
||||
@NonNull
|
||||
private final int[] mHalfFoldedDeviceStates;
|
||||
@NonNull
|
||||
private final int[] mFoldedDeviceStates;
|
||||
@NonNull
|
||||
private final int[] mRearDisplayDeviceStates;
|
||||
@NonNull
|
||||
private final List<Consumer<DeviceState>> mDeviceStateCallbacks = new ArrayList<>();
|
||||
|
||||
private FoldStateListener mDeviceStateListener;
|
||||
@Nullable
|
||||
private DeviceState mLastDeviceState;
|
||||
|
||||
public enum FoldState {
|
||||
UNKNOWN, OPEN, FOLDED, HALF_FOLDED
|
||||
public enum DeviceState {
|
||||
UNKNOWN, OPEN, FOLDED, HALF_FOLDED, REAR,
|
||||
}
|
||||
|
||||
DeviceStateController(Context context, Handler handler, Consumer<FoldState> delegate) {
|
||||
mContext = context;
|
||||
mDeviceStateManager = mContext.getSystemService(DeviceStateManager.class);
|
||||
DeviceStateController(@NonNull Context context, @NonNull Handler handler) {
|
||||
mDeviceStateManager = context.getSystemService(DeviceStateManager.class);
|
||||
mOpenDeviceStates = context.getResources()
|
||||
.getIntArray(R.array.config_openDeviceStates);
|
||||
mHalfFoldedDeviceStates = context.getResources()
|
||||
.getIntArray(R.array.config_halfFoldedDeviceStates);
|
||||
mFoldedDeviceStates = context.getResources()
|
||||
.getIntArray(R.array.config_foldedDeviceStates);
|
||||
mRearDisplayDeviceStates = context.getResources()
|
||||
.getIntArray(R.array.config_rearDisplayDeviceStates);
|
||||
|
||||
if (mDeviceStateManager != null) {
|
||||
mDeviceStateListener = new FoldStateListener(mContext, delegate);
|
||||
mDeviceStateManager
|
||||
.registerCallback(new HandlerExecutor(handler),
|
||||
mDeviceStateListener);
|
||||
mDeviceStateManager.registerCallback(new HandlerExecutor(handler), this);
|
||||
}
|
||||
}
|
||||
|
||||
void unregisterFromDeviceStateManager() {
|
||||
if (mDeviceStateListener != null) {
|
||||
mDeviceStateManager.unregisterCallback(mDeviceStateListener);
|
||||
if (mDeviceStateManager != null) {
|
||||
mDeviceStateManager.unregisterCallback(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A listener for half-fold device state events that dispatches state changes to a delegate.
|
||||
*/
|
||||
static final class FoldStateListener implements DeviceStateManager.DeviceStateCallback {
|
||||
void registerDeviceStateCallback(@NonNull Consumer<DeviceState> callback) {
|
||||
mDeviceStateCallbacks.add(callback);
|
||||
}
|
||||
|
||||
private final int[] mHalfFoldedDeviceStates;
|
||||
private final int[] mFoldedDeviceStates;
|
||||
|
||||
@Nullable
|
||||
private FoldState mLastResult;
|
||||
private final Consumer<FoldState> mDelegate;
|
||||
|
||||
FoldStateListener(Context context, Consumer<FoldState> delegate) {
|
||||
mFoldedDeviceStates = context.getResources().getIntArray(
|
||||
com.android.internal.R.array.config_foldedDeviceStates);
|
||||
mHalfFoldedDeviceStates = context.getResources().getIntArray(
|
||||
com.android.internal.R.array.config_halfFoldedDeviceStates);
|
||||
mDelegate = delegate;
|
||||
@Override
|
||||
public void onStateChanged(int state) {
|
||||
final DeviceState deviceState;
|
||||
if (ArrayUtils.contains(mHalfFoldedDeviceStates, state)) {
|
||||
deviceState = DeviceState.HALF_FOLDED;
|
||||
} else if (ArrayUtils.contains(mFoldedDeviceStates, state)) {
|
||||
deviceState = DeviceState.FOLDED;
|
||||
} else if (ArrayUtils.contains(mRearDisplayDeviceStates, state)) {
|
||||
deviceState = DeviceState.REAR;
|
||||
} else if (ArrayUtils.contains(mOpenDeviceStates, state)) {
|
||||
deviceState = DeviceState.OPEN;
|
||||
} else {
|
||||
deviceState = DeviceState.UNKNOWN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateChanged(int state) {
|
||||
final boolean halfFolded = ArrayUtils.contains(mHalfFoldedDeviceStates, state);
|
||||
FoldState result;
|
||||
if (halfFolded) {
|
||||
result = FoldState.HALF_FOLDED;
|
||||
} else {
|
||||
final boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state);
|
||||
result = folded ? FoldState.FOLDED : FoldState.OPEN;
|
||||
}
|
||||
if (mLastResult == null || !mLastResult.equals(result)) {
|
||||
mLastResult = result;
|
||||
mDelegate.accept(result);
|
||||
if (mLastDeviceState == null || !mLastDeviceState.equals(deviceState)) {
|
||||
mLastDeviceState = deviceState;
|
||||
|
||||
for (Consumer<DeviceState> callback : mDeviceStateCallbacks) {
|
||||
callback.accept(mLastDeviceState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1125,14 +1125,17 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp
|
||||
mWmService.mAtmService.getRecentTasks().getInputListener());
|
||||
}
|
||||
|
||||
mDeviceStateController = new DeviceStateController(mWmService.mContext, mWmService.mH);
|
||||
|
||||
mDisplayPolicy = new DisplayPolicy(mWmService, this);
|
||||
mDisplayRotation = new DisplayRotation(mWmService, this, mDisplayInfo.address);
|
||||
|
||||
mDeviceStateController = new DeviceStateController(mWmService.mContext, mWmService.mH,
|
||||
newFoldState -> {
|
||||
final Consumer<DeviceStateController.DeviceState> deviceStateConsumer =
|
||||
(@NonNull DeviceStateController.DeviceState newFoldState) -> {
|
||||
mDisplaySwitchTransitionLauncher.foldStateChanged(newFoldState);
|
||||
mDisplayRotation.foldStateChanged(newFoldState);
|
||||
});
|
||||
};
|
||||
mDeviceStateController.registerDeviceStateCallback(deviceStateConsumer);
|
||||
|
||||
mCloseToSquareMaxAspectRatio = mWmService.mContext.getResources().getFloat(
|
||||
R.dimen.config_closeToSquareDisplayMaxAspectRatio);
|
||||
|
||||
@@ -1573,7 +1573,7 @@ public class DisplayRotation {
|
||||
proto.end(token);
|
||||
}
|
||||
|
||||
boolean isDeviceInPosture(DeviceStateController.FoldState state, boolean isTabletop) {
|
||||
boolean isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop) {
|
||||
if (mFoldController == null) return false;
|
||||
return mFoldController.isDeviceInPosture(state, isTabletop);
|
||||
}
|
||||
@@ -1585,10 +1585,10 @@ public class DisplayRotation {
|
||||
/**
|
||||
* Called by the DeviceStateManager callback when the device state changes.
|
||||
*/
|
||||
void foldStateChanged(DeviceStateController.FoldState foldState) {
|
||||
void foldStateChanged(DeviceStateController.DeviceState deviceState) {
|
||||
if (mFoldController != null) {
|
||||
synchronized (mLock) {
|
||||
mFoldController.foldStateChanged(foldState);
|
||||
mFoldController.foldStateChanged(deviceState);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1596,8 +1596,8 @@ public class DisplayRotation {
|
||||
private class FoldController {
|
||||
@Surface.Rotation
|
||||
private int mHalfFoldSavedRotation = -1; // No saved rotation
|
||||
private DeviceStateController.FoldState mFoldState =
|
||||
DeviceStateController.FoldState.UNKNOWN;
|
||||
private DeviceStateController.DeviceState mDeviceState =
|
||||
DeviceStateController.DeviceState.UNKNOWN;
|
||||
private boolean mInHalfFoldTransition = false;
|
||||
private final boolean mIsDisplayAlwaysSeparatingHinge;
|
||||
private final Set<Integer> mTabletopRotations;
|
||||
@@ -1637,32 +1637,33 @@ public class DisplayRotation {
|
||||
R.bool.config_isDisplayHingeAlwaysSeparating);
|
||||
}
|
||||
|
||||
boolean isDeviceInPosture(DeviceStateController.FoldState state, boolean isTabletop) {
|
||||
if (state != mFoldState) {
|
||||
boolean isDeviceInPosture(DeviceStateController.DeviceState state, boolean isTabletop) {
|
||||
if (state != mDeviceState) {
|
||||
return false;
|
||||
}
|
||||
if (mFoldState == DeviceStateController.FoldState.HALF_FOLDED) {
|
||||
if (mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED) {
|
||||
return !(isTabletop ^ mTabletopRotations.contains(mRotation));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
DeviceStateController.FoldState getFoldState() {
|
||||
return mFoldState;
|
||||
DeviceStateController.DeviceState getFoldState() {
|
||||
return mDeviceState;
|
||||
}
|
||||
|
||||
boolean isSeparatingHinge() {
|
||||
return mFoldState == DeviceStateController.FoldState.HALF_FOLDED
|
||||
|| (mFoldState == DeviceStateController.FoldState.OPEN
|
||||
return mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED
|
||||
|| (mDeviceState == DeviceStateController.DeviceState.OPEN
|
||||
&& mIsDisplayAlwaysSeparatingHinge);
|
||||
}
|
||||
|
||||
boolean overrideFrozenRotation() {
|
||||
return mFoldState == DeviceStateController.FoldState.HALF_FOLDED;
|
||||
return mDeviceState == DeviceStateController.DeviceState.HALF_FOLDED;
|
||||
}
|
||||
|
||||
boolean shouldRevertOverriddenRotation() {
|
||||
return mFoldState == DeviceStateController.FoldState.OPEN // When transitioning to open.
|
||||
// When transitioning to open.
|
||||
return mDeviceState == DeviceStateController.DeviceState.OPEN
|
||||
&& mInHalfFoldTransition
|
||||
&& mHalfFoldSavedRotation != -1 // Ignore if we've already reverted.
|
||||
&& mUserRotationMode
|
||||
@@ -1676,30 +1677,30 @@ public class DisplayRotation {
|
||||
return savedRotation;
|
||||
}
|
||||
|
||||
void foldStateChanged(DeviceStateController.FoldState newState) {
|
||||
void foldStateChanged(DeviceStateController.DeviceState newState) {
|
||||
ProtoLog.v(WM_DEBUG_ORIENTATION,
|
||||
"foldStateChanged: displayId %d, halfFoldStateChanged %s, "
|
||||
+ "saved rotation: %d, mUserRotation: %d, mLastSensorRotation: %d, "
|
||||
+ "mLastOrientation: %d, mRotation: %d",
|
||||
mDisplayContent.getDisplayId(), newState.name(), mHalfFoldSavedRotation,
|
||||
mUserRotation, mLastSensorRotation, mLastOrientation, mRotation);
|
||||
if (mFoldState == DeviceStateController.FoldState.UNKNOWN) {
|
||||
mFoldState = newState;
|
||||
if (mDeviceState == DeviceStateController.DeviceState.UNKNOWN) {
|
||||
mDeviceState = newState;
|
||||
return;
|
||||
}
|
||||
if (newState == DeviceStateController.FoldState.HALF_FOLDED
|
||||
&& mFoldState != DeviceStateController.FoldState.HALF_FOLDED) {
|
||||
if (newState == DeviceStateController.DeviceState.HALF_FOLDED
|
||||
&& mDeviceState != DeviceStateController.DeviceState.HALF_FOLDED) {
|
||||
// The device has transitioned to HALF_FOLDED state: save the current rotation and
|
||||
// update the device rotation.
|
||||
mHalfFoldSavedRotation = mRotation;
|
||||
mFoldState = newState;
|
||||
mDeviceState = newState;
|
||||
// Now mFoldState is set to HALF_FOLDED, the overrideFrozenRotation function will
|
||||
// return true, so rotation is unlocked.
|
||||
mService.updateRotation(false /* alwaysSendConfiguration */,
|
||||
false /* forceRelayout */);
|
||||
} else {
|
||||
mInHalfFoldTransition = true;
|
||||
mFoldState = newState;
|
||||
mDeviceState = newState;
|
||||
// Tell the device to update its orientation.
|
||||
mService.updateRotation(false /* alwaysSendConfiguration */,
|
||||
false /* forceRelayout */);
|
||||
@@ -1822,7 +1823,7 @@ public class DisplayRotation {
|
||||
final long mTimestamp = System.currentTimeMillis();
|
||||
final int mHalfFoldSavedRotation;
|
||||
final boolean mInHalfFoldTransition;
|
||||
final DeviceStateController.FoldState mFoldState;
|
||||
final DeviceStateController.DeviceState mDeviceState;
|
||||
@Nullable final String mDisplayRotationCompatPolicySummary;
|
||||
|
||||
Record(DisplayRotation dr, int fromRotation, int toRotation) {
|
||||
@@ -1852,11 +1853,11 @@ public class DisplayRotation {
|
||||
if (dr.mFoldController != null) {
|
||||
mHalfFoldSavedRotation = dr.mFoldController.mHalfFoldSavedRotation;
|
||||
mInHalfFoldTransition = dr.mFoldController.mInHalfFoldTransition;
|
||||
mFoldState = dr.mFoldController.mFoldState;
|
||||
mDeviceState = dr.mFoldController.mDeviceState;
|
||||
} else {
|
||||
mHalfFoldSavedRotation = NO_FOLD_CONTROLLER;
|
||||
mInHalfFoldTransition = false;
|
||||
mFoldState = DeviceStateController.FoldState.UNKNOWN;
|
||||
mDeviceState = DeviceStateController.DeviceState.UNKNOWN;
|
||||
}
|
||||
mDisplayRotationCompatPolicySummary = dc.mDisplayRotationCompatPolicy == null
|
||||
? null
|
||||
@@ -1882,7 +1883,7 @@ public class DisplayRotation {
|
||||
pw.println(prefix + " halfFoldSavedRotation="
|
||||
+ mHalfFoldSavedRotation
|
||||
+ " mInHalfFoldTransition=" + mInHalfFoldTransition
|
||||
+ " mFoldState=" + mFoldState);
|
||||
+ " mFoldState=" + mDeviceState);
|
||||
}
|
||||
if (mDisplayRotationCompatPolicySummary != null) {
|
||||
pw.println(prefix + mDisplayRotationCompatPolicySummary);
|
||||
|
||||
@@ -533,7 +533,7 @@ final class LetterboxUiController {
|
||||
// Note that we check the task rather than the parent as with ActivityEmbedding the parent might
|
||||
// be a TaskFragment, and its windowing mode is always MULTI_WINDOW, even if the task is
|
||||
// actually fullscreen.
|
||||
private boolean isDisplayFullScreenAndInPosture(DeviceStateController.FoldState state,
|
||||
private boolean isDisplayFullScreenAndInPosture(DeviceStateController.DeviceState state,
|
||||
boolean isTabletop) {
|
||||
Task task = mActivityRecord.getTask();
|
||||
return mActivityRecord.mDisplayContent != null
|
||||
@@ -559,7 +559,7 @@ final class LetterboxUiController {
|
||||
// Don't check resolved configuration because it may not be updated yet during
|
||||
// configuration change.
|
||||
boolean bookMode = isDisplayFullScreenAndInPosture(
|
||||
DeviceStateController.FoldState.HALF_FOLDED, false /* isTabletop */);
|
||||
DeviceStateController.DeviceState.HALF_FOLDED, false /* isTabletop */);
|
||||
return isHorizontalReachabilityEnabled(parentConfiguration)
|
||||
// Using the last global dynamic position to avoid "jumps" when moving
|
||||
// between apps or activities.
|
||||
@@ -571,7 +571,7 @@ final class LetterboxUiController {
|
||||
// Don't check resolved configuration because it may not be updated yet during
|
||||
// configuration change.
|
||||
boolean tabletopMode = isDisplayFullScreenAndInPosture(
|
||||
DeviceStateController.FoldState.HALF_FOLDED, true /* isTabletop */);
|
||||
DeviceStateController.DeviceState.HALF_FOLDED, true /* isTabletop */);
|
||||
return isVerticalReachabilityEnabled(parentConfiguration)
|
||||
// Using the last global dynamic position to avoid "jumps" when moving
|
||||
// between apps or activities.
|
||||
@@ -1087,7 +1087,7 @@ final class LetterboxUiController {
|
||||
int letterboxPositionForHorizontalReachability = getLetterboxConfiguration()
|
||||
.getLetterboxPositionForHorizontalReachability(
|
||||
isDisplayFullScreenAndInPosture(
|
||||
DeviceStateController.FoldState.HALF_FOLDED,
|
||||
DeviceStateController.DeviceState.HALF_FOLDED,
|
||||
false /* isTabletop */));
|
||||
positionToLog = letterboxHorizontalReachabilityPositionToLetterboxPosition(
|
||||
letterboxPositionForHorizontalReachability);
|
||||
@@ -1095,7 +1095,7 @@ final class LetterboxUiController {
|
||||
int letterboxPositionForVerticalReachability = getLetterboxConfiguration()
|
||||
.getLetterboxPositionForVerticalReachability(
|
||||
isDisplayFullScreenAndInPosture(
|
||||
DeviceStateController.FoldState.HALF_FOLDED,
|
||||
DeviceStateController.DeviceState.HALF_FOLDED,
|
||||
true /* isTabletop */));
|
||||
positionToLog = letterboxVerticalReachabilityPositionToLetterboxPosition(
|
||||
letterboxPositionForVerticalReachability);
|
||||
|
||||
@@ -51,10 +51,10 @@ public class PhysicalDisplaySwitchTransitionLauncher {
|
||||
/**
|
||||
* Called by the DeviceStateManager callback when the state changes.
|
||||
*/
|
||||
void foldStateChanged(DeviceStateController.FoldState newFoldState) {
|
||||
void foldStateChanged(DeviceStateController.DeviceState newDeviceState) {
|
||||
// Ignore transitions to/from half-folded.
|
||||
if (newFoldState == DeviceStateController.FoldState.HALF_FOLDED) return;
|
||||
mIsFolded = newFoldState == DeviceStateController.FoldState.FOLDED;
|
||||
if (newDeviceState == DeviceStateController.DeviceState.HALF_FOLDED) return;
|
||||
mIsFolded = newDeviceState == DeviceStateController.DeviceState.FOLDED;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -16,13 +16,12 @@
|
||||
|
||||
package com.android.server.wm;
|
||||
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
|
||||
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
@@ -32,9 +31,10 @@ import android.platform.test.annotations.Presubmit;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -48,92 +48,76 @@ import java.util.function.Consumer;
|
||||
@Presubmit
|
||||
public class DeviceStateControllerTests {
|
||||
|
||||
private DeviceStateController.FoldStateListener mFoldStateListener;
|
||||
private DeviceStateController mTarget;
|
||||
private DeviceStateControllerBuilder mBuilder;
|
||||
|
||||
private Context mMockContext;
|
||||
private Handler mMockHandler;
|
||||
private Resources mMockRes;
|
||||
private DeviceStateManager mMockDeviceStateManager;
|
||||
|
||||
private Consumer<DeviceStateController.FoldState> mDelegate;
|
||||
private DeviceStateController.FoldState mCurrentState = DeviceStateController.FoldState.UNKNOWN;
|
||||
private DeviceStateController.DeviceState mCurrentState =
|
||||
DeviceStateController.DeviceState.UNKNOWN;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
mBuilder = new DeviceStateControllerBuilder();
|
||||
mCurrentState = DeviceStateController.FoldState.UNKNOWN;
|
||||
mCurrentState = DeviceStateController.DeviceState.UNKNOWN;
|
||||
}
|
||||
|
||||
private void initialize(boolean supportFold, boolean supportHalfFold) throws Exception {
|
||||
private void initialize(boolean supportFold, boolean supportHalfFold) {
|
||||
mBuilder.setSupportFold(supportFold, supportHalfFold);
|
||||
mDelegate = (newFoldState) -> {
|
||||
Consumer<DeviceStateController.DeviceState> delegate = (newFoldState) -> {
|
||||
mCurrentState = newFoldState;
|
||||
};
|
||||
mBuilder.setDelegate(mDelegate);
|
||||
mBuilder.setDelegate(delegate);
|
||||
mBuilder.build();
|
||||
verifyFoldStateListenerRegistration(1);
|
||||
verify(mMockDeviceStateManager).registerCallback(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitialization() throws Exception {
|
||||
public void testInitialization() {
|
||||
initialize(true /* supportFold */, true /* supportHalfFolded */);
|
||||
mFoldStateListener.onStateChanged(mUnfoldedStates[0]);
|
||||
assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN);
|
||||
mTarget.onStateChanged(mOpenDeviceStates[0]);
|
||||
assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitializationWithNoFoldSupport() throws Exception {
|
||||
public void testInitializationWithNoFoldSupport() {
|
||||
initialize(false /* supportFold */, false /* supportHalfFolded */);
|
||||
mFoldStateListener.onStateChanged(mFoldedStates[0]);
|
||||
mTarget.onStateChanged(mFoldedStates[0]);
|
||||
// Note that the folded state is ignored.
|
||||
assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN);
|
||||
assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithFoldSupported() throws Exception {
|
||||
public void testWithFoldSupported() {
|
||||
initialize(true /* supportFold */, false /* supportHalfFolded */);
|
||||
mFoldStateListener.onStateChanged(mUnfoldedStates[0]);
|
||||
assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN);
|
||||
mFoldStateListener.onStateChanged(mFoldedStates[0]);
|
||||
assertEquals(mCurrentState, DeviceStateController.FoldState.FOLDED);
|
||||
mFoldStateListener.onStateChanged(mHalfFoldedStates[0]);
|
||||
assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN); // Ignored
|
||||
mTarget.onStateChanged(mOpenDeviceStates[0]);
|
||||
assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
|
||||
mTarget.onStateChanged(mFoldedStates[0]);
|
||||
assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
|
||||
mTarget.onStateChanged(mHalfFoldedStates[0]);
|
||||
assertEquals(DeviceStateController.DeviceState.UNKNOWN, mCurrentState); // Ignored
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithHalfFoldSupported() throws Exception {
|
||||
public void testWithHalfFoldSupported() {
|
||||
initialize(true /* supportFold */, true /* supportHalfFolded */);
|
||||
mFoldStateListener.onStateChanged(mUnfoldedStates[0]);
|
||||
assertEquals(mCurrentState, DeviceStateController.FoldState.OPEN);
|
||||
mFoldStateListener.onStateChanged(mFoldedStates[0]);
|
||||
assertEquals(mCurrentState, DeviceStateController.FoldState.FOLDED);
|
||||
mFoldStateListener.onStateChanged(mHalfFoldedStates[0]);
|
||||
assertEquals(mCurrentState, DeviceStateController.FoldState.HALF_FOLDED);
|
||||
mTarget.onStateChanged(mOpenDeviceStates[0]);
|
||||
assertEquals(DeviceStateController.DeviceState.OPEN, mCurrentState);
|
||||
mTarget.onStateChanged(mFoldedStates[0]);
|
||||
assertEquals(DeviceStateController.DeviceState.FOLDED, mCurrentState);
|
||||
mTarget.onStateChanged(mHalfFoldedStates[0]);
|
||||
assertEquals(DeviceStateController.DeviceState.HALF_FOLDED, mCurrentState);
|
||||
}
|
||||
|
||||
|
||||
private final int[] mFoldedStates = {0};
|
||||
private final int[] mUnfoldedStates = {1};
|
||||
private final int[] mOpenDeviceStates = {1};
|
||||
private final int[] mHalfFoldedStates = {2};
|
||||
|
||||
|
||||
private void verifyFoldStateListenerRegistration(int numOfInvocation) {
|
||||
final ArgumentCaptor<DeviceStateController.FoldStateListener> listenerCaptor =
|
||||
ArgumentCaptor.forClass(DeviceStateController.FoldStateListener.class);
|
||||
verify(mMockDeviceStateManager, times(numOfInvocation)).registerCallback(
|
||||
any(),
|
||||
listenerCaptor.capture());
|
||||
if (numOfInvocation > 0) {
|
||||
mFoldStateListener = listenerCaptor.getValue();
|
||||
}
|
||||
}
|
||||
private final int[] mRearDisplayStates = {3};
|
||||
|
||||
private class DeviceStateControllerBuilder {
|
||||
private boolean mSupportFold = false;
|
||||
private boolean mSupportHalfFold = false;
|
||||
private Consumer<DeviceStateController.FoldState> mDelegate;
|
||||
private Consumer<DeviceStateController.DeviceState> mDelegate;
|
||||
|
||||
DeviceStateControllerBuilder setSupportFold(
|
||||
boolean supportFold, boolean supportHalfFold) {
|
||||
@@ -143,34 +127,44 @@ public class DeviceStateControllerTests {
|
||||
}
|
||||
|
||||
DeviceStateControllerBuilder setDelegate(
|
||||
Consumer<DeviceStateController.FoldState> delegate) {
|
||||
Consumer<DeviceStateController.DeviceState> delegate) {
|
||||
mDelegate = delegate;
|
||||
return this;
|
||||
}
|
||||
|
||||
private void mockFold(boolean enableFold, boolean enableHalfFold) {
|
||||
if (enableFold || enableHalfFold) {
|
||||
when(mMockContext.getResources()
|
||||
.getIntArray(R.array.config_openDeviceStates))
|
||||
.thenReturn(mOpenDeviceStates);
|
||||
when(mMockContext.getResources()
|
||||
.getIntArray(R.array.config_rearDisplayDeviceStates))
|
||||
.thenReturn(mRearDisplayStates);
|
||||
}
|
||||
|
||||
if (enableFold) {
|
||||
when(mMockContext.getResources().getIntArray(
|
||||
com.android.internal.R.array.config_foldedDeviceStates))
|
||||
when(mMockContext.getResources()
|
||||
.getIntArray(R.array.config_foldedDeviceStates))
|
||||
.thenReturn(mFoldedStates);
|
||||
}
|
||||
if (enableHalfFold) {
|
||||
when(mMockContext.getResources().getIntArray(
|
||||
com.android.internal.R.array.config_halfFoldedDeviceStates))
|
||||
when(mMockContext.getResources()
|
||||
.getIntArray(R.array.config_halfFoldedDeviceStates))
|
||||
.thenReturn(mHalfFoldedStates);
|
||||
}
|
||||
}
|
||||
|
||||
private void build() throws Exception {
|
||||
private void build() {
|
||||
mMockContext = mock(Context.class);
|
||||
mMockRes = mock(Resources.class);
|
||||
when(mMockContext.getResources()).thenReturn((mMockRes));
|
||||
mMockDeviceStateManager = mock(DeviceStateManager.class);
|
||||
when(mMockContext.getSystemService(DeviceStateManager.class))
|
||||
.thenReturn(mMockDeviceStateManager);
|
||||
Resources mockRes = mock(Resources.class);
|
||||
when(mMockContext.getResources()).thenReturn((mockRes));
|
||||
mockFold(mSupportFold, mSupportHalfFold);
|
||||
mMockHandler = mock(Handler.class);
|
||||
mTarget = new DeviceStateController(mMockContext, mMockHandler, mDelegate);
|
||||
Handler mockHandler = mock(Handler.class);
|
||||
mTarget = new DeviceStateController(mMockContext, mockHandler);
|
||||
mTarget.registerDeviceStateCallback(mDelegate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -705,7 +705,7 @@ public class DisplayRotationTests {
|
||||
|
||||
enableOrientationSensor();
|
||||
|
||||
mTarget.foldStateChanged(DeviceStateController.FoldState.OPEN);
|
||||
mTarget.foldStateChanged(DeviceStateController.DeviceState.OPEN);
|
||||
freezeRotation(Surface.ROTATION_270);
|
||||
|
||||
mOrientationSensorListener.onSensorChanged(createSensorEvent(Surface.ROTATION_0));
|
||||
@@ -715,7 +715,7 @@ public class DisplayRotationTests {
|
||||
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
|
||||
|
||||
// ... until half-fold
|
||||
mTarget.foldStateChanged(DeviceStateController.FoldState.HALF_FOLDED);
|
||||
mTarget.foldStateChanged(DeviceStateController.DeviceState.HALF_FOLDED);
|
||||
assertTrue(waitForUiHandler());
|
||||
verify(sMockWm).updateRotation(false, false);
|
||||
assertTrue(waitForUiHandler());
|
||||
@@ -723,7 +723,7 @@ public class DisplayRotationTests {
|
||||
SCREEN_ORIENTATION_UNSPECIFIED, Surface.ROTATION_0));
|
||||
|
||||
// ... then transition back to flat
|
||||
mTarget.foldStateChanged(DeviceStateController.FoldState.OPEN);
|
||||
mTarget.foldStateChanged(DeviceStateController.DeviceState.OPEN);
|
||||
assertTrue(waitForUiHandler());
|
||||
verify(sMockWm, atLeast(1)).updateRotation(false, false);
|
||||
assertTrue(waitForUiHandler());
|
||||
|
||||
@@ -98,7 +98,7 @@ import androidx.test.filters.MediumTest;
|
||||
import com.android.internal.policy.SystemBarUtils;
|
||||
import com.android.internal.statusbar.LetterboxDetails;
|
||||
import com.android.server.statusbar.StatusBarManagerInternal;
|
||||
import com.android.server.wm.DeviceStateController.FoldState;
|
||||
import com.android.server.wm.DeviceStateController.DeviceState;
|
||||
|
||||
import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges;
|
||||
import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
|
||||
@@ -3059,9 +3059,9 @@ public class SizeCompatTests extends WindowTestsBase {
|
||||
private void setFoldablePosture(boolean isHalfFolded, boolean isTabletop) {
|
||||
final DisplayRotation r = mActivity.mDisplayContent.getDisplayRotation();
|
||||
doReturn(isHalfFolded).when(r).isDisplaySeparatingHinge();
|
||||
doReturn(false).when(r).isDeviceInPosture(any(FoldState.class), anyBoolean());
|
||||
doReturn(false).when(r).isDeviceInPosture(any(DeviceState.class), anyBoolean());
|
||||
if (isHalfFolded) {
|
||||
doReturn(true).when(r).isDeviceInPosture(FoldState.HALF_FOLDED, isTabletop);
|
||||
doReturn(true).when(r).isDeviceInPosture(DeviceState.HALF_FOLDED, isTabletop);
|
||||
}
|
||||
mActivity.recomputeConfiguration();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user