From 4bc4a9cd04b2e1382d790553787e82d0a7167673 Mon Sep 17 00:00:00 2001 From: Diego Vela <diegovela@google.com> Date: Thu, 15 Feb 2024 23:48:38 +0000 Subject: [PATCH] Report folds in SupportedWindowFeatures. Report folds in SupportedWindowFeatures. Add utility test method to check if device supports half opened mode. Bug: 293340299 Test: atest CtsWindowManagerJetpackTestCases Change-Id: I76ae0db0af2e7982149959f946641d4b9473d18e --- .../window/common/CommonFoldingFeature.java | 2 +- ...iceStateManagerFoldingFeatureProducer.java | 41 ++++++++++-- .../extensions/WindowExtensionsImpl.java | 8 ++- .../layout/DisplayFoldFeatureUtil.java | 63 +++++++++++++++++++ .../layout/WindowLayoutComponentImpl.java | 21 +++++-- .../window/sidecar/SampleSidecarImpl.java | 7 ++- 6 files changed, 127 insertions(+), 15 deletions(-) create mode 100644 libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/DisplayFoldFeatureUtil.java diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java index 65955b1d9bcc..e37dea4dfd69 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java @@ -126,7 +126,7 @@ public final class CommonFoldingFeature { * @see #FEATURE_PATTERN * @return {@link List} of {@link CommonFoldingFeature}. */ - static List<CommonFoldingFeature> parseListFromString(@NonNull String value, + public static List<CommonFoldingFeature> parseListFromString(@NonNull String value, @State int hingeState) { List<CommonFoldingFeature> features = new ArrayList<>(); String[] featureStrings = value.split(";"); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java index a184dff5005b..88fd461debbe 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java @@ -36,6 +36,7 @@ import androidx.window.util.BaseDataProducer; import com.android.internal.R; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -78,7 +79,9 @@ public final class DeviceStateManagerFoldingFeatureProducer private int mCurrentBaseDeviceState = INVALID_DEVICE_STATE; @NonNull - private final BaseDataProducer<String> mRawFoldSupplier; + private final RawFoldingFeatureProducer mRawFoldSupplier; + + private final boolean mIsHalfOpenedSupported; private final DeviceStateCallback mDeviceStateCallback = new DeviceStateCallback() { @Override @@ -101,10 +104,12 @@ public final class DeviceStateManagerFoldingFeatureProducer }; public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context, - @NonNull BaseDataProducer<String> rawFoldSupplier) { + @NonNull RawFoldingFeatureProducer rawFoldSupplier, + @NonNull DeviceStateManager deviceStateManager) { mRawFoldSupplier = rawFoldSupplier; String[] deviceStatePosturePairs = context.getResources() .getStringArray(R.array.config_device_state_postures); + boolean isHalfOpenedSupported = false; for (String deviceStatePosturePair : deviceStatePosturePairs) { String[] deviceStatePostureMapping = deviceStatePosturePair.split(":"); if (deviceStatePostureMapping.length != 2) { @@ -128,12 +133,13 @@ public final class DeviceStateManagerFoldingFeatureProducer } continue; } - + isHalfOpenedSupported = isHalfOpenedSupported + || posture == CommonFoldingFeature.COMMON_STATE_HALF_OPENED; mDeviceStateToPostureMap.put(deviceState, posture); } - + mIsHalfOpenedSupported = isHalfOpenedSupported; if (mDeviceStateToPostureMap.size() > 0) { - Objects.requireNonNull(context.getSystemService(DeviceStateManager.class)) + Objects.requireNonNull(deviceStateManager) .registerCallback(context.getMainExecutor(), mDeviceStateCallback); } } @@ -187,6 +193,31 @@ public final class DeviceStateManagerFoldingFeatureProducer } } + /** + * Returns a {@link List} of all the {@link CommonFoldingFeature} with the state set to + * {@link CommonFoldingFeature#COMMON_STATE_UNKNOWN}. This method parses a {@link String} so a + * caller should consider caching the value or the derived value. + */ + @NonNull + public List<CommonFoldingFeature> getFoldsWithUnknownState() { + Optional<String> optionalFoldingFeatureString = mRawFoldSupplier.getCurrentData(); + + if (optionalFoldingFeatureString.isPresent()) { + return CommonFoldingFeature.parseListFromString( + optionalFoldingFeatureString.get(), CommonFoldingFeature.COMMON_STATE_UNKNOWN + ); + } + return Collections.emptyList(); + } + + + /** + * Returns {@code true} if the device supports half-opened mode, {@code false} otherwise. + */ + public boolean isHalfOpenedSupported() { + return mIsHalfOpenedSupported; + } + /** * Adds the data to the storeFeaturesConsumer when the data is ready. * @param storeFeaturesConsumer a consumer to collect the data when it is first available. diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java index 29cf05407a61..6714263ad952 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java @@ -20,6 +20,7 @@ import android.app.ActivityTaskManager; import android.app.ActivityThread; import android.app.Application; import android.content.Context; +import android.hardware.devicestate.DeviceStateManager; import android.util.Log; import androidx.annotation.NonNull; @@ -63,6 +64,11 @@ public class WindowExtensionsImpl implements WindowExtensions { return Objects.requireNonNull(ActivityThread.currentApplication()); } + @NonNull + private DeviceStateManager getDeviceStateManager() { + return Objects.requireNonNull(getApplication().getSystemService(DeviceStateManager.class)); + } + @NonNull private DeviceStateManagerFoldingFeatureProducer getFoldingFeatureProducer() { if (mFoldingFeatureProducer == null) { @@ -73,7 +79,7 @@ public class WindowExtensionsImpl implements WindowExtensions { new RawFoldingFeatureProducer(context); mFoldingFeatureProducer = new DeviceStateManagerFoldingFeatureProducer(context, - foldingFeatureProducer); + foldingFeatureProducer, getDeviceStateManager()); } } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/DisplayFoldFeatureUtil.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/DisplayFoldFeatureUtil.java new file mode 100644 index 000000000000..a0f481a911ad --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/DisplayFoldFeatureUtil.java @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2024 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.layout; + +import androidx.window.common.CommonFoldingFeature; +import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; + +import java.util.ArrayList; +import java.util.List; + +/** + * Util functions for working with {@link androidx.window.extensions.layout.DisplayFoldFeature}. + */ +public class DisplayFoldFeatureUtil { + + private DisplayFoldFeatureUtil() {} + + private static DisplayFoldFeature create(CommonFoldingFeature foldingFeature, + boolean isHalfOpenedSupported) { + final int foldType; + if (foldingFeature.getType() == CommonFoldingFeature.COMMON_TYPE_HINGE) { + foldType = DisplayFoldFeature.TYPE_HINGE; + } else { + foldType = DisplayFoldFeature.TYPE_SCREEN_FOLD_IN; + } + DisplayFoldFeature.Builder featureBuilder = new DisplayFoldFeature.Builder(foldType); + + if (isHalfOpenedSupported) { + featureBuilder.addProperty(DisplayFoldFeature.FOLD_PROPERTY_SUPPORTS_HALF_OPENED); + } + return featureBuilder.build(); + } + + /** + * Returns the list of supported {@link DisplayFeature} calculated from the + * {@link DeviceStateManagerFoldingFeatureProducer}. + */ + public static List<DisplayFoldFeature> extractDisplayFoldFeatures( + DeviceStateManagerFoldingFeatureProducer producer) { + List<DisplayFoldFeature> foldFeatures = new ArrayList<>(); + List<CommonFoldingFeature> folds = producer.getFoldsWithUnknownState(); + + final boolean isHalfOpenedSupported = producer.isHalfOpenedSupported(); + for (CommonFoldingFeature fold : folds) { + foldFeatures.add(DisplayFoldFeatureUtil.create(fold, isHalfOpenedSupported)); + } + return foldFeatures; + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java index 6e704f35fb22..4fd11c495529 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java @@ -45,7 +45,6 @@ import androidx.window.common.CommonFoldingFeature; import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; import androidx.window.common.EmptyLifecycleCallbacksAdapter; import androidx.window.extensions.core.util.function.Consumer; -import androidx.window.util.DataProducer; import java.util.ArrayList; import java.util.Collections; @@ -56,10 +55,6 @@ import java.util.Set; /** * Reference implementation of androidx.window.extensions.layout 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. */ public class WindowLayoutComponentImpl implements WindowLayoutComponent { private static final String TAG = WindowLayoutComponentImpl.class.getSimpleName(); @@ -71,7 +66,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { new ArrayMap<>(); @GuardedBy("mLock") - private final DataProducer<List<CommonFoldingFeature>> mFoldingFeatureProducer; + private final DeviceStateManagerFoldingFeatureProducer mFoldingFeatureProducer; @GuardedBy("mLock") private final List<CommonFoldingFeature> mLastReportedFoldingFeatures = new ArrayList<>(); @@ -87,12 +82,17 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { private final RawConfigurationChangedListener mRawConfigurationChangedListener = new RawConfigurationChangedListener(); + private final SupportedWindowFeatures mSupportedWindowFeatures; + public WindowLayoutComponentImpl(@NonNull Context context, @NonNull DeviceStateManagerFoldingFeatureProducer foldingFeatureProducer) { ((Application) context.getApplicationContext()) .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged()); mFoldingFeatureProducer = foldingFeatureProducer; mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); + final List<DisplayFoldFeature> displayFoldFeatures = + DisplayFoldFeatureUtil.extractDisplayFoldFeatures(mFoldingFeatureProducer); + mSupportedWindowFeatures = new SupportedWindowFeatures.Builder(displayFoldFeatures).build(); } /** @@ -283,6 +283,15 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { } } + /** + * Returns the {@link SupportedWindowFeatures} for the device. This list does not change over + * time. + */ + @NonNull + public SupportedWindowFeatures getSupportedWindowFeatures() { + return mSupportedWindowFeatures; + } + /** @see #getWindowLayoutInfo(Context, List) */ private WindowLayoutInfo getWindowLayoutInfo(int displayId, @NonNull WindowConfiguration windowConfiguration, diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java index a836e05b2d66..56c3bce87d6e 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java @@ -17,6 +17,7 @@ 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; @@ -25,6 +26,7 @@ import android.app.ActivityThread; import android.app.Application; import android.content.Context; import android.graphics.Rect; +import android.hardware.devicestate.DeviceStateManager; import android.os.Bundle; import android.os.IBinder; @@ -49,10 +51,11 @@ class SampleSidecarImpl extends StubSidecar { SampleSidecarImpl(Context context) { ((Application) context.getApplicationContext()) .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged()); - BaseDataProducer<String> settingsFeatureProducer = new RawFoldingFeatureProducer(context); + RawFoldingFeatureProducer settingsFeatureProducer = new RawFoldingFeatureProducer(context); BaseDataProducer<List<CommonFoldingFeature>> foldingFeatureProducer = new DeviceStateManagerFoldingFeatureProducer(context, - settingsFeatureProducer); + settingsFeatureProducer, + context.getSystemService(DeviceStateManager.class)); foldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); } -- GitLab