diff options
Diffstat (limited to 'libs/WindowManager')
6 files changed, 448 insertions, 0 deletions
diff --git a/libs/WindowManager/Jetpack/Android.bp b/libs/WindowManager/Jetpack/Android.bp new file mode 100644 index 000000000000..308c1a59a7aa --- /dev/null +++ b/libs/WindowManager/Jetpack/Android.bp @@ -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, +} diff --git a/libs/WindowManager/Jetpack/androidx.window.extensions.xml b/libs/WindowManager/Jetpack/androidx.window.extensions.xml new file mode 100644 index 000000000000..1f0ff6656de0 --- /dev/null +++ b/libs/WindowManager/Jetpack/androidx.window.extensions.xml @@ -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> diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java new file mode 100644 index 000000000000..c4f11a0a370c --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java @@ -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; + } +} 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 000000000000..47349f11fb93 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java @@ -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"; + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java new file mode 100644 index 000000000000..7a3fbf3ad9b8 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java @@ -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(); + } + } +} diff --git a/libs/WindowManager/Jetpack/window-extensions-release.aar b/libs/WindowManager/Jetpack/window-extensions-release.aar Binary files differnew file mode 100644 index 000000000000..0ebbb86daf82 --- /dev/null +++ b/libs/WindowManager/Jetpack/window-extensions-release.aar |