Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit dd25a9dc authored by Kevin Chyn's avatar Kevin Chyn Committed by Android (Google) Code Review
Browse files

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

parents 1ce0d2cf c3850324
Loading
Loading
Loading
Loading
+42 −12
Original line number Diff line number Diff line
@@ -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");
        }
+54 −9
Original line number Diff line number Diff line
@@ -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());
    }

    @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);
        }

    private int globalHingeState() {
        return mDeviceStateToPostureMap.get(mCurrentDeviceState, COMMON_STATE_UNKNOWN);
        return posture;
    }
}