Merge "Support FoldingFeature for CONCURRENT state" into udc-dev

This commit is contained in:
Kevin Chyn
2023-03-10 07:09:26 +00:00
committed by Android (Google) Code Review
2 changed files with 96 additions and 21 deletions

View File

@@ -21,6 +21,7 @@ import static androidx.window.util.ExtensionHelper.isZero;
import android.annotation.IntDef;
import android.annotation.Nullable;
import android.graphics.Rect;
import android.hardware.devicestate.DeviceStateManager;
import android.util.Log;
import androidx.annotation.NonNull;
@@ -33,7 +34,8 @@ import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** A representation of a folding feature for both Extension and Sidecar.
/**
* A representation of a folding feature for both Extension and Sidecar.
* For Sidecar this is the same as combining {@link androidx.window.sidecar.SidecarDeviceState} and
* {@link androidx.window.sidecar.SidecarDisplayFeature}. For Extensions this is the mirror of
* {@link androidx.window.extensions.layout.FoldingFeature}.
@@ -67,10 +69,11 @@ public final class CommonFoldingFeature {
public static final int COMMON_STATE_UNKNOWN = -1;
/**
* A common state to represent a FLAT hinge. This is needed because the definitions in Sidecar
* and Extensions do not match exactly.
* A common state that contains no folding features. For example, an in-folding device in the
* "closed" device state.
*/
public static final int COMMON_STATE_FLAT = 3;
public static final int COMMON_STATE_NO_FOLDING_FEATURES = 1;
/**
* A common state to represent a HALF_OPENED hinge. This is needed because the definitions in
* Sidecar and Extensions do not match exactly.
@@ -78,9 +81,27 @@ public final class CommonFoldingFeature {
public static final int COMMON_STATE_HALF_OPENED = 2;
/**
* The possible states for a folding hinge.
* A common state to represent a FLAT hinge. This is needed because the definitions in Sidecar
* and Extensions do not match exactly.
*/
@IntDef({COMMON_STATE_UNKNOWN, COMMON_STATE_FLAT, COMMON_STATE_HALF_OPENED})
public static final int COMMON_STATE_FLAT = 3;
/**
* A common state where the hinge state should be derived using the base state from
* {@link DeviceStateManager.DeviceStateCallback#onBaseStateChanged(int)} instead of the
* emulated state. This is an internal state and must not be passed to clients.
*/
public static final int COMMON_STATE_USE_BASE_STATE = 1000;
/**
* The possible states for a folding hinge. Common in this context means normalized between
* extensions and sidecar.
*/
@IntDef({COMMON_STATE_UNKNOWN,
COMMON_STATE_NO_FOLDING_FEATURES,
COMMON_STATE_HALF_OPENED,
COMMON_STATE_FLAT,
COMMON_STATE_USE_BASE_STATE})
@Retention(RetentionPolicy.SOURCE)
public @interface State {
}
@@ -167,7 +188,7 @@ public final class CommonFoldingFeature {
}
String stateString = featureMatcher.group(6);
stateString = stateString == null ? "" : stateString;
final int state;
@State final int state;
switch (stateString) {
case PATTERN_STATE_FLAT:
state = COMMON_STATE_FLAT;
@@ -191,8 +212,8 @@ public final class CommonFoldingFeature {
@NonNull
private final Rect mRect;
CommonFoldingFeature(int type, int state, @NonNull Rect rect) {
assertValidState(state);
CommonFoldingFeature(int type, @State int state, @NonNull Rect rect) {
assertReportableState(state);
this.mType = type;
this.mState = state;
if (rect.width() == 0 && rect.height() == 0) {
@@ -230,14 +251,23 @@ public final class CommonFoldingFeature {
&& mRect.equals(that.mRect);
}
@Override
public String toString() {
return "CommonFoldingFeature=[Type: " + mType + ", state: " + mState + "]";
}
@Override
public int hashCode() {
return Objects.hash(mType, mState, mRect);
}
private static void assertValidState(@Nullable Integer state) {
if (state != null && state != COMMON_STATE_FLAT
&& state != COMMON_STATE_HALF_OPENED && state != COMMON_STATE_UNKNOWN) {
/**
* Checks if the provided folding feature state should be reported to clients. See
* {@link androidx.window.extensions.layout.FoldingFeature}
*/
private static void assertReportableState(@State int state) {
if (state != COMMON_STATE_FLAT && state != COMMON_STATE_HALF_OPENED
&& state != COMMON_STATE_UNKNOWN) {
throw new IllegalArgumentException("Invalid state: " + state
+ "must be either COMMON_STATE_FLAT or COMMON_STATE_HALF_OPENED");
}

View File

@@ -19,6 +19,7 @@ package androidx.window.common;
import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_UNKNOWN;
import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_USE_BASE_STATE;
import static androidx.window.common.CommonFoldingFeature.parseListFromString;
import android.annotation.NonNull;
@@ -52,13 +53,54 @@ public final class DeviceStateManagerFoldingFeatureProducer
DeviceStateManagerFoldingFeatureProducer.class.getSimpleName();
private static final boolean DEBUG = false;
/**
* Emulated device state {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)} to
* {@link CommonFoldingFeature.State} map.
*/
private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
/**
* Emulated device state received via
* {@link DeviceStateManager.DeviceStateCallback#onStateChanged(int)}.
* "Emulated" states differ from "base" state in the sense that they may not correspond 1:1 with
* physical device states. They represent the state of the device when various software
* features and APIs are applied. The emulated states generally consist of all "base" states,
* but may have additional states such as "concurrent" or "rear display". Concurrent mode for
* example is activated via public API and can be active in both the "open" and "half folded"
* device states.
*/
private int mCurrentDeviceState = INVALID_DEVICE_STATE;
/**
* Base device state received via
* {@link DeviceStateManager.DeviceStateCallback#onBaseStateChanged(int)}.
* "Base" in this context means the "physical" state of the device.
*/
private int mCurrentBaseDeviceState = INVALID_DEVICE_STATE;
@NonNull
private final BaseDataProducer<String> mRawFoldSupplier;
private final DeviceStateCallback mDeviceStateCallback = new DeviceStateCallback() {
@Override
public void onStateChanged(int state) {
mCurrentDeviceState = state;
mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer
.this::notifyFoldingFeatureChange);
}
@Override
public void onBaseStateChanged(int state) {
mCurrentBaseDeviceState = state;
if (mDeviceStateToPostureMap.get(mCurrentDeviceState)
== COMMON_STATE_USE_BASE_STATE) {
mRawFoldSupplier.getData(DeviceStateManagerFoldingFeatureProducer
.this::notifyFoldingFeatureChange);
}
}
};
public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context,
@NonNull BaseDataProducer<String> rawFoldSupplier) {
mRawFoldSupplier = rawFoldSupplier;
@@ -92,12 +134,8 @@ public final class DeviceStateManagerFoldingFeatureProducer
}
if (mDeviceStateToPostureMap.size() > 0) {
DeviceStateCallback deviceStateCallback = (state) -> {
mCurrentDeviceState = state;
mRawFoldSupplier.getData(this::notifyFoldingFeatureChange);
};
Objects.requireNonNull(context.getSystemService(DeviceStateManager.class))
.registerCallback(context.getMainExecutor(), deviceStateCallback);
.registerCallback(context.getMainExecutor(), mDeviceStateCallback);
}
}
@@ -178,11 +216,18 @@ public final class DeviceStateManagerFoldingFeatureProducer
}
private List<CommonFoldingFeature> calculateFoldingFeature(String displayFeaturesString) {
final int globalHingeState = globalHingeState();
return parseListFromString(displayFeaturesString, globalHingeState);
return parseListFromString(displayFeaturesString, currentHingeState());
}
private int globalHingeState() {
return mDeviceStateToPostureMap.get(mCurrentDeviceState, COMMON_STATE_UNKNOWN);
@CommonFoldingFeature.State
private int currentHingeState() {
@CommonFoldingFeature.State
int posture = mDeviceStateToPostureMap.get(mCurrentDeviceState, COMMON_STATE_UNKNOWN);
if (posture == CommonFoldingFeature.COMMON_STATE_USE_BASE_STATE) {
posture = mDeviceStateToPostureMap.get(mCurrentBaseDeviceState, COMMON_STATE_UNKNOWN);
}
return posture;
}
}