Merge "DO NOT MERGE Test sidecar OEM implementation for WM support library" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
24cfad9c2c
38
libs/WindowManager/Jetpack/Android.bp
Normal file
38
libs/WindowManager/Jetpack/Android.bp
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
android_library_import {
|
||||||
|
name: "window-extensions",
|
||||||
|
aars: ["window-extensions-release.aar"],
|
||||||
|
sdk_version: "current",
|
||||||
|
}
|
||||||
|
|
||||||
|
java_library {
|
||||||
|
name: "androidx.window.extensions",
|
||||||
|
srcs: ["src/**/*.java"],
|
||||||
|
static_libs: ["window-extensions"],
|
||||||
|
installable: true,
|
||||||
|
sdk_version: "core_platform",
|
||||||
|
vendor: true,
|
||||||
|
libs: ["framework", "androidx.annotation_annotation",],
|
||||||
|
required: ["androidx.window.extensions.xml",],
|
||||||
|
}
|
||||||
|
|
||||||
|
prebuilt_etc {
|
||||||
|
name: "androidx.window.extensions.xml",
|
||||||
|
vendor: 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 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.
|
||||||
|
-->
|
||||||
|
<permissions>
|
||||||
|
<library
|
||||||
|
name="androidx.window.extensions"
|
||||||
|
file="/vendor/framework/androidx.window.extensions.jar"/>
|
||||||
|
</permissions>
|
||||||
@@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 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.extensions;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toolkit class for calculation of the display feature bounds within the window.
|
||||||
|
* NOTE: This sample implementation only works for Activity windows, because there is no public APIs
|
||||||
|
* to obtain layout params or bounds for arbitrary windows.
|
||||||
|
*/
|
||||||
|
class ExtensionHelper {
|
||||||
|
/**
|
||||||
|
* Rotate the input rectangle specified in default display orientation to the current display
|
||||||
|
* rotation.
|
||||||
|
*/
|
||||||
|
static void rotateRectToDisplayRotation(Rect inOutRect, int displayId) {
|
||||||
|
DisplayManagerGlobal dmGlobal = DisplayManagerGlobal.getInstance();
|
||||||
|
DisplayInfo displayInfo = dmGlobal.getDisplayInfo(displayId);
|
||||||
|
int rotation = displayInfo.rotation;
|
||||||
|
|
||||||
|
boolean isSideRotation = rotation == ROTATION_90 || rotation == ROTATION_270;
|
||||||
|
int displayWidth = isSideRotation ? displayInfo.logicalHeight : displayInfo.logicalWidth;
|
||||||
|
int displayHeight = isSideRotation ? displayInfo.logicalWidth : displayInfo.logicalHeight;
|
||||||
|
|
||||||
|
inOutRect.intersect(0, 0, displayWidth, displayHeight);
|
||||||
|
|
||||||
|
rotateBounds(inOutRect, displayWidth, displayHeight, rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotate the input rectangle within parent bounds for a given delta.
|
||||||
|
*/
|
||||||
|
private static void rotateBounds(Rect inOutRect, int parentWidth, int parentHeight,
|
||||||
|
@Surface.Rotation int delta) {
|
||||||
|
int origLeft = inOutRect.left;
|
||||||
|
switch (delta) {
|
||||||
|
case ROTATION_0:
|
||||||
|
return;
|
||||||
|
case ROTATION_90:
|
||||||
|
inOutRect.left = inOutRect.top;
|
||||||
|
inOutRect.top = parentWidth - inOutRect.right;
|
||||||
|
inOutRect.right = inOutRect.bottom;
|
||||||
|
inOutRect.bottom = parentWidth - origLeft;
|
||||||
|
return;
|
||||||
|
case ROTATION_180:
|
||||||
|
inOutRect.left = parentWidth - inOutRect.right;
|
||||||
|
inOutRect.right = parentWidth - origLeft;
|
||||||
|
return;
|
||||||
|
case ROTATION_270:
|
||||||
|
inOutRect.left = parentHeight - inOutRect.bottom;
|
||||||
|
inOutRect.bottom = inOutRect.right;
|
||||||
|
inOutRect.right = parentHeight - inOutRect.top;
|
||||||
|
inOutRect.top = origLeft;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Transform rectangle from absolute coordinate space to the window coordinate space. */
|
||||||
|
static void transformToWindowSpaceRect(Rect inOutRect, IBinder windowToken) {
|
||||||
|
Rect windowRect = getWindowRect(windowToken);
|
||||||
|
if (windowRect == null) {
|
||||||
|
inOutRect.setEmpty();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!Rect.intersects(inOutRect, windowRect)) {
|
||||||
|
inOutRect.setEmpty();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
inOutRect.intersect(windowRect);
|
||||||
|
inOutRect.offset(-windowRect.left, -windowRect.top);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current window bounds in absolute coordinates.
|
||||||
|
* NOTE: Only works with Activity windows.
|
||||||
|
*/
|
||||||
|
private static Rect getWindowRect(IBinder windowToken) {
|
||||||
|
Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
|
||||||
|
final Rect windowRect = new Rect();
|
||||||
|
if (activity != null) {
|
||||||
|
activity.getWindow().getDecorView().getWindowDisplayFrame(windowRect);
|
||||||
|
}
|
||||||
|
return windowRect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this window is an Activity window that is in multi-window mode.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 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.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 {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The support library will instantiate the vendor implementation using this interface.
|
||||||
|
* @return An implementation of {@link ExtensionInterface}.
|
||||||
|
*/
|
||||||
|
public static ExtensionInterface getExtensionImpl(Context context) {
|
||||||
|
return new SettingsExtensionImpl(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,217 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 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.extensions;
|
||||||
|
|
||||||
|
import static android.view.Display.DEFAULT_DISPLAY;
|
||||||
|
|
||||||
|
import static androidx.window.extensions.ExtensionHelper.getWindowDisplay;
|
||||||
|
import static androidx.window.extensions.ExtensionHelper.isInMultiWindow;
|
||||||
|
import static androidx.window.extensions.ExtensionHelper.rotateRectToDisplayRotation;
|
||||||
|
import static androidx.window.extensions.ExtensionHelper.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 java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
class SettingsExtensionImpl extends StubExtension {
|
||||||
|
private static final String TAG = "SettingsExtension";
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsExtensionImpl(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()) {
|
||||||
|
ExtensionWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken);
|
||||||
|
updateWindowLayout(windowToken, newLayout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ExtensionDeviceState getDeviceState() {
|
||||||
|
ContentResolver resolver = mContext.getContentResolver();
|
||||||
|
int posture = Settings.Global.getInt(resolver, DEVICE_POSTURE,
|
||||||
|
ExtensionDeviceState.POSTURE_UNKNOWN);
|
||||||
|
return new ExtensionDeviceState(posture);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ExtensionWindowLayoutInfo getWindowLayoutInfo(@NonNull IBinder windowToken) {
|
||||||
|
List<ExtensionDisplayFeature> displayFeatures = readDisplayFeatures(windowToken);
|
||||||
|
return new ExtensionWindowLayoutInfo(displayFeatures);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ExtensionDisplayFeature> readDisplayFeatures(IBinder windowToken) {
|
||||||
|
List<ExtensionDisplayFeature> features = new ArrayList<ExtensionDisplayFeature>();
|
||||||
|
int displayId = getWindowDisplay(windowToken);
|
||||||
|
if (displayId != DEFAULT_DISPLAY) {
|
||||||
|
Log.w(TAG, "This sample doesn't support display features on secondary displays");
|
||||||
|
return features;
|
||||||
|
}
|
||||||
|
|
||||||
|
ContentResolver resolver = mContext.getContentResolver();
|
||||||
|
final String displayFeaturesString = Settings.Global.getString(resolver, DISPLAY_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;
|
||||||
|
}
|
||||||
|
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 = ExtensionDisplayFeature.TYPE_FOLD;
|
||||||
|
break;
|
||||||
|
case FEATURE_TYPE_HINGE:
|
||||||
|
type = ExtensionDisplayFeature.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 (!featureRect.isEmpty()) {
|
||||||
|
ExtensionDisplayFeature feature =
|
||||||
|
new ExtensionDisplayFeature(featureRect, 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onListenersChanged() {
|
||||||
|
if (mSettingsObserver == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasListeners()) {
|
||||||
|
mSettingsObserver.registerObserversIfNeeded();
|
||||||
|
} else {
|
||||||
|
mSettingsObserver.unregisterObserversIfNeeded();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
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