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>
This commit is contained in:
Diego Vela
2022-04-04 16:50:13 +00:00
committed by Automerger Merge Worker
6 changed files with 75 additions and 126 deletions

View File

@@ -31,11 +31,13 @@ import android.util.Log;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import androidx.window.util.BaseDataProducer; import androidx.window.util.BaseDataProducer;
import androidx.window.util.DataProducer;
import com.android.internal.R; import com.android.internal.R;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
/** /**
* An implementation of {@link androidx.window.util.DataProducer} that returns the device's posture * 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(); DeviceStateManagerFoldingFeatureProducer.class.getSimpleName();
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
private final Context mContext;
private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray(); private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
private int mCurrentDeviceState = INVALID_DEVICE_STATE; private int mCurrentDeviceState = INVALID_DEVICE_STATE;
@@ -57,9 +58,12 @@ public final class DeviceStateManagerFoldingFeatureProducer extends
mCurrentDeviceState = state; mCurrentDeviceState = state;
notifyDataChanged(); notifyDataChanged();
}; };
@NonNull
private final DataProducer<String> mRawFoldSupplier;
public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context) { public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context,
mContext = context; @NonNull DataProducer<String> rawFoldSupplier) {
mRawFoldSupplier = rawFoldSupplier;
String[] deviceStatePosturePairs = context.getResources() String[] deviceStatePosturePairs = context.getResources()
.getStringArray(R.array.config_device_state_postures); .getStringArray(R.array.config_device_state_postures);
for (String deviceStatePosturePair : deviceStatePosturePairs) { for (String deviceStatePosturePair : deviceStatePosturePairs) {
@@ -97,12 +101,21 @@ public final class DeviceStateManagerFoldingFeatureProducer extends
@Nullable @Nullable
public Optional<List<CommonFoldingFeature>> getData() { public Optional<List<CommonFoldingFeature>> getData() {
final int globalHingeState = globalHingeState(); final int globalHingeState = globalHingeState();
String displayFeaturesString = mContext.getResources().getString( Optional<String> displayFeaturesString = mRawFoldSupplier.getData();
R.string.config_display_features); if (displayFeaturesString.isEmpty() || TextUtils.isEmpty(displayFeaturesString.get())) {
if (TextUtils.isEmpty(displayFeaturesString)) {
return Optional.empty(); 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() { private int globalHingeState() {

View File

@@ -16,11 +16,6 @@
package androidx.window.common; 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.annotation.NonNull;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
@@ -33,75 +28,88 @@ import android.text.TextUtils;
import androidx.window.util.BaseDataProducer; import androidx.window.util.BaseDataProducer;
import java.util.Collections; import com.android.internal.R;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
/** /**
* Implementation of {@link androidx.window.util.DataProducer} that produces * Implementation of {@link androidx.window.util.DataProducer} that produces a
* {@link CommonFoldingFeature} parsed from a string stored in {@link Settings}. * {@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 public final class RawFoldingFeatureProducer extends BaseDataProducer<String> {
extends BaseDataProducer<List<CommonFoldingFeature>> {
private static final String DISPLAY_FEATURES = "display_features"; 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 = private final Uri mDisplayFeaturesUri =
Settings.Global.getUriFor(DISPLAY_FEATURES); Settings.Global.getUriFor(DISPLAY_FEATURES);
private final ContentResolver mResolver; private final ContentResolver mResolver;
private final ContentObserver mObserver; private final ContentObserver mObserver;
private final String mResourceFeature;
private boolean mRegisteredObservers; private boolean mRegisteredObservers;
public SettingsDisplayFeatureProducer(@NonNull Context context) { public RawFoldingFeatureProducer(@NonNull Context context) {
mResolver = context.getContentResolver(); mResolver = context.getContentResolver();
mObserver = new SettingsObserver(); mObserver = new SettingsObserver();
} mResourceFeature = context.getResources().getString(R.string.config_display_features);
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 @Override
@NonNull @NonNull
public Optional<List<CommonFoldingFeature>> getData() { public Optional<String> getData() {
String displayFeaturesString = Settings.Global.getString(mResolver, DISPLAY_FEATURES); String displayFeaturesString = getFeatureString();
if (displayFeaturesString == null) { if (displayFeaturesString == null) {
return Optional.empty(); 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 * Registers settings observers, if needed. When settings observers are registered for this
* producer callbacks for changes in data will be triggered. * producer callbacks for changes in data will be triggered.
*/ */
public void registerObserversIfNeeded() { private void registerObserversIfNeeded() {
if (mRegisteredObservers) { if (mRegisteredObservers) {
return; return;
} }
mRegisteredObservers = true; mRegisteredObservers = true;
mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendants */, mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendants */,
mObserver /* ContentObserver */); mObserver /* ContentObserver */);
mResolver.registerContentObserver(mDevicePostureUri, false, mObserver);
} }
/** /**
* Unregisters settings observers, if needed. When settings observers are unregistered for this * Unregisters settings observers, if needed. When settings observers are unregistered for this
* producer callbacks for changes in data will not be triggered. * producer callbacks for changes in data will not be triggered.
*/ */
public void unregisterObserversIfNeeded() { private void unregisterObserversIfNeeded() {
if (!mRegisteredObservers) { if (!mRegisteredObservers) {
return; return;
} }
@@ -116,7 +124,7 @@ public final class SettingsDisplayFeatureProducer
@Override @Override
public void onChange(boolean selfChange, Uri uri) { public void onChange(boolean selfChange, Uri uri) {
if (mDisplayFeaturesUri.equals(uri) || mDevicePostureUri.equals(uri)) { if (mDisplayFeaturesUri.equals(uri)) {
notifyDataChanged(); notifyDataChanged();
} }
} }

View File

@@ -37,9 +37,8 @@ import androidx.annotation.NonNull;
import androidx.window.common.CommonFoldingFeature; import androidx.window.common.CommonFoldingFeature;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.common.EmptyLifecycleCallbacksAdapter; import androidx.window.common.EmptyLifecycleCallbacksAdapter;
import androidx.window.common.SettingsDisplayFeatureProducer; import androidx.window.common.RawFoldingFeatureProducer;
import androidx.window.util.DataProducer; import androidx.window.util.DataProducer;
import androidx.window.util.PriorityDataProducer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -62,17 +61,14 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
private final Map<Activity, Consumer<WindowLayoutInfo>> mWindowLayoutChangeListeners = private final Map<Activity, Consumer<WindowLayoutInfo>> mWindowLayoutChangeListeners =
new ArrayMap<>(); new ArrayMap<>();
private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer;
private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer; private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer;
public WindowLayoutComponentImpl(Context context) { public WindowLayoutComponentImpl(Context context) {
((Application) context.getApplicationContext()) ((Application) context.getApplicationContext())
.registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged()); .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
mSettingsDisplayFeatureProducer = new SettingsDisplayFeatureProducer(context); RawFoldingFeatureProducer foldingFeatureProducer = new RawFoldingFeatureProducer(context);
mFoldingFeatureProducer = new PriorityDataProducer<>(List.of( mFoldingFeatureProducer = new DeviceStateManagerFoldingFeatureProducer(context,
mSettingsDisplayFeatureProducer, foldingFeatureProducer);
new DeviceStateManagerFoldingFeatureProducer(context)
));
mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
} }
@@ -85,7 +81,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
public void addWindowLayoutInfoListener(@NonNull Activity activity, public void addWindowLayoutInfoListener(@NonNull Activity activity,
@NonNull Consumer<WindowLayoutInfo> consumer) { @NonNull Consumer<WindowLayoutInfo> consumer) {
mWindowLayoutChangeListeners.put(activity, consumer); mWindowLayoutChangeListeners.put(activity, consumer);
updateRegistrations(); onDisplayFeaturesChanged();
} }
/** /**
@@ -96,7 +92,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
public void removeWindowLayoutInfoListener( public void removeWindowLayoutInfoListener(
@NonNull Consumer<WindowLayoutInfo> consumer) { @NonNull Consumer<WindowLayoutInfo> consumer) {
mWindowLayoutChangeListeners.values().remove(consumer); mWindowLayoutChangeListeners.values().remove(consumer);
updateRegistrations(); onDisplayFeaturesChanged();
} }
void updateWindowLayout(@NonNull Activity activity, void updateWindowLayout(@NonNull Activity activity,
@@ -210,15 +206,6 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent {
return features; return features;
} }
private void updateRegistrations() {
if (hasListeners()) {
mSettingsDisplayFeatureProducer.registerObserversIfNeeded();
} else {
mSettingsDisplayFeatureProducer.unregisterObserversIfNeeded();
}
onDisplayFeaturesChanged();
}
private final class NotifyOnConfigurationChanged extends EmptyLifecycleCallbacksAdapter { private final class NotifyOnConfigurationChanged extends EmptyLifecycleCallbacksAdapter {
@Override @Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) { public void onActivityCreated(Activity activity, Bundle savedInstanceState) {

View File

@@ -34,9 +34,8 @@ import androidx.annotation.NonNull;
import androidx.window.common.CommonFoldingFeature; import androidx.window.common.CommonFoldingFeature;
import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; import androidx.window.common.DeviceStateManagerFoldingFeatureProducer;
import androidx.window.common.EmptyLifecycleCallbacksAdapter; import androidx.window.common.EmptyLifecycleCallbacksAdapter;
import androidx.window.common.SettingsDisplayFeatureProducer; import androidx.window.common.RawFoldingFeatureProducer;
import androidx.window.util.DataProducer; import androidx.window.util.DataProducer;
import androidx.window.util.PriorityDataProducer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@@ -52,16 +51,13 @@ class SampleSidecarImpl extends StubSidecar {
private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer; private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer;
private final SettingsDisplayFeatureProducer mSettingsFoldingFeatureProducer;
SampleSidecarImpl(Context context) { SampleSidecarImpl(Context context) {
((Application) context.getApplicationContext()) ((Application) context.getApplicationContext())
.registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged()); .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged());
mSettingsFoldingFeatureProducer = new SettingsDisplayFeatureProducer(context); DataProducer<String> settingsFeatureProducer = new RawFoldingFeatureProducer(context);
mFoldingFeatureProducer = new PriorityDataProducer<>(List.of( mFoldingFeatureProducer = new DeviceStateManagerFoldingFeatureProducer(context,
mSettingsFoldingFeatureProducer, settingsFeatureProducer);
new DeviceStateManagerFoldingFeatureProducer(context)
));
mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
} }
@@ -142,10 +138,7 @@ class SampleSidecarImpl extends StubSidecar {
@Override @Override
protected void onListenersChanged() { protected void onListenersChanged() {
if (hasListeners()) { if (hasListeners()) {
mSettingsFoldingFeatureProducer.registerObserversIfNeeded();
onDisplayFeaturesChanged(); onDisplayFeaturesChanged();
} else {
mSettingsFoldingFeatureProducer.unregisterObserversIfNeeded();
} }
} }

View File

@@ -33,13 +33,17 @@ public abstract class BaseDataProducer<T> implements DataProducer<T> {
@Override @Override
public final void addDataChangedCallback(@NonNull Runnable callback) { public final void addDataChangedCallback(@NonNull Runnable callback) {
mCallbacks.add(callback); mCallbacks.add(callback);
onListenersChanged(mCallbacks);
} }
@Override @Override
public final void removeDataChangedCallback(@NonNull Runnable callback) { public final void removeDataChangedCallback(@NonNull Runnable callback) {
mCallbacks.remove(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 * Called to notify all registered callbacks that the data provided by {@link #getData()} has
* changed. * changed.

View File

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