From cb70f071130f1196457216b75b65178fde49d9ee Mon Sep 17 00:00:00 2001 From: Diego Vela Date: Wed, 30 Mar 2022 13:23:26 -0700 Subject: [PATCH] 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 --- ...iceStateManagerFoldingFeatureProducer.java | 27 +++++-- ...er.java => RawFoldingFeatureProducer.java} | 74 ++++++++++--------- .../layout/WindowLayoutComponentImpl.java | 25 ++----- .../window/sidecar/SampleSidecarImpl.java | 15 +--- .../window/util/BaseDataProducer.java | 4 + .../window/util/PriorityDataProducer.java | 56 -------------- 6 files changed, 75 insertions(+), 126 deletions(-) rename libs/WindowManager/Jetpack/src/androidx/window/common/{SettingsDisplayFeatureProducer.java => RawFoldingFeatureProducer.java} (58%) delete mode 100644 libs/WindowManager/Jetpack/src/androidx/window/util/PriorityDataProducer.java diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java index 6987401525b4a..fdcb7be597d50 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.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 mRawFoldSupplier; - public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context) { - mContext = context; + public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context, + @NonNull DataProducer 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> getData() { final int globalHingeState = globalHingeState(); - String displayFeaturesString = mContext.getResources().getString( - R.string.config_display_features); - if (TextUtils.isEmpty(displayFeaturesString)) { + Optional 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 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/SettingsDisplayFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java similarity index 58% rename from libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java rename to libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java index 0e696eb8efb79..69ad1badce60a 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/RawFoldingFeatureProducer.java @@ -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> { +public final class RawFoldingFeatureProducer extends BaseDataProducer { 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> getData() { - String displayFeaturesString = Settings.Global.getString(mResolver, DISPLAY_FEATURES); + public Optional 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 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(); } } 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 a4fbdbc493f53..2f7d958434a3b 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.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> mWindowLayoutChangeListeners = new ArrayMap<>(); - private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer; private final DataProducer> 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 consumer) { mWindowLayoutChangeListeners.put(activity, consumer); - updateRegistrations(); + onDisplayFeaturesChanged(); } /** @@ -96,7 +92,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { public void removeWindowLayoutInfoListener( @NonNull Consumer 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) { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java index c7b7093470607..970f0a2af6326 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.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> 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 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(); } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java index 0a46703451abc..930db3b701b7b 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 @@ public abstract class BaseDataProducer implements DataProducer { @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 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 990ae20cc934d..0000000000000 --- 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. - *

- * 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 The type of data this producer returns through {@link #getData()}. - */ -public final class PriorityDataProducer extends BaseDataProducer { - private final List> mChildProducers; - - public PriorityDataProducer(List> childProducers) { - mChildProducers = childProducers; - for (DataProducer childProducer : mChildProducers) { - childProducer.addDataChangedCallback(this::notifyDataChanged); - } - } - - @Nullable - @Override - public Optional getData() { - for (DataProducer childProducer : mChildProducers) { - final Optional data = childProducer.getData(); - if (data.isPresent()) { - return data; - } - } - return Optional.empty(); - } -}