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