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

Commit ae42f512 authored by Diego Vela's avatar Diego Vela
Browse files

Ignore folding feature when it is not on the active display.

Ignore the folding feature states that are not reported through
extensions. There is a crash when trying to process a folding
feature while the corresponding display is not active. We had a
fallback value for the posture of flat but the bounds that we
calculated would be incorrect.  We removed the default value
and ignore the folding feature when the corresponding display
is not active.

Bug: 204477995
Test: manual - install the samples app from WindowManager
Test: close a folding device and open the samples app
Test: select the display feature sample.
Test: app should not crash.
Change-Id: I102ed61618915ae6782c40964882a54184538e81
parent 7e37ab84
Loading
Loading
Loading
Loading
+60 −11
Original line number Diff line number Diff line
@@ -18,9 +18,12 @@ package androidx.window.extensions.layout;

import static android.view.Display.DEFAULT_DISPLAY;

import static androidx.window.common.DisplayFeature.COMMON_STATE_FLAT;
import static androidx.window.common.DisplayFeature.COMMON_STATE_HALF_OPENED;
import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;

import android.annotation.Nullable;
import android.app.Activity;
import android.content.Context;
import android.graphics.Rect;
@@ -119,22 +122,45 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
        return !mWindowLayoutChangeListeners.isEmpty();
    }

    private int getFeatureState(DisplayFeature feature) {
    /**
     * Calculate the {@link DisplayFeature.State} from the feature or the device posture producer.
     * If the given {@link DisplayFeature.State} is not valid then {@code null} will be returned.
     * The {@link FoldingFeature} should be ignored in the case of an invalid
     * {@link DisplayFeature.State}.
     *
     * @param feature a {@link DisplayFeature} to provide the feature state if present.
     * @return {@link DisplayFeature.State} of the hinge if present or the state from the posture
     * produce if present.
     */
    @Nullable
    private Integer getFeatureState(DisplayFeature feature) {
        Integer featureState = feature.getState();
        Optional<Integer> posture = mDevicePostureProducer.getData();
        int fallbackPosture = posture.orElse(DisplayFeature.COMMON_STATE_FLAT);
        int displayFeatureState = featureState == null ? fallbackPosture : featureState;
        return convertToExtensionState(displayFeatureState);
        Integer state = featureState == null ? posture.orElse(null) : featureState;
        return convertToExtensionState(state);
    }

    private int convertToExtensionState(int state) {
        switch (state) {
            case DisplayFeature.COMMON_STATE_FLAT:
    /**
     * A convenience method to translate from the common feature state to the extensions feature
     * state.  More specifically, translates from {@link DisplayFeature.State} to
     * {@link FoldingFeature.STATE_FLAT} or {@link FoldingFeature.STATE_HALF_OPENED}. If it is not
     * possible to translate, then we will return a {@code null} value.
     *
     * @param state if it matches a value in {@link DisplayFeature.State}, {@code null} otherwise.
     * @return a {@link FoldingFeature.STATE_FLAT} or {@link FoldingFeature.STATE_HALF_OPENED} if
     * the given state matches a value in {@link DisplayFeature.State} and {@code null} otherwise.
     */
    @Nullable
    private Integer convertToExtensionState(@Nullable Integer state) {
        if (state == null) { // The null check avoids a NullPointerException.
            return null;
        } else if (state == COMMON_STATE_FLAT) {
            return FoldingFeature.STATE_FLAT;
            case DisplayFeature.COMMON_STATE_HALF_OPENED:
        } else if (state == COMMON_STATE_HALF_OPENED) {
            return FoldingFeature.STATE_HALF_OPENED;
        } else {
            return null;
        }
        return FoldingFeature.STATE_FLAT;
    }

    private void onDisplayFeaturesChanged() {
@@ -151,6 +177,25 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
        return new WindowLayoutInfo(displayFeatures);
    }

    /**
     * Translate from the {@link DisplayFeature} to
     * {@link androidx.window.extensions.layout.DisplayFeature} for a given {@link Activity}. If a
     * {@link DisplayFeature} is not valid then it will be omitted.
     *
     * For a {@link FoldingFeature} the bounds are localized into the {@link Activity} window
     * coordinate space and the state is calculated either from {@link DisplayFeature#getState()} or
     * {@link #mDisplayFeatureProducer}. The state from {@link #mDisplayFeatureProducer} may not be
     * valid since {@link #mDisplayFeatureProducer} is a general state controller. If the state is
     * not valid, the {@link FoldingFeature} is omitted from the {@link List} of
     * {@link androidx.window.extensions.layout.DisplayFeature}. If the bounds are not valid,
     * constructing a {@link FoldingFeature} will throw an {@link IllegalArgumentException} since
     * this can cause negative UI effects down stream.
     *
     * @param activity a proxy for the {@link android.view.Window} that contains the
     * {@link androidx.window.extensions.layout.DisplayFeature}.
     * @return a {@link List} of valid {@link androidx.window.extensions.layout.DisplayFeature} that
     * are within the {@link android.view.Window} of the {@link Activity}
     */
    private List<androidx.window.extensions.layout.DisplayFeature> getDisplayFeatures(
            @NonNull Activity activity) {
        List<androidx.window.extensions.layout.DisplayFeature> features = new ArrayList<>();
@@ -170,6 +215,10 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
        if (storedFeatures.isPresent()) {

            for (DisplayFeature baseFeature : storedFeatures.get()) {
                Integer state = getFeatureState(baseFeature);
                if (state == null) {
                    continue;
                }
                Rect featureRect = baseFeature.getRect();
                rotateRectToDisplayRotation(displayId, featureRect);
                transformToWindowSpaceRect(activity, featureRect);