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.
This commit is contained in:
committed by
Android (Google) Code Review
commit
30cefedcdb
@@ -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
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -30,13 +30,20 @@ android_library_import {
|
||||
|
||||
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 @@ android_library_import {
|
||||
|
||||
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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -14,39 +14,23 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package androidx.window.util;
|
||||
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 class BaseDisplayFeature {
|
||||
private final int mType;
|
||||
private final int mState;
|
||||
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
|
||||
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;
|
||||
}
|
||||
Rect getRect();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,11 +27,17 @@ import android.graphics.Rect;
|
||||
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 @@ import java.util.List;
|
||||
* 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 @@ class SampleExtensionImpl extends StubExtension implements
|
||||
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 @@ class SampleExtensionImpl extends StubExtension implements
|
||||
@Override
|
||||
protected void onListenersChanged() {
|
||||
if (hasListeners()) {
|
||||
mConfigProvider.registerObserversIfNeeded();
|
||||
mSettingsDevicePostureProducer.registerObserversIfNeeded();
|
||||
mSettingsDisplayFeatureProducer.registerObserversIfNeeded();
|
||||
} else {
|
||||
mConfigProvider.unregisterObserversIfNeeded();
|
||||
mSettingsDevicePostureProducer.unregisterObserversIfNeeded();
|
||||
mSettingsDisplayFeatureProducer.unregisterObserversIfNeeded();
|
||||
}
|
||||
|
||||
onDevicePostureChanged();
|
||||
|
||||
@@ -29,33 +29,53 @@ import android.os.IBinder;
|
||||
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 @@ class SampleSidecarImpl extends StubSidecar implements
|
||||
@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 @@ class SampleSidecarImpl extends StubSidecar implements
|
||||
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 @@ class SampleSidecarImpl extends StubSidecar implements
|
||||
@Override
|
||||
protected void onListenersChanged() {
|
||||
if (hasListeners()) {
|
||||
mConfigProvider.registerObserversIfNeeded();
|
||||
mSettingsDevicePostureProducer.registerObserversIfNeeded();
|
||||
mSettingsDisplayFeatureProducer.registerObserversIfNeeded();
|
||||
} else {
|
||||
mConfigProvider.unregisterObserversIfNeeded();
|
||||
mSettingsDevicePostureProducer.unregisterObserversIfNeeded();
|
||||
mSettingsDisplayFeatureProducer.unregisterObserversIfNeeded();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user