Merge "Fix emulator not emitting folding feature." into tm-dev am: dbda026b6f am: 94d417a62c

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17511593

Change-Id: Ibc0abfac92e08f547c7ff1418de0bb4771c6a48a
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
index 6987401..fdcb7be 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java
@@ -31,11 +31,13 @@
 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 @@
             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 @@
         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 @@
     @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() {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java
new file mode 100644
index 0000000..69ad1ba
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.window.common;
+
+import android.annotation.NonNull;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Looper;
+import android.provider.Settings;
+import android.text.TextUtils;
+
+import androidx.window.util.BaseDataProducer;
+
+import com.android.internal.R;
+
+import java.util.Optional;
+import java.util.Set;
+
+/**
+ * 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 RawFoldingFeatureProducer extends BaseDataProducer<String> {
+    private static final String DISPLAY_FEATURES = "display_features";
+
+    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 RawFoldingFeatureProducer(@NonNull Context context) {
+        mResolver = context.getContentResolver();
+        mObserver = new SettingsObserver();
+        mResourceFeature = context.getResources().getString(R.string.config_display_features);
+    }
+
+    @Override
+    @NonNull
+    public Optional<String> getData() {
+        String displayFeaturesString = getFeatureString();
+        if (displayFeaturesString == null) {
+            return Optional.empty();
+        }
+        return Optional.of(displayFeaturesString);
+    }
+
+    /**
+     * 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();
+        }
+    }
+
+    /**
+     * Registers settings observers, if needed. When settings observers are registered for this
+     * producer callbacks for changes in data will be triggered.
+     */
+    private void registerObserversIfNeeded() {
+        if (mRegisteredObservers) {
+            return;
+        }
+        mRegisteredObservers = true;
+        mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendants */,
+                mObserver /* ContentObserver */);
+    }
+
+    /**
+     * Unregisters settings observers, if needed. When settings observers are unregistered for this
+     * producer callbacks for changes in data will not be triggered.
+     */
+    private void unregisterObserversIfNeeded() {
+        if (!mRegisteredObservers) {
+            return;
+        }
+        mRegisteredObservers = false;
+        mResolver.unregisterContentObserver(mObserver);
+    }
+
+    private final class SettingsObserver extends ContentObserver {
+        SettingsObserver() {
+            super(new Handler(Looper.getMainLooper()));
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            if (mDisplayFeaturesUri.equals(uri)) {
+                notifyDataChanged();
+            }
+        }
+    }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java
deleted file mode 100644
index 0e696eb..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-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;
-import android.database.ContentObserver;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.Settings;
-import android.text.TextUtils;
-
-import androidx.window.util.BaseDataProducer;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Optional;
-
-/**
- * Implementation of {@link androidx.window.util.DataProducer} that produces
- * {@link CommonFoldingFeature} parsed from a string stored in {@link Settings}.
- */
-public final class SettingsDisplayFeatureProducer
-        extends BaseDataProducer<List<CommonFoldingFeature>> {
-    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 boolean mRegisteredObservers;
-
-    public SettingsDisplayFeatureProducer(@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;
-        }
-    }
-
-    @Override
-    @NonNull
-    public Optional<List<CommonFoldingFeature>> getData() {
-        String displayFeaturesString = Settings.Global.getString(mResolver, DISPLAY_FEATURES);
-        if (displayFeaturesString == null) {
-            return Optional.empty();
-        }
-
-        if (TextUtils.isEmpty(displayFeaturesString)) {
-            return Optional.of(Collections.emptyList());
-        }
-        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() {
-        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() {
-        if (!mRegisteredObservers) {
-            return;
-        }
-        mRegisteredObservers = false;
-        mResolver.unregisterContentObserver(mObserver);
-    }
-
-    private final class SettingsObserver extends ContentObserver {
-        SettingsObserver() {
-            super(new Handler(Looper.getMainLooper()));
-        }
-
-        @Override
-        public void onChange(boolean selfChange, Uri uri) {
-            if (mDisplayFeaturesUri.equals(uri) || mDevicePostureUri.equals(uri)) {
-                notifyDataChanged();
-            }
-        }
-    }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
index a4fbdbc..2f7d958 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java
@@ -37,9 +37,8 @@
 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 @@
     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 void addWindowLayoutInfoListener(@NonNull Activity activity,
             @NonNull Consumer<WindowLayoutInfo> consumer) {
         mWindowLayoutChangeListeners.put(activity, consumer);
-        updateRegistrations();
+        onDisplayFeaturesChanged();
     }
 
     /**
@@ -96,7 +92,7 @@
     public void removeWindowLayoutInfoListener(
             @NonNull Consumer<WindowLayoutInfo> consumer) {
         mWindowLayoutChangeListeners.values().remove(consumer);
-        updateRegistrations();
+        onDisplayFeaturesChanged();
     }
 
     void updateWindowLayout(@NonNull Activity activity,
@@ -210,15 +206,6 @@
         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) {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
index c7b7093..970f0a2 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
@@ -34,9 +34,8 @@
 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 @@
 
     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 @@
     @Override
     protected void onListenersChanged() {
         if (hasListeners()) {
-            mSettingsFoldingFeatureProducer.registerObserversIfNeeded();
             onDisplayFeaturesChanged();
-        } else {
-            mSettingsFoldingFeatureProducer.unregisterObserversIfNeeded();
         }
     }
 
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java
index 0a46703451..930db3b 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java
@@ -33,13 +33,17 @@
     @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.
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/PriorityDataProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/PriorityDataProducer.java
deleted file mode 100644
index 990ae20..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/util/PriorityDataProducer.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.window.util;
-
-import android.annotation.Nullable;
-
-import java.util.List;
-import java.util.Optional;
-
-/**
- * Implementation of {@link DataProducer} that delegates calls to {@link #getData()} to the list of
- * provided child producers.
- * <p>
- * The value returned is based on the precedence of the supplied children where the producer with
- * index 0 has a higher precedence than producers that come later in the list. When a producer with
- * a higher precedence has a non-empty value returned from {@link #getData()}, its value will be
- * returned from an instance of this class, ignoring all other producers with lower precedence.
- *
- * @param <T> The type of data this producer returns through {@link #getData()}.
- */
-public final class PriorityDataProducer<T> extends BaseDataProducer<T> {
-    private final List<DataProducer<T>> mChildProducers;
-
-    public PriorityDataProducer(List<DataProducer<T>> childProducers) {
-        mChildProducers = childProducers;
-        for (DataProducer<T> childProducer : mChildProducers) {
-            childProducer.addDataChangedCallback(this::notifyDataChanged);
-        }
-    }
-
-    @Nullable
-    @Override
-    public Optional<T> getData() {
-        for (DataProducer<T> childProducer : mChildProducers) {
-            final Optional<T> data = childProducer.getData();
-            if (data.isPresent()) {
-                return data;
-            }
-        }
-        return Optional.empty();
-    }
-}