Loading libs/WindowManager/Jetpack/Android.bp 0 → 100644 +38 −0 Original line number Diff line number Diff line // 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, } libs/WindowManager/Jetpack/androidx.window.extensions.xml 0 → 100644 +21 −0 Original line number Diff line number Diff line <?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> libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java 0 → 100644 +130 −0 Original line number Diff line number Diff line /* * 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; } } libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java 0 → 100644 +42 −0 Original line number Diff line number Diff line /* * 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"; } } libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java 0 → 100644 +217 −0 Original line number Diff line number Diff line /* * 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(); } } } Loading
libs/WindowManager/Jetpack/Android.bp 0 → 100644 +38 −0 Original line number Diff line number Diff line // 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, }
libs/WindowManager/Jetpack/androidx.window.extensions.xml 0 → 100644 +21 −0 Original line number Diff line number Diff line <?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>
libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionHelper.java 0 → 100644 +130 −0 Original line number Diff line number Diff line /* * 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; } }
libs/WindowManager/Jetpack/src/androidx/window/extensions/ExtensionProvider.java 0 → 100644 +42 −0 Original line number Diff line number Diff line /* * 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"; } }
libs/WindowManager/Jetpack/src/androidx/window/extensions/SettingsExtensionImpl.java 0 → 100644 +217 −0 Original line number Diff line number Diff line /* * 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(); } } }