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

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

Fix emulator not emitting folding feature.

Fix emulator not working for folds. Emulator is using DeviceStateManager
and that creates a mismatch on how data is merged.
Remove priority data produce as it adds more complexity than necessary.

Bug: 218872245
Test: Manual - build and run the emulator.
 Install the samples from the androidx.window:window-samples module
 Run folding feature sample.
Change-Id: I1f274a208952ff1418f2873356deacc588b99a2d
parent 44df60c3
Loading
Loading
Loading
Loading
+20 −7
Original line number Diff line number Diff line
@@ -31,11 +31,13 @@ import android.util.Log;
import android.util.SparseIntArray;

import androidx.window.util.BaseDataProducer;
import androidx.window.util.DataProducer;

import com.android.internal.R;

import java.util.List;
import java.util.Optional;
import java.util.Set;

/**
 * An implementation of {@link androidx.window.util.DataProducer} that returns the device's posture
@@ -48,7 +50,6 @@ public final class DeviceStateManagerFoldingFeatureProducer extends
            DeviceStateManagerFoldingFeatureProducer.class.getSimpleName();
    private static final boolean DEBUG = false;

    private final Context mContext;
    private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();

    private int mCurrentDeviceState = INVALID_DEVICE_STATE;
@@ -57,9 +58,12 @@ public final class DeviceStateManagerFoldingFeatureProducer extends
        mCurrentDeviceState = state;
        notifyDataChanged();
    };
    @NonNull
    private final DataProducer<String> mRawFoldSupplier;

    public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context) {
        mContext = context;
    public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context,
            @NonNull DataProducer<String> rawFoldSupplier) {
        mRawFoldSupplier = rawFoldSupplier;
        String[] deviceStatePosturePairs = context.getResources()
                .getStringArray(R.array.config_device_state_postures);
        for (String deviceStatePosturePair : deviceStatePosturePairs) {
@@ -97,12 +101,21 @@ public final class DeviceStateManagerFoldingFeatureProducer extends
    @Nullable
    public Optional<List<CommonFoldingFeature>> getData() {
        final int globalHingeState = globalHingeState();
        String displayFeaturesString = mContext.getResources().getString(
                R.string.config_display_features);
        if (TextUtils.isEmpty(displayFeaturesString)) {
        Optional<String> displayFeaturesString = mRawFoldSupplier.getData();
        if (displayFeaturesString.isEmpty() || TextUtils.isEmpty(displayFeaturesString.get())) {
            return Optional.empty();
        }
        return Optional.of(parseListFromString(displayFeaturesString, globalHingeState));
        return Optional.of(parseListFromString(displayFeaturesString.get(), globalHingeState));
    }

    @Override
    protected void onListenersChanged(Set<Runnable> callbacks) {
        super.onListenersChanged(callbacks);
        if (callbacks.isEmpty()) {
            mRawFoldSupplier.removeDataChangedCallback(this::notifyDataChanged);
        } else {
            mRawFoldSupplier.addDataChangedCallback(this::notifyDataChanged);
        }
    }

    private int globalHingeState() {
+41 −33
Original line number Diff line number Diff line
@@ -16,11 +16,6 @@

package androidx.window.common;

import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_FLAT;
import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_HALF_OPENED;
import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_UNKNOWN;
import static androidx.window.common.CommonFoldingFeature.parseListFromString;

import android.annotation.NonNull;
import android.content.ContentResolver;
import android.content.Context;
@@ -33,75 +28,88 @@ import android.text.TextUtils;

import androidx.window.util.BaseDataProducer;

import java.util.Collections;
import java.util.List;
import com.android.internal.R;

import java.util.Optional;
import java.util.Set;

/**
 * Implementation of {@link androidx.window.util.DataProducer} that produces
 * {@link CommonFoldingFeature} parsed from a string stored in {@link Settings}.
 * Implementation of {@link androidx.window.util.DataProducer} that produces a
 * {@link String} that can be parsed to a {@link CommonFoldingFeature}.
 * {@link RawFoldingFeatureProducer} searches for the value in two places. The first check is in
 * settings where the {@link String} property is saved with the key
 * {@link RawFoldingFeatureProducer#DISPLAY_FEATURES}. If this value is null or empty then the
 * value in {@link android.content.res.Resources} is used. If both are empty then
 * {@link RawFoldingFeatureProducer#getData()} returns an empty object.
 * {@link RawFoldingFeatureProducer} listens to changes in the setting so that it can override
 * the system {@link CommonFoldingFeature} data.
 */
