Support FoldingFeature for CONCURRENT state
Some emulated states such as CONCURRENT can be used in multiple
base/physical states (OPENED and HALF_OPENED).
This change adds support for the one-to-many mapping by:
1) Adding COMMON_STATE_USE_BASE_STATE constant. An internal state
where the CommonFoldingFeature.State should be derived from the
base/physical device state.
2) Updated DeviceStateCallback implementation which now listens to
both onStateChanged (existing) and onBaseStateChanged (new).
For emulated states configured as COMMON_STATE_USE_BASE_STATE,
the base/physical state is sent to the client.
Slight code cleanup:
1) Adds COMMON_STATE_NO_FOLDING_FEATURES which is used by some
device configurations but not currently defined. This constant
is used for device states where folding features do not exist.
2) A few function/variable renames
3) Added annotations
Bug: 268613897
Test: Using sample app, enter concurrent mode and then check
folding features
Change-Id: I8e2ad9e52996d6e337b22a21312518bf839c5670
This commit is contained in:
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user