Add sample WM Jetpack Extensions impl
Moving some of the common bits of Sidecar and Extensions
into helper classes and adding both to the reference repo.
Bug: 154172225
Test: m androidx.window.extensions
Test: Manual, by including extensions in the emulator image
Test: gradlew window:window:cAT
Change-Id: I44ff06b06b2202a5d687216e017528098dc14d1d
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp
index 7fbbb61..4612ba2 100644
--- a/libs/WindowManager/Jetpack/Android.bp
+++ b/libs/WindowManager/Jetpack/Android.bp
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// Sidecar
android_library_import {
name: "window-sidecar",
aars: ["window-sidecar-release.aar"],
@@ -20,7 +21,7 @@
java_library {
name: "androidx.window.sidecar",
- srcs: ["src/**/*.java"],
+ srcs: ["src/androidx/window/sidecar/**/*.java", "src/androidx/window/util/**/*.java"],
static_libs: ["window-sidecar"],
installable: true,
sdk_version: "core_platform",
@@ -36,3 +37,31 @@
src: "androidx.window.sidecar.xml",
filename_from_src: true,
}
+
+// Extensions
+// NOTE: This module is still under active development and must not
+// be used in production. Use 'androidx.window.sidecar' instead.
+android_library_import {
+ name: "window-extensions",
+ aars: ["window-extensions-release.aar"],
+ sdk_version: "current",
+}
+
+java_library {
+ name: "androidx.window.extensions",
+ srcs: ["src/androidx/window/extensions/**/*.java", "src/androidx/window/util/**/*.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",],
+}
+
+prebuilt_etc {
+ name: "androidx.window.extensions.xml",
+ system_ext_specific: true,
+ sub_dir: "permissions",
+ src: "androidx.window.extensions.xml",
+ filename_from_src: true,
+}
diff --git a/libs/WindowManager/Jetpack/androidx.window.extensions.xml b/libs/WindowManager/Jetpack/androidx.window.extensions.xml
new file mode 100644
index 0000000..34264aa
--- /dev/null
+++ b/libs/WindowManager/Jetpack/androidx.window.extensions.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<permissions>
+ <library
+ name="androidx.window.extensions"
+ file="/system_ext/framework/androidx.window.extensions.jar"/>
+</permissions>
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
new file mode 100644
index 0000000..b7a6039
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java
@@ -0,0 +1,41 @@
+/*
+ * 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.extensions;
+
+import android.content.Context;
+
+/**
+ * Provider class that will instantiate the library implementation. It must be included in the
+ * vendor library, and the vendor implementation must match the signature of this class.
+ */
+public class ExtensionProvider {
+ /**
+ * Provides a simple implementation of {@link ExtensionInterface} that can be replaced by
+ * an OEM by overriding this method.
+ */
+ public static ExtensionInterface getExtensionImpl(Context context) {
+ return new SampleExtensionImpl(context);
+ }
+
+ /**
+ * The support library will use this method to check API version compatibility.
+ * @return API version string in MAJOR.MINOR.PATCH-description format.
+ */
+ public static String getApiVersion() {
+ return "1.0.0-settings_sample";
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
new file mode 100644
index 0000000..0bf6965
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SampleExtensionImpl.java
@@ -0,0 +1,109 @@
+/*
+ * 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.extensions;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
+import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Rect;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.window.util.BaseDisplayFeature;
+import androidx.window.util.SettingsConfigProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reference implementation of androidx.window.extensions OEM interface for use with
+ * WindowManager Jetpack.
+ *
+ * NOTE: This version is a work in progress and under active development. It MUST NOT be used in
+ * 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 {
+ private static final String TAG = "SampleExtension";
+
+ private final SettingsConfigProvider mConfigProvider;
+
+ SampleExtensionImpl(Context context) {
+ mConfigProvider = new SettingsConfigProvider(context, this);
+ }
+
+ @Override
+ public void onDevicePostureChanged() {
+ updateDeviceState(new ExtensionDeviceState(mConfigProvider.getDeviceState()));
+ }
+
+ @Override
+ public void onDisplayFeaturesChanged() {
+ for (Activity activity : getActivitiesListeningForLayoutChanges()) {
+ ExtensionWindowLayoutInfo newLayout = getWindowLayoutInfo(activity);
+ updateWindowLayout(activity, newLayout);
+ }
+ }
+
+ @NonNull
+ private ExtensionWindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) {
+ List<ExtensionDisplayFeature> displayFeatures = getDisplayFeatures(activity);
+ return new ExtensionWindowLayoutInfo(displayFeatures);
+ }
+
+ private List<ExtensionDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
+ List<ExtensionDisplayFeature> features = new ArrayList<>();
+ int displayId = activity.getDisplayId();
+ if (displayId != DEFAULT_DISPLAY) {
+ Log.w(TAG, "This sample doesn't support display features on secondary displays");
+ return features;
+ }
+
+ if (activity.isInMultiWindowMode()) {
+ // It is recommended not to report any display features in multi-window mode, since it
+ // won't be possible to synchronize the display feature positions with window movement.
+ 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()));
+ }
+ return features;
+ }
+
+ @Override
+ protected void onListenersChanged() {
+ if (hasListeners()) {
+ mConfigProvider.registerObserversIfNeeded();
+ } else {
+ mConfigProvider.unregisterObserversIfNeeded();
+ }
+
+ onDevicePostureChanged();
+ onDisplayFeaturesChanged();
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java
new file mode 100644
index 0000000..b0895ef
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/StubExtension.java
@@ -0,0 +1,86 @@
+/*
+ * 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.extensions;
+
+import android.app.Activity;
+
+import androidx.annotation.NonNull;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Basic implementation of the {@link ExtensionInterface}. An OEM can choose to use it as the base
+ * class for their implementation.
+ */
+abstract class StubExtension implements ExtensionInterface {
+
+ private ExtensionCallback mExtensionCallback;
+ private final Set<Activity> mWindowLayoutChangeListenerActivities = new HashSet<>();
+ private boolean mDeviceStateChangeListenerRegistered;
+
+ StubExtension() {
+ }
+
+ @Override
+ public void setExtensionCallback(@NonNull ExtensionCallback extensionCallback) {
+ this.mExtensionCallback = extensionCallback;
+ }
+
+ @Override
+ public void onWindowLayoutChangeListenerAdded(@NonNull Activity activity) {
+ this.mWindowLayoutChangeListenerActivities.add(activity);
+ this.onListenersChanged();
+ }
+
+ @Override
+ public void onWindowLayoutChangeListenerRemoved(@NonNull Activity activity) {
+ this.mWindowLayoutChangeListenerActivities.remove(activity);
+ this.onListenersChanged();
+ }
+
+ @Override
+ public void onDeviceStateListenersChanged(boolean isEmpty) {
+ this.mDeviceStateChangeListenerRegistered = !isEmpty;
+ this.onListenersChanged();
+ }
+
+ void updateDeviceState(ExtensionDeviceState newState) {
+ if (this.mExtensionCallback != null) {
+ mExtensionCallback.onDeviceStateChanged(newState);
+ }
+ }
+
+ void updateWindowLayout(@NonNull Activity activity,
+ @NonNull ExtensionWindowLayoutInfo newLayout) {
+ if (this.mExtensionCallback != null) {
+ mExtensionCallback.onWindowLayoutChanged(activity, newLayout);
+ }
+ }
+
+ @NonNull
+ Set<Activity> getActivitiesListeningForLayoutChanges() {
+ return mWindowLayoutChangeListenerActivities;
+ }
+
+ protected boolean hasListeners() {
+ return !mWindowLayoutChangeListenerActivities.isEmpty()
+ || mDeviceStateChangeListenerRegistered;
+ }
+
+ protected abstract void onListenersChanged();
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
new file mode 100644
index 0000000..1094a0e
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2020 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.sidecar;
+
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation;
+import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect;
+
+import android.app.Activity;
+import android.app.ActivityThread;
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.window.util.BaseDisplayFeature;
+import androidx.window.util.SettingsConfigProvider;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Reference implementation of androidx.window.sidecar OEM interface for use with
+ * WindowManager Jetpack.
+ */
+class SampleSidecarImpl extends StubSidecar implements
+ SettingsConfigProvider.StateChangeCallback {
+ private static final String TAG = "SampleSidecar";
+
+ private final SettingsConfigProvider mConfigProvider;
+
+ SampleSidecarImpl(Context context) {
+ mConfigProvider = new SettingsConfigProvider(context, this);
+ }
+
+ @Override
+ public void onDevicePostureChanged() {
+ updateDeviceState(getDeviceState());
+ }
+
+ @Override
+ public void onDisplayFeaturesChanged() {
+ for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
+ SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
+ updateWindowLayout(windowToken, newLayout);
+ }
+ }
+
+ @NonNull
+ @Override
+ public SidecarDeviceState getDeviceState() {
+ SidecarDeviceState deviceState = new SidecarDeviceState();
+ deviceState.posture = mConfigProvider.getDeviceState();
+ return deviceState;
+ }
+
+ @NonNull
+ @Override
+ public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
+ Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
+ SidecarWindowLayoutInfo windowLayoutInfo = new SidecarWindowLayoutInfo();
+ if (activity == null) {
+ return windowLayoutInfo;
+ }
+ windowLayoutInfo.displayFeatures = getDisplayFeatures(activity);
+ return windowLayoutInfo;
+ }
+
+ private List<SidecarDisplayFeature> getDisplayFeatures(@NonNull Activity activity) {
+ List<SidecarDisplayFeature> features = new ArrayList<SidecarDisplayFeature>();
+ int displayId = activity.getDisplayId();
+ if (displayId != DEFAULT_DISPLAY) {
+ Log.w(TAG, "This sample doesn't support display features on secondary displays");
+ return features;
+ }
+
+ if (activity.isInMultiWindowMode()) {
+ // It is recommended not to report any display features in multi-window mode, since it
+ // won't be possible to synchronize the display feature positions with window movement.
+ 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);
+ }
+ return features;
+ }
+
+ @Override
+ protected void onListenersChanged() {
+ if (hasListeners()) {
+ mConfigProvider.registerObserversIfNeeded();
+ } else {
+ mConfigProvider.unregisterObserversIfNeeded();
+ }
+ }
+}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
deleted file mode 100644
index 5397302..0000000
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SettingsSidecarImpl.java
+++ /dev/null
@@ -1,233 +0,0 @@
-/*
- * Copyright (C) 2020 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.sidecar;
-
-import static android.view.Display.DEFAULT_DISPLAY;
-
-import static androidx.window.sidecar.SidecarHelper.getWindowDisplay;
-import static androidx.window.sidecar.SidecarHelper.isInMultiWindow;
-import static androidx.window.sidecar.SidecarHelper.rotateRectToDisplayRotation;
-import static androidx.window.sidecar.SidecarHelper.transformToWindowSpaceRect;
-
-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.IBinder;
-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;
-
-class SettingsSidecarImpl extends StubSidecar {
- private static final String TAG = "SettingsSidecar";
-
- 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 Context mContext;
- private SettingsObserver mSettingsObserver;
-
- final class SettingsObserver extends ContentObserver {
- private final Uri mDevicePostureUri =
- Settings.Global.getUriFor(DEVICE_POSTURE);
- private final Uri mDisplayFeaturesUri =
- Settings.Global.getUriFor(DISPLAY_FEATURES);
- private final ContentResolver mResolver = mContext.getContentResolver();
- private boolean mRegisteredObservers;
-
-
- private SettingsObserver() {
- super(new Handler(Looper.getMainLooper()));
- }
-
- private void registerObserversIfNeeded() {
- if (mRegisteredObservers) {
- return;
- }
- mRegisteredObservers = true;
- mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendents */,
- this /* ContentObserver */);
- mResolver.registerContentObserver(mDisplayFeaturesUri, false /* notifyForDescendents */,
- this /* ContentObserver */);
- }
-
- private void unregisterObserversIfNeeded() {
- if (!mRegisteredObservers) {
- return;
- }
- mRegisteredObservers = false;
- mResolver.unregisterContentObserver(this);
- }
-
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- if (uri == null) {
- return;
- }
-
- if (mDevicePostureUri.equals(uri)) {
- updateDevicePosture();
- return;
- }
- if (mDisplayFeaturesUri.equals(uri)) {
- updateDisplayFeatures();
- return;
- }
- }
- }
-
- SettingsSidecarImpl(Context context) {
- mContext = context;
- mSettingsObserver = new SettingsObserver();
- }
-
- private void updateDevicePosture() {
- updateDeviceState(getDeviceState());
- }
-
- /** Update display features with values read from settings. */
- private void updateDisplayFeatures() {
- for (IBinder windowToken : getWindowsListeningForLayoutChanges()) {
- SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
- updateWindowLayout(windowToken, newLayout);
- }
- }
-
- @NonNull
- @Override
- public SidecarDeviceState getDeviceState() {
- ContentResolver resolver = mContext.getContentResolver();
- int posture = Settings.Global.getInt(resolver, DEVICE_POSTURE,
- SidecarDeviceState.POSTURE_UNKNOWN);
- SidecarDeviceState deviceState = new SidecarDeviceState();
- deviceState.posture = posture;
- return deviceState;
- }
-
- @NonNull
- @Override
- public SidecarWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
- List<SidecarDisplayFeature> displayFeatures = readDisplayFeatures(windowToken);
- SidecarWindowLayoutInfo windowLayoutInfo = new SidecarWindowLayoutInfo();
- windowLayoutInfo.displayFeatures = displayFeatures;
- return windowLayoutInfo;
- }
-
- private List<SidecarDisplayFeature> readDisplayFeatures(IBinder windowToken) {
- List<SidecarDisplayFeature> features = new ArrayList<SidecarDisplayFeature>();
- int displayId = getWindowDisplay(windowToken);
- if (displayId != DEFAULT_DISPLAY) {
- Log.w(TAG, "This sample doesn't support display features on secondary displays");
- return features;
- }
-
- if (isInMultiWindow(windowToken)) {
- // It is recommended not to report any display features in multi-window mode, since it
- // won't be possible to synchronize the display feature positions with window movement.
- return features;
- }
-
- ContentResolver resolver = mContext.getContentResolver();
- String displayFeaturesString = Settings.Global.getString(resolver, 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(";");
- 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 = SidecarDisplayFeature.TYPE_FOLD;
- break;
- case FEATURE_TYPE_HINGE:
- type = SidecarDisplayFeature.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);
- rotateRectToDisplayRotation(featureRect, displayId);
- transformToWindowSpaceRect(featureRect, windowToken);
- if (isNotZero(featureRect)) {
- SidecarDisplayFeature feature = new SidecarDisplayFeature();
- feature.setRect(featureRect);
- feature.setType(type);
- features.add(feature);
- } else {
- Log.w(TAG, "Failed to adjust feature to window");
- }
- } catch (NumberFormatException e) {
- Log.e(TAG, "Malformed feature description: " + featureString);
- }
- }
- return features;
- }
-
- private static boolean isNotZero(Rect rect) {
- return rect.height() > 0 || rect.width() > 0;
- }
-
- @Override
- protected void onListenersChanged() {
- if (mSettingsObserver == null) {
- return;
- }
-
- if (hasListeners()) {
- mSettingsObserver.registerObserversIfNeeded();
- } else {
- mSettingsObserver.unregisterObserversIfNeeded();
- }
- }
-}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
index 0b4915ed..e6f8388 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarProvider.java
@@ -28,7 +28,7 @@
* an OEM by overriding this method.
*/
public static SidecarInterface getSidecarImpl(Context context) {
- return new SettingsSidecarImpl(context);
+ return new SampleSidecarImpl(context);
}
/**
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.java
new file mode 100644
index 0000000..b74a2a4
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/BaseDisplayFeature.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 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/sidecar/SidecarHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
similarity index 62%
rename from libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
rename to libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
index e5b6cff..2a593f1 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SidecarHelper.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/ExtensionHelper.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -14,30 +14,36 @@
* limitations under the License.
*/
-package androidx.window.sidecar;
+package androidx.window.util;
-import static android.view.Display.INVALID_DISPLAY;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import android.app.Activity;
-import android.app.ActivityThread;
import android.graphics.Rect;
import android.hardware.display.DisplayManagerGlobal;
-import android.os.IBinder;
import android.view.DisplayInfo;
import android.view.Surface;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-class SidecarHelper {
+/**
+ * Util class for both Sidecar and Extensions.
+ */
+public final class ExtensionHelper {
+
+ private ExtensionHelper() {
+ // Util class, no instances should be created.
+ }
+
/**
- * Rotate the input rectangle specified in default display orientation to the current display
+ * Rotates the input rectangle specified in default display orientation to the current display
* rotation.
*/
- static void rotateRectToDisplayRotation(Rect inOutRect, int displayId) {
+ public static void rotateRectToDisplayRotation(int displayId, Rect inOutRect) {
DisplayManagerGlobal dmGlobal = DisplayManagerGlobal.getInstance();
DisplayInfo displayInfo = dmGlobal.getDisplayInfo(displayId);
int rotation = displayInfo.rotation;
@@ -52,7 +58,7 @@
}
/**
- * Rotate the input rectangle within parent bounds for a given delta.
+ * Rotates the input rectangle within parent bounds for a given delta.
*/
private static void rotateBounds(Rect inOutRect, int parentWidth, int parentHeight,
@Surface.Rotation int delta) {
@@ -79,9 +85,9 @@
}
}
- /** Transform rectangle from absolute coordinate space to the window coordinate space. */
- static void transformToWindowSpaceRect(Rect inOutRect, IBinder windowToken) {
- Rect windowRect = getWindowBounds(windowToken);
+ /** Transforms rectangle from absolute coordinate space to the window coordinate space. */
+ public static void transformToWindowSpaceRect(Activity activity, Rect inOutRect) {
+ Rect windowRect = getWindowBounds(activity);
if (windowRect == null) {
inOutRect.setEmpty();
return;
@@ -95,32 +101,17 @@
}
/**
- * Get the current window bounds in absolute coordinates.
- * NOTE: Only works with Activity windows.
+ * Gets the current window bounds in absolute coordinates.
*/
@Nullable
- private static Rect getWindowBounds(IBinder windowToken) {
- Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
- return activity != null
- ? activity.getWindowManager().getCurrentWindowMetrics().getBounds()
- : null;
+ private static Rect getWindowBounds(@NonNull Activity activity) {
+ return activity.getWindowManager().getCurrentWindowMetrics().getBounds();
}
/**
- * Check if this window is an Activity window that is in multi-window mode.
+ * Checks if both dimensions of the given rect are zero at the same time.
*/
- static boolean isInMultiWindow(IBinder windowToken) {
- Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
- return activity != null && activity.isInMultiWindowMode();
- }
-
- /**
- * Get the id of the parent display for the window.
- * NOTE: Only works with Activity windows.
- */
- static int getWindowDisplay(IBinder windowToken) {
- Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
- return activity != null
- ? activity.getWindowManager().getDefaultDisplay().getDisplayId() : INVALID_DISPLAY;
+ public static boolean isZero(@NonNull Rect rect) {
+ return rect.height() == 0 && rect.width() == 0;
}
}
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java b/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java
new file mode 100644
index 0000000..6dd190c
--- /dev/null
+++ b/libs/WindowManager/Jetpack/src/androidx/window/util/SettingsConfigProvider.java
@@ -0,0 +1,196 @@
+/*
+ * 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();
+ }
+}
diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar
new file mode 100644
index 0000000..7b306b0
--- /dev/null
+++ b/libs/WindowManager/Jetpack/window-extensions-release.aar
Binary files differ