Merge "Add sample WM Jetpack Extensions impl" into sc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
90f1b95ccf
@@ -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 @@ android_library_import {
|
||||
|
||||
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 @@ prebuilt_etc {
|
||||
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,
|
||||
}
|
||||
|
||||
21
libs/WindowManager/Jetpack/androidx.window.extensions.xml
Normal file
21
libs/WindowManager/Jetpack/androidx.window.extensions.xml
Normal file
@@ -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>
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ public class SidecarProvider {
|
||||
* an OEM by overriding this method.
|
||||
*/
|
||||
public static SidecarInterface getSidecarImpl(Context context) {
|
||||
return new SettingsSidecarImpl(context);
|
||||
return new SampleSidecarImpl(context);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 @@ class SidecarHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 @@ class SidecarHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/** 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 @@ class SidecarHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
BIN
libs/WindowManager/Jetpack/window-extensions-release.aar
Normal file
BIN
libs/WindowManager/Jetpack/window-extensions-release.aar
Normal file
Binary file not shown.
Reference in New Issue
Block a user