Merge changes from topic "jumbo-sidecar-posture" into sc-dev
* changes:
Update sample Sidecar/Extension implementation to allow mapping system device state to posture via a config.
Seperate SettingsConfigProvider into producers.
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index b0327a5..b4ef63f 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4649,6 +4649,10 @@
<!-- WindowsManager JetPack display features -->
<string name="config_display_features" translatable="false" />
+ <!-- Map of System DeviceState supplied by DeviceStateManager to WM Jetpack posture. Must be in
+ the format [System DeviceState]:[WM Jetpack Posture], for example: "0:1". -->
+ <string-array name="config_device_state_postures" translatable="false" />
+
<!-- Aspect ratio of letterboxing for fixed orientation. Values <= 1.0 will be ignored.
Note: Activity min/max aspect ratio restrictions will still be respected.
Therefore this override can control the maximum screen area that can be occupied by
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index cdeb71c..1d74d85 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4182,6 +4182,7 @@
<java-symbol type="dimen" name="default_background_blur_radius" />
<java-symbol type="array" name="config_keep_warming_services" />
<java-symbol type="string" name="config_display_features" />
+ <java-symbol type="array" name="config_device_state_postures" />
<java-symbol type="dimen" name="controls_thumbnail_image_max_height" />
<java-symbol type="dimen" name="controls_thumbnail_image_max_width" />
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp
index d8f00bb..dc4b563 100644
--- a/libs/WindowManager/Jetpack/Android.bp
+++ b/libs/WindowManager/Jetpack/Android.bp
@@ -30,13 +30,20 @@
java_library {
name: "androidx.window.sidecar",
- srcs: ["src/androidx/window/sidecar/**/*.java", "src/androidx/window/util/**/*.java"],
+ srcs: [
+ "src/androidx/window/sidecar/**/*.java",
+ "src/androidx/window/util/**/*.java",
+ "src/androidx/window/common/**/*.java",
+ ],
static_libs: ["window-sidecar"],
installable: true,
sdk_version: "core_platform",
system_ext_specific: true,
- libs: ["framework", "androidx.annotation_annotation",],
- required: ["androidx.window.sidecar.xml",],
+ libs: [
+ "framework",
+ "androidx.annotation_annotation",
+ ],
+ required: ["androidx.window.sidecar.xml"],
}
prebuilt_etc {
@@ -58,13 +65,20 @@
java_library {
name: "androidx.window.extensions",
- srcs: ["src/androidx/window/extensions/**/*.java", "src/androidx/window/util/**/*.java"],
+ srcs: [
+ "src/androidx/window/extensions/**/*.java",
+ "src/androidx/window/util/**/*.java",
+ "src/androidx/window/common/**/*.java",
+ ],
static_libs: ["window-extensions"],
installable: true,
sdk_version: "core_platform",
system_ext_specific: true,
- libs: ["framework", "androidx.annotation_annotation",],
- required: ["androidx.window.extensions.xml",],
+ libs: [
+ "framework",
+ "androidx.annotation_annotation",
+ ],
+ required: ["androidx.window.extensions.xml"],
}
prebuilt_etc {
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java
new file mode 100644
index 0000000..e6ad011
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java
@@ -0,0 +1,128 @@
+/*
+ * 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.util.ExtensionHelper.isZero;
+
+import android.annotation.Nullable;
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */
+final class CommonDisplayFeature implements DisplayFeature {
+ private static final Pattern FEATURE_PATTERN =
+ Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]");
+
+ private static final String FEATURE_TYPE_FOLD = "fold";
+ private static final String FEATURE_TYPE_HINGE = "hinge";
+
+ // TODO(b/183049815): Support feature strings that include the state of the feature.
+ /**
+ * Parses a display feature from a string.
+ *
+ * @throws IllegalArgumentException if the provided string is improperly formatted or could not
+ * otherwise be parsed.
+ *
+ * @see #FEATURE_PATTERN
+ */
+ @NonNull
+ static CommonDisplayFeature parseFromString(@NonNull String string) {
+ Matcher featureMatcher = FEATURE_PATTERN.matcher(string);
+ if (!featureMatcher.matches()) {
+ throw new IllegalArgumentException("Malformed feature description format: " + string);
+ }
+ try {
+ String featureType = featureMatcher.group(1);
+ int type;
+ switch (featureType) {
+ case FEATURE_TYPE_FOLD:
+ type = 1 /* TYPE_FOLD */;
+ break;
+ case FEATURE_TYPE_HINGE:
+ type = 2 /* TYPE_HINGE */;
+ break;
+ default: {
+ throw new IllegalArgumentException("Malformed feature type: " + featureType);
+ }
+ }
+
+ int left = Integer.parseInt(featureMatcher.group(2));
+ int top = Integer.parseInt(featureMatcher.group(3));
+ int right = Integer.parseInt(featureMatcher.group(4));
+ int bottom = Integer.parseInt(featureMatcher.group(5));
+ Rect featureRect = new Rect(left, top, right, bottom);
+ if (isZero(featureRect)) {
+ throw new IllegalArgumentException("Feature has empty bounds: " + string);
+ }
+
+ return new CommonDisplayFeature(type, null, featureRect);
+ } catch (NumberFormatException e) {
+ throw new IllegalArgumentException("Malformed feature description: " + string, e);
+ }
+ }
+
+ private final int mType;
+ @Nullable
+ private final Integer mState;
+ @NonNull
+ private final Rect mRect;
+
+ CommonDisplayFeature(int type, @Nullable Integer state, @NonNull Rect rect) {
+ this.mType = type;
+ this.mState = state;
+ if (rect.width() == 0 && rect.height() == 0) {
+ throw new IllegalArgumentException(
+ "Display feature rectangle cannot have zero width and height simultaneously.");
+ }
+ this.mRect = rect;
+ }
+
+ public int getType() {
+ return mType;
+ }
+
+ /** Returns the state of the feature, or {@code null} if the feature has no state. */
+ @Nullable
+ public Integer getState() {
+ return mState;
+ }
+
+ @NonNull
+ public Rect getRect() {
+ return mRect;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ CommonDisplayFeature that = (CommonDisplayFeature) o;
+ return mType == that.mType
+ && Objects.equals(mState, that.mState)
+ && mRect.equals(that.mRect);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mType, mState, mRect);
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerPostureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerPostureProducer.java
new file mode 100644
index 0000000..fa9a5a8
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerPostureProducer.java
@@ -0,0 +1,93 @@
+/*
+ * 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 android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.devicestate.DeviceStateManager;
+import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback;
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import androidx.window.util.BaseDataProducer;
+
+import com.android.internal.R;
+
+import java.util.Optional;
+
+/**
+ * An implementation of {@link androidx.window.util.DataProducer} that returns the device's posture
+ * by mapping the state returned from {@link DeviceStateManager} to values provided in the resources
+ * config at {@link R.array#config_device_state_postures}.
+ */
+public final class DeviceStateManagerPostureProducer extends BaseDataProducer<Integer> {
+ private static final String TAG = "ConfigDevicePostureProducer";
+ private static final boolean DEBUG = false;
+
+ private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray();
+
+ private int mCurrentDeviceState = INVALID_DEVICE_STATE;
+
+ private final DeviceStateCallback mDeviceStateCallback = (state) -> {
+ mCurrentDeviceState = state;
+ notifyDataChanged();
+ };
+
+ public DeviceStateManagerPostureProducer(@NonNull Context context) {
+ String[] deviceStatePosturePairs = context.getResources()
+ .getStringArray(R.array.config_device_state_postures);
+ for (String deviceStatePosturePair : deviceStatePosturePairs) {
+ String[] deviceStatePostureMapping = deviceStatePosturePair.split(":");
+ if (deviceStatePostureMapping.length != 2) {
+ if (DEBUG) {
+ Log.e(TAG, "Malformed device state posture pair: " + deviceStatePosturePair);
+ }
+ continue;
+ }
+
+ int deviceState;
+ int posture;
+ try {
+ deviceState = Integer.parseInt(deviceStatePostureMapping[0]);
+ posture = Integer.parseInt(deviceStatePostureMapping[1]);
+ } catch (NumberFormatException e) {
+ if (DEBUG) {
+ Log.e(TAG, "Failed to parse device state or posture: " + deviceStatePosturePair,
+ e);
+ }
+ continue;
+ }
+
+ mDeviceStateToPostureMap.put(deviceState, posture);
+ }
+
+ if (mDeviceStateToPostureMap.size() > 0) {
+ context.getSystemService(DeviceStateManager.class)
+ .registerCallback(context.getMainExecutor(), mDeviceStateCallback);
+ }
+ }
+
+ @Override
+ @Nullable
+ public Optional<Integer> getData() {
+ final int posture = mDeviceStateToPostureMap.get(mCurrentDeviceState, -1);
+ return posture != -1 ? Optional.of(posture) : Optional.empty();
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java
new file mode 100644
index 0000000..b6c4c43
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java
@@ -0,0 +1,36 @@
+/*
+ * 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.Nullable;
+import android.graphics.Rect;
+
+import androidx.annotation.NonNull;
+
+/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */
+public interface DisplayFeature {
+ /** Returns the type of the feature. */
+ int getType();
+
+ /** Returns the state of the feature, or {@code null} if the feature has no state. */
+ @Nullable
+ Integer getState();
+
+ /** Returns the bounds of the feature. */
+ @NonNull
+ Rect getRect();
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java
new file mode 100644
index 0000000..cd2cadc
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java
@@ -0,0 +1,74 @@
+/*
+ * 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.annotation.Nullable;
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.window.util.BaseDataProducer;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Implementation of {@link androidx.window.util.DataProducer} that produces
+ * {@link CommonDisplayFeature} parsed from a string stored in the resources config at
+ * {@link R.string#config_display_features}.
+ */
+public final class ResourceConfigDisplayFeatureProducer extends
+ BaseDataProducer<List<DisplayFeature>> {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "ResourceConfigDisplayFeatureProducer";
+
+ private final Context mContext;
+
+ public ResourceConfigDisplayFeatureProducer(@NonNull Context context) {
+ mContext = context;
+ }
+
+ @Override
+ @Nullable
+ public Optional<List<DisplayFeature>> getData() {
+ String displayFeaturesString = mContext.getResources().getString(
+ R.string.config_display_features);
+ if (TextUtils.isEmpty(displayFeaturesString)) {
+ return Optional.empty();
+ }
+
+ List<DisplayFeature> features = new ArrayList<>();
+ String[] featureStrings = displayFeaturesString.split(";");
+ for (String featureString : featureStrings) {
+ CommonDisplayFeature feature;
+ try {
+ feature = CommonDisplayFeature.parseFromString(featureString);
+ } catch (IllegalArgumentException e) {
+ if (DEBUG) {
+ Log.w(TAG, "Failed to parse display feature: " + featureString, e);
+ }
+ continue;
+ }
+ features.add(feature);
+ }
+ return Optional.of(features);
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDevicePostureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDevicePostureProducer.java
new file mode 100644
index 0000000..2026df3
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDevicePostureProducer.java
@@ -0,0 +1,96 @@
+/*
+ * 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.annotation.Nullable;
+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 androidx.window.util.BaseDataProducer;
+
+import java.util.Optional;
+
+/**
+ * Implementation of {@link androidx.window.util.DataProducer} that provides the device posture
+ * as an {@link Integer} from a value stored in {@link Settings}.
+ */
+public final class SettingsDevicePostureProducer extends BaseDataProducer<Integer> {
+ private static final String DEVICE_POSTURE = "device_posture";
+
+ private final Uri mDevicePostureUri =
+ Settings.Global.getUriFor(DEVICE_POSTURE);
+
+ private final ContentResolver mResolver;
+ private final ContentObserver mObserver;
+ private boolean mRegisteredObservers;
+
+ public SettingsDevicePostureProducer(@NonNull Context context) {
+ mResolver = context.getContentResolver();
+ mObserver = new SettingsObserver();
+ }
+
+ @Override
+ @Nullable
+ public Optional<Integer> getData() {
+ int posture = Settings.Global.getInt(mResolver, DEVICE_POSTURE, -1);
+ return posture == -1 ? Optional.empty() : Optional.of(posture);
+ }
+
+ /**
+ * 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(mDevicePostureUri, 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.
+ */
+ 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 (mDevicePostureUri.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
new file mode 100644
index 0000000..0406626
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java
@@ -0,0 +1,125 @@
+/*
+ * 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.annotation.Nullable;
+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 android.util.Log;
+
+import androidx.window.util.BaseDataProducer;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Implementation of {@link androidx.window.util.DataProducer} that produces
+ * {@link CommonDisplayFeature} parsed from a string stored in {@link Settings}.
+ */
+public final class SettingsDisplayFeatureProducer
+ extends BaseDataProducer<List<DisplayFeature>> {
+ private static final boolean DEBUG = false;
+ private static final String TAG = "SettingsDisplayFeatureProducer";
+ 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 boolean mRegisteredObservers;
+
+ public SettingsDisplayFeatureProducer(@NonNull Context context) {
+ mResolver = context.getContentResolver();
+ mObserver = new SettingsObserver();
+ }
+
+ @Override
+ @Nullable
+ public Optional<List<DisplayFeature>> getData() {
+ String displayFeaturesString = Settings.Global.getString(mResolver, DISPLAY_FEATURES);
+ if (displayFeaturesString == null) {
+ return Optional.empty();
+ }
+
+ List<DisplayFeature> features = new ArrayList<>();
+ if (TextUtils.isEmpty(displayFeaturesString)) {
+ return Optional.of(features);
+ }
+ String[] featureStrings = displayFeaturesString.split(";");
+
+ for (String featureString : featureStrings) {
+ CommonDisplayFeature feature;
+ try {
+ feature = CommonDisplayFeature.parseFromString(featureString);
+ } catch (IllegalArgumentException e) {
+ if (DEBUG) {
+ Log.w(TAG, "Failed to parse display feature: " + featureString, e);
+ }
+ continue;
+ }
+ features.add(feature);
+ }
+ return Optional.of(features);
+ }
+
+ /**
+ * 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 */);
+ }
+
+ /**
+ * 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)) {
+ notifyDataChanged();
+ }
+ }
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
index 5c91cf41..ce9be6a 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
@@ -27,11 +27,17 @@
import android.util.Log;
import androidx.annotation.NonNull;
-import androidx.window.util.BaseDisplayFeature;
-import androidx.window.util.SettingsConfigProvider;
+import androidx.window.common.DeviceStateManagerPostureProducer;
+import androidx.window.common.DisplayFeature;
+import androidx.window.common.ResourceConfigDisplayFeatureProducer;
+import androidx.window.common.SettingsDevicePostureProducer;
+import androidx.window.common.SettingsDisplayFeatureProducer;
+import androidx.window.util.DataProducer;
+import androidx.window.util.PriorityDataProducer;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
/**
* Reference implementation of androidx.window.extensions OEM interface for use with
@@ -41,23 +47,46 @@
* production builds since the interface can still change before reaching stable version.
* Please refer to {@link androidx.window.sidecar.SampleSidecarImpl} instead.
*/
-class SampleExtensionImpl extends StubExtension implements
- SettingsConfigProvider.StateChangeCallback {
+class SampleExtensionImpl extends StubExtension {
private static final String TAG = "SampleExtension";
- private final SettingsConfigProvider mConfigProvider;
+ private final SettingsDevicePostureProducer mSettingsDevicePostureProducer;
+ private final DataProducer<Integer> mDevicePostureProducer;
+
+ private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer;
+ private final DataProducer<List<DisplayFeature>> mDisplayFeatureProducer;
SampleExtensionImpl(Context context) {
- mConfigProvider = new SettingsConfigProvider(context, this);
+ mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context);
+ mDevicePostureProducer = new PriorityDataProducer<>(List.of(
+ mSettingsDevicePostureProducer,
+ new DeviceStateManagerPostureProducer(context)
+ ));
+
+ mSettingsDisplayFeatureProducer = new SettingsDisplayFeatureProducer(context);
+ mDisplayFeatureProducer = new PriorityDataProducer<>(List.of(
+ mSettingsDisplayFeatureProducer,
+ new ResourceConfigDisplayFeatureProducer(context)
+ ));
+
+ mDevicePostureProducer.addDataChangedCallback(this::onDevicePostureChanged);
+ mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
}
- @Override
- public void onDevicePostureChanged() {
- updateDeviceState(new ExtensionDeviceState(mConfigProvider.getDeviceState()));
+ private void onDevicePostureChanged() {
+ updateDeviceState(new ExtensionDeviceState(getDevicePosture()));
+
+ // Trigger a change in display features as the posture will be used in place of the feature
+ // state if the state is left unset by the producer.
+ onDisplayFeaturesChanged();
}
- @Override
- public void onDisplayFeaturesChanged() {
+ private int getDevicePosture() {
+ Optional<Integer> posture = mDevicePostureProducer.getData();
+ return posture.orElse(ExtensionDeviceState.POSTURE_UNKNOWN);
+ }
+
+ private void onDisplayFeaturesChanged() {
for (Activity activity : getActivitiesListeningForLayoutChanges()) {
ExtensionWindowLayoutInfo newLayout = getWindowLayoutInfo(activity);
updateWindowLayout(activity, newLayout);
@@ -84,13 +113,20 @@
return features;
}
- List<BaseDisplayFeature> storedFeatures = mConfigProvider.getDisplayFeatures();
- for (BaseDisplayFeature baseFeature : storedFeatures) {
- Rect featureRect = baseFeature.getRect();
- rotateRectToDisplayRotation(displayId, featureRect);
- transformToWindowSpaceRect(activity, featureRect);
- features.add(new ExtensionFoldingFeature(featureRect, baseFeature.getType(),
- baseFeature.getState()));
+ Optional<List<DisplayFeature>> storedFeatures = mDisplayFeatureProducer.getData();
+ if (storedFeatures.isPresent()) {
+ int posture = getDevicePosture();
+
+ for (DisplayFeature baseFeature : storedFeatures.get()) {
+ Rect featureRect = baseFeature.getRect();
+ rotateRectToDisplayRotation(displayId, featureRect);
+ transformToWindowSpaceRect(activity, featureRect);
+
+ Integer featureState = baseFeature.getState();
+
+ features.add(new ExtensionFoldingFeature(featureRect, baseFeature.getType(),
+ featureState == null ? posture : featureState));
+ }
}
return features;
}
@@ -98,9 +134,11 @@
@Override
protected void onListenersChanged() {
if (hasListeners()) {
- mConfigProvider.registerObserversIfNeeded();
+ mSettingsDevicePostureProducer.registerObserversIfNeeded();
+ mSettingsDisplayFeatureProducer.registerObserversIfNeeded();
} else {
- mConfigProvider.unregisterObserversIfNeeded();
+ mSettingsDevicePostureProducer.unregisterObserversIfNeeded();
+ mSettingsDisplayFeatureProducer.unregisterObserversIfNeeded();
}
onDevicePostureChanged();
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
index d3700f8..ece198c 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
@@ -29,33 +29,53 @@
import android.util.Log;
import androidx.annotation.NonNull;
-import androidx.window.util.BaseDisplayFeature;
-import androidx.window.util.SettingsConfigProvider;
+import androidx.window.common.DeviceStateManagerPostureProducer;
+import androidx.window.common.DisplayFeature;
+import androidx.window.common.ResourceConfigDisplayFeatureProducer;
+import androidx.window.common.SettingsDevicePostureProducer;
+import androidx.window.common.SettingsDisplayFeatureProducer;
+import androidx.window.util.DataProducer;
+import androidx.window.util.PriorityDataProducer;
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
/**
* Reference implementation of androidx.window.sidecar OEM interface for use with
* WindowManager Jetpack.
*/
-class SampleSidecarImpl extends StubSidecar implements
- SettingsConfigProvider.StateChangeCallback {
+class SampleSidecarImpl extends StubSidecar {
private static final String TAG = "SampleSidecar";
- private final SettingsConfigProvider mConfigProvider;
+ private final SettingsDevicePostureProducer mSettingsDevicePostureProducer;
+ private final DataProducer<Integer> mDevicePostureProducer;
+
+ private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer;
+ private final DataProducer<List<DisplayFeature>> mDisplayFeatureProducer;
SampleSidecarImpl(Context context) {
- mConfigProvider = new SettingsConfigProvider(context, this);
+ mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context);
+ mDevicePostureProducer = new PriorityDataProducer<>(List.of(
+ mSettingsDevicePostureProducer,
+ new DeviceStateManagerPostureProducer(context)
+ ));
+
+ mSettingsDisplayFeatureProducer = new SettingsDisplayFeatureProducer(context);
+ mDisplayFeatureProducer = new PriorityDataProducer<>(List.of(
+ mSettingsDisplayFeatureProducer,
+ new ResourceConfigDisplayFeatureProducer(context)
+ ));
+
+ mDevicePostureProducer.addDataChangedCallback(this::onDevicePostureChanged);
+ mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged);
}
- @Override
- public void onDevicePostureChanged() {
+ private void onDevicePostureChanged() {
updateDeviceState(getDeviceState());
}
- @Override
- public void onDisplayFeaturesChanged() {
+ private void onDisplayFeaturesChanged() {
for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
updateWindowLayout(windowToken, newLayout);
@@ -65,8 +85,10 @@
@NonNull
@Override
public SidecarDeviceState getDeviceState() {
+ Optional<Integer> posture = mDevicePostureProducer.getData();
+
SidecarDeviceState deviceState = new SidecarDeviceState();
- deviceState.posture = mConfigProvider.getDeviceState();
+ deviceState.posture = posture.orElse(SidecarDeviceState.POSTURE_UNKNOWN);
return deviceState;
}
@@ -96,15 +118,17 @@
return features;
}
- List<BaseDisplayFeature> storedFeatures = mConfigProvider.getDisplayFeatures();
- for (BaseDisplayFeature baseFeature : storedFeatures) {
- SidecarDisplayFeature feature = new SidecarDisplayFeature();
- Rect featureRect = baseFeature.getRect();
- rotateRectToDisplayRotation(displayId, featureRect);
- transformToWindowSpaceRect(activity, featureRect);
- feature.setRect(featureRect);
- feature.setType(baseFeature.getType());
- features.add(feature);
+ Optional<List<DisplayFeature>> storedFeatures = mDisplayFeatureProducer.getData();
+ if (storedFeatures.isPresent()) {
+ for (DisplayFeature baseFeature : storedFeatures.get()) {
+ SidecarDisplayFeature feature = new SidecarDisplayFeature();
+ Rect featureRect = baseFeature.getRect();
+ rotateRectToDisplayRotation(displayId, featureRect);
+ transformToWindowSpaceRect(activity, featureRect);
+ feature.setRect(featureRect);
+ feature.setType(baseFeature.getType());
+ features.add(feature);
+ }
}
return features;
}
@@ -112,9 +136,11 @@
@Override
protected void onListenersChanged() {
if (hasListeners()) {
- mConfigProvider.registerObserversIfNeeded();
+ mSettingsDevicePostureProducer.registerObserversIfNeeded();
+ mSettingsDisplayFeatureProducer.registerObserversIfNeeded();
} else {
- mConfigProvider.unregisterObserversIfNeeded();
+ mSettingsDevicePostureProducer.unregisterObserversIfNeeded();
+ mSettingsDisplayFeatureProducer.unregisterObserversIfNeeded();
}
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java
new file mode 100644
index 0000000..0a46703451
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDataProducer.java
@@ -0,0 +1,52 @@
+/*
+ * 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 androidx.annotation.NonNull;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+/**
+ * Base class that provides the implementation for the callback mechanism of the
+ * {@link DataProducer} API.
+ *
+ * @param <T> The type of data this producer returns through {@link #getData()}.
+ */
+public abstract class BaseDataProducer<T> implements DataProducer<T> {
+ private final Set<Runnable> mCallbacks = new LinkedHashSet<>();
+
+ @Override
+ public final void addDataChangedCallback(@NonNull Runnable callback) {
+ mCallbacks.add(callback);
+ }
+
+ @Override
+ public final void removeDataChangedCallback(@NonNull Runnable callback) {
+ mCallbacks.remove(callback);
+ }
+
+ /**
+ * Called to notify all registered callbacks that the data provided by {@link #getData()} has
+ * changed.
+ */
+ protected void notifyDataChanged() {
+ for (Runnable callback : mCallbacks) {
+ callback.run();
+ }
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java
deleted file mode 100644
index b74a2a4..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java
+++ /dev/null
@@ -1,52 +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.graphics.Rect;
-
-import androidx.annotation.NonNull;
-
-/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */
-public class BaseDisplayFeature {
- private final int mType;
- private final int mState;
- @NonNull
- public final Rect mRect;
-
- public BaseDisplayFeature(int type, int state, @NonNull Rect rect) {
- this.mType = type;
- this.mState = state;
- if (rect.width() == 0 && rect.height() == 0) {
- throw new IllegalArgumentException(
- "Display feature rectangle cannot have zero width and height simultaneously.");
- }
- this.mRect = rect;
- }
-
- public int getType() {
- return mType;
- }
-
- public int getState() {
- return mState;
- }
-
- @NonNull
- public Rect getRect() {
- return mRect;
- }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/DataProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/DataProducer.java
new file mode 100644
index 0000000..d4d1a23
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/DataProducer.java
@@ -0,0 +1,43 @@
+/*
+ * 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.NonNull;
+
+import java.util.Optional;
+
+/**
+ * Produces data through {@link #getData()} and provides a mechanism for receiving a callback when
+ * the data managed by the produces has changed.
+ *
+ * @param <T> The type of data this producer returns through {@link #getData()}.
+ */
+public interface DataProducer<T> {
+ /**
+ * Returns the data currently stored in the provider, or {@link Optional#empty()} if the
+ * provider has no data.
+ */
+ Optional<T> getData();
+
+ /**
+ * Adds a callback to be notified when the data returned from {@link #getData()} has changed.
+ */
+ void addDataChangedCallback(@NonNull Runnable callback);
+
+ /** Removes a callback previously added with {@link #addDataChangedCallback(Runnable)}. */
+ void removeDataChangedCallback(@NonNull Runnable callback);
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/PriorityDataProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/util/PriorityDataProducer.java
new file mode 100644
index 0000000..990ae20
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/PriorityDataProducer.java
@@ -0,0 +1,56 @@
+/*
+ * 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();
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java
deleted file mode 100644
index 6dd190c..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java
+++ /dev/null
@@ -1,196 +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 static androidx.window.util.ExtensionHelper.isZero;
-
-import android.content.ContentResolver;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.graphics.Rect;
-import android.net.Uri;
-import android.os.Handler;
-import android.os.Looper;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Device and display feature state provider that uses Settings as the source.
- */
-public final class SettingsConfigProvider extends ContentObserver {
- private static final String TAG = "SettingsConfigProvider";
- private static final String DEVICE_POSTURE = "device_posture";
- private static final String DISPLAY_FEATURES = "display_features";
-
- private static final Pattern FEATURE_PATTERN =
- Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]");
-
- private static final String FEATURE_TYPE_FOLD = "fold";
- private static final String FEATURE_TYPE_HINGE = "hinge";
-
- private final Uri mDevicePostureUri =
- Settings.Global.getUriFor(DEVICE_POSTURE);
- private final Uri mDisplayFeaturesUri =
- Settings.Global.getUriFor(DISPLAY_FEATURES);
- private final Context mContext;
- private final ContentResolver mResolver;
- private final StateChangeCallback mCallback;
- private boolean mRegisteredObservers;
-
- public SettingsConfigProvider(@NonNull Context context, @NonNull StateChangeCallback callback) {
- super(new Handler(Looper.getMainLooper()));
- mContext = context;
- mResolver = context.getContentResolver();
- mCallback = callback;
- }
-
- /**
- * Registers the content observers for Settings keys that store device state and display feature
- * configurations.
- */
- public void registerObserversIfNeeded() {
- if (mRegisteredObservers) {
- return;
- }
- mRegisteredObservers = true;
- mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendants */,
- this /* ContentObserver */);
- mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendants */,
- this /* ContentObserver */);
- }
-
- /**
- * Unregisters the content observers that are tracking the state changes.
- * @see #registerObserversIfNeeded()
- */
- public void unregisterObserversIfNeeded() {
- if (!mRegisteredObservers) {
- return;
- }
- mRegisteredObservers = false;
- mResolver.unregisterContentObserver(this);
- }
-
- /**
- * Gets the device posture int stored in Settings.
- */
- public int getDeviceState() {
- return Settings.Global.getInt(mResolver, DEVICE_POSTURE,
- 0 /* POSTURE_UNKNOWN */);
- }
-
- /**
- * Gets the list of all display feature configs stored in Settings. Uses a custom
- * {@link BaseDisplayFeature} class to report the config to be translated for actual
- * containers in Sidecar or Extensions.
- */
- public List<BaseDisplayFeature> getDisplayFeatures() {
- List<BaseDisplayFeature> features = new ArrayList<>();
- String displayFeaturesString = Settings.Global.getString(mResolver, DISPLAY_FEATURES);
- if (TextUtils.isEmpty(displayFeaturesString)) {
- displayFeaturesString = mContext.getResources().getString(
- R.string.config_display_features);
- }
- if (TextUtils.isEmpty(displayFeaturesString)) {
- return features;
- }
- String[] featureStrings = displayFeaturesString.split(";");
-
- int deviceState = getDeviceState();
-
- for (String featureString : featureStrings) {
- Matcher featureMatcher = FEATURE_PATTERN.matcher(featureString);
- if (!featureMatcher.matches()) {
- Log.e(TAG, "Malformed feature description format: " + featureString);
- continue;
- }
- try {
- String featureType = featureMatcher.group(1);
- int type;
- switch (featureType) {
- case FEATURE_TYPE_FOLD:
- type = 1 /* TYPE_FOLD */;
- break;
- case FEATURE_TYPE_HINGE:
- type = 2 /* TYPE_HINGE */;
- break;
- default: {
- Log.e(TAG, "Malformed feature type: " + featureType);
- continue;
- }
- }
-
- int left = Integer.parseInt(featureMatcher.group(2));
- int top = Integer.parseInt(featureMatcher.group(3));
- int right = Integer.parseInt(featureMatcher.group(4));
- int bottom = Integer.parseInt(featureMatcher.group(5));
- Rect featureRect = new Rect(left, top, right, bottom);
- if (!isZero(featureRect)) {
- BaseDisplayFeature feature = new BaseDisplayFeature(type, deviceState,
- featureRect);
- features.add(feature);
- } else {
- Log.w(TAG, "Read empty feature");
- }
- } catch (NumberFormatException e) {
- Log.e(TAG, "Malformed feature description: " + featureString);
- }
- }
- return features;
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- if (uri == null) {
- return;
- }
-
- if (mDevicePostureUri.equals(uri)) {
- mCallback.onDevicePostureChanged();
- mCallback.onDisplayFeaturesChanged();
- return;
- }
- if (mDisplayFeaturesUri.equals(uri)) {
- mCallback.onDisplayFeaturesChanged();
- }
- }
-
- /**
- * Callback that notifies about device or display feature state changes.
- */
- public interface StateChangeCallback {
- /**
- * Notifies about the device state update.
- */
- void onDevicePostureChanged();
-
- /**
- * Notifies about the display feature config update.
- */
- void onDisplayFeaturesChanged();
- }
-}