public final class SettingsDisplayFeatureProducer
        extends BaseDataProducer<List<CommonFoldingFeature>> {
public final class RawFoldingFeatureProducer extends BaseDataProducer<String> {
    private static final String DISPLAY_FEATURES = "display_features";
    private static final String DEVICE_POSTURE = "device_posture";

    private final Uri mDevicePostureUri =
            Settings.Global.getUriFor(DEVICE_POSTURE);
    private final Uri mDisplayFeaturesUri =
            Settings.Global.getUriFor(DISPLAY_FEATURES);

    private final ContentResolver mResolver;
    private final ContentObserver mObserver;
    private final String mResourceFeature;
    private boolean mRegisteredObservers;

    public SettingsDisplayFeatureProducer(@NonNull Context context) {
    public RawFoldingFeatureProducer(@NonNull Context context) {
        mResolver = context.getContentResolver();
        mObserver = new SettingsObserver();
    }

    private int getPosture() {
        int posture = Settings.Global.getInt(mResolver, DEVICE_POSTURE, COMMON_STATE_UNKNOWN);
        if (posture == COMMON_STATE_HALF_OPENED || posture == COMMON_STATE_FLAT) {
            return posture;
        } else {
            return COMMON_STATE_UNKNOWN;
        }
        mResourceFeature = context.getResources().getString(R.string.config_display_features);
    }

    @Override
    @NonNull
    public Optional<List<CommonFoldingFeature>> getData() {
        String displayFeaturesString = Settings.Global.getString(mResolver, DISPLAY_FEATURES);
    public Optional<String> getData() {
        String displayFeaturesString = getFeatureString();
        if (displayFeaturesString == null) {
            return Optional.empty();
        }
        return Optional.of(displayFeaturesString);
    }

        if (TextUtils.isEmpty(displayFeaturesString)) {
            return Optional.of(Collections.emptyList());
    /**
     * Returns the {@link String} representation for a {@link CommonFoldingFeature} from settings if
     * present and falls back to the resource value if empty or {@code null}.
     */
    private String getFeatureString() {
        String settingsFeature = Settings.Global.getString(mResolver, DISPLAY_FEATURES);
        if (TextUtils.isEmpty(settingsFeature)) {
            return mResourceFeature;
        }
        return settingsFeature;
    }

    @Override
    protected void onListenersChanged(Set<Runnable> callbacks) {
        if (callbacks.isEmpty()) {
            unregisterObserversIfNeeded();
        } else {
            registerObserversIfNeeded();
        }
        return Optional.of(parseListFromString(displayFeaturesString, getPosture()));
    }

    /**
     * Registers settings observers, if needed. When settings observers are registered for this
     * producer callbacks for changes in data will be triggered.
     */
    public void registerObserversIfNeeded() {
    private void registerObserversIfNeeded() {
        if (mRegisteredObservers) {
            return;
        }
        mRegisteredObservers = true;
        mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendants */,
                mObserver /* ContentObserver */);
        mResolver.registerContentObserver(mDevicePostureUri, false, mObserver);
    }

    /**
     * Unregisters settings observers, if needed. When settings observers are unregistered for this
     * producer callbacks for changes in data will not be triggered.
     */
    public void unregisterObserversIfNeeded() {
    private void unregisterObserversIfNeeded() {
        if (!mRegisteredObservers) {
            return;
        }
@@ -116,7 +124,7 @@ public final class SettingsDisplayFeatureProducer

        @Override
        public void onChange(boolean selfChange, Uri uri) {
            if (mDisplayFeaturesUri.equals(uri) || mDevicePostureUri.equals(uri)) {
            if (mDisplayFeaturesUri.equals(uri)) {
                notifyDataChanged();
            }
        }
+6 −19
Original line number Diff line number Diff line
@@ -37,9 +37,8 @@ import androidx.annotation.NonNull;
import androidx.window.common.CommonFoldingFeature;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
import androidx.window.common.SettingsDisplayFeatureProducer;
import androidx.window.common.RawFoldingFeatureProducer;
import androidx.window.util.DataProducer;
import androidx.window.util.PriorityDataProducer;

import java.util.ArrayList;
import java.util.List;
@@ -62,17 +61,14 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
    private final Map<Activity, Consumer<WindowLayoutInfo>> mWindowLayoutChangeListeners =
            new ArrayMap<>();

    private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer;
    private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer;

    public WindowLayoutComponentImpl(Context context) {
        ((Application) context.getApplicationContext())
                .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
        mSettingsDisplayFeatureProducer = new SettingsDisplayFeatureProducer(context);
        mFoldingFeatureProducer = new PriorityDataProducer<>(List.of(
                mSettingsDisplayFeatureProducer,
                new DeviceStateManagerFoldingFeatureProducer(context)
        ));
        RawFoldingFeatureProducer foldingFeatureProducer = new RawFoldingFeatureProducer(context);
        mFoldingFeatureProducer = new DeviceStateManagerFoldingFeatureProducer(context,
                foldingFeatureProducer);
        mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
    }

@@ -85,7 +81,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
    public void addWindowLayoutInfoListener(@NonNull Activity activity,
            @NonNull Consumer<WindowLayoutInfo> consumer) {
        mWindowLayoutChangeListeners.put(activity, consumer);
        updateRegistrations();
        onDisplayFeaturesChanged();
    }

    /**
@@ -96,7 +92,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
    public void removeWindowLayoutInfoListener(
            @NonNull Consumer<WindowLayoutInfo> consumer) {
        mWindowLayoutChangeListeners.values().remove(consumer);
        updateRegistrations();
        onDisplayFeaturesChanged();
    }

    void updateWindowLayout(@NonNull Activity activity,
@@ -210,15 +206,6 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
        return features;
    }

    private void updateRegistrations() {
        if (hasListeners()) {
            mSettingsDisplayFeatureProducer.registerObserversIfNeeded();
        } else {
            mSettingsDisplayFeatureProducer.unregisterObserversIfNeeded();
        }
        onDisplayFeaturesChanged();
    }

    private final class NotifyOnConfigurationChanged extends EmptyLifecycleCallbacksAdapter {
        @Override
        public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
+4 −11
Original line number Diff line number Diff line
@@ -34,9 +34,8 @@ import androidx.annotation.NonNull;
import androidx.window.common.CommonFoldingFeature;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.common.EmptyLifecycleCallbacksAdapter;
import androidx.window.common.SettingsDisplayFeatureProducer;
import androidx.window.common.RawFoldingFeatureProducer;
import androidx.window.util.DataProducer;
import androidx.window.util.PriorityDataProducer;

import java.util.ArrayList;
import java.util.Collections;
@@ -52,16 +51,13 @@ class SampleSidecarImpl extends StubSidecar {

    private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer;

    private final SettingsDisplayFeatureProducer mSettingsFoldingFeatureProducer;

    SampleSidecarImpl(Context context) {
        ((Application) context.getApplicationContext())
                .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
        mSettingsFoldingFeatureProducer = new SettingsDisplayFeatureProducer(context);
        mFoldingFeatureProducer = new PriorityDataProducer<>(List.of(
                mSettingsFoldingFeatureProducer,
                new DeviceStateManagerFoldingFeatureProducer(context)
        ));
        DataProducer<String> settingsFeatureProducer = new RawFoldingFeatureProducer(context);
        mFoldingFeatureProducer = new DeviceStateManagerFoldingFeatureProducer(context,
                settingsFeatureProducer);

        mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
    }
@@ -142,10 +138,7 @@ class SampleSidecarImpl extends StubSidecar {
    @Override
    protected void onListenersChanged() {
        if (hasListeners()) {
            mSettingsFoldingFeatureProducer.registerObserversIfNeeded();
            onDisplayFeaturesChanged();
        } else {
            mSettingsFoldingFeatureProducer.unregisterObserversIfNeeded();
        }
    }

+4 −0
Original line number Diff line number Diff line
@@ -33,13 +33,17 @@ public abstract class BaseDataProducer<T> implements DataProducer<T> {
    @Override
    public final void addDataChangedCallback(@NonNull Runnable callback) {
        mCallbacks.add(callback);
        onListenersChanged(mCallbacks);
    }

    @Override
    public final void removeDataChangedCallback(@NonNull Runnable callback) {
        mCallbacks.remove(callback);
        onListenersChanged(mCallbacks);
    }

    protected void onListenersChanged(Set<Runnable> callbacks) {}

    /**
     * Called to notify all registered callbacks that the data provided by {@link #getData()} has
     * changed.
Loading