From 882901805b1440c5367ce1134e03a835a3624a5d Mon Sep 17 00:00:00 2001 From: Diego Vela Date: Wed, 1 Dec 2021 15:20:00 -0800 Subject: DO NOT MERGE Remove DisplayFeature from common package. Remove DisplayFeature interface from the common package since it actually represents a CommonFoldingFeature. This makes the code a little confusing since it is not properly named. Bug: 205342008 Test: manual - run WindowManager samples. Change-Id: I18e609fb71004544e7de1f346ccef9ac04efb0d7 --- .../window/common/CommonDisplayFeature.java | 153 ---------------- .../window/common/CommonFoldingFeature.java | 198 +++++++++++++++++++++ .../src/androidx/window/common/DisplayFeature.java | 60 ------- .../ResourceConfigDisplayFeatureProducer.java | 12 +- .../common/SettingsDisplayFeatureProducer.java | 12 +- .../layout/WindowLayoutComponentImpl.java | 70 ++++---- .../androidx/window/sidecar/SampleSidecarImpl.java | 18 +- 7 files changed, 254 insertions(+), 269 deletions(-) delete mode 100644 libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java create mode 100644 libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java delete mode 100644 libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java (limited to 'libs') diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java deleted file mode 100644 index eb9429747b66..000000000000 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonDisplayFeature.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2021 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.common; - -import static androidx.window.util.ExtensionHelper.isZero; - -import android.annotation.Nullable; -import android.graphics.Rect; - -import androidx.annotation.NonNull; - -import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */ -final class CommonDisplayFeature implements DisplayFeature { - private static final Pattern FEATURE_PATTERN = - Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]-?(flat|half-opened)?"); - - private static final String FEATURE_TYPE_FOLD = "fold"; - private static final String FEATURE_TYPE_HINGE = "hinge"; - - private static final String PATTERN_STATE_FLAT = "flat"; - private static final String PATTERN_STATE_HALF_OPENED = "half-opened"; - - // TODO(b/183049815): Support feature strings that include the state of the feature. - - /** - * Parses a display feature from a string. - * - * @throws IllegalArgumentException if the provided string is improperly formatted or could not - * otherwise be parsed. - * @see #FEATURE_PATTERN - */ - @NonNull - static CommonDisplayFeature parseFromString(@NonNull String string) { - Matcher featureMatcher = FEATURE_PATTERN.matcher(string); - if (!featureMatcher.matches()) { - throw new IllegalArgumentException("Malformed feature description format: " + string); - } - try { - String featureType = featureMatcher.group(1); - featureType = featureType == null ? "" : featureType; - int type; - switch (featureType) { - case FEATURE_TYPE_FOLD: - type = 1 /* TYPE_FOLD */; - break; - case FEATURE_TYPE_HINGE: - type = 2 /* TYPE_HINGE */; - break; - default: { - throw new IllegalArgumentException("Malformed feature type: " + featureType); - } - } - - 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); - if (isZero(featureRect)) { - throw new IllegalArgumentException("Feature has empty bounds: " + string); - } - String stateString = featureMatcher.group(6); - stateString = stateString == null ? "" : stateString; - Integer state; - switch (stateString) { - case PATTERN_STATE_FLAT: - state = COMMON_STATE_FLAT; - break; - case PATTERN_STATE_HALF_OPENED: - state = COMMON_STATE_HALF_OPENED; - break; - default: - state = null; - break; - } - return new CommonDisplayFeature(type, state, featureRect); - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Malformed feature description: " + string, e); - } - } - - private final int mType; - @Nullable - private final Integer mState; - @NonNull - private final Rect mRect; - - CommonDisplayFeature(int type, @Nullable Integer state, @NonNull Rect rect) { - assertValidState(state); - this.mType = type; - this.mState = state; - if (rect.width() == 0 && rect.height() == 0) { - throw new IllegalArgumentException( - "Display feature rectangle cannot have zero width and height simultaneously."); - } - this.mRect = rect; - } - - public int getType() { - return mType; - } - - /** Returns the state of the feature, or {@code null} if the feature has no state. */ - @Nullable - public Integer getState() { - return mState; - } - - @NonNull - public Rect getRect() { - return mRect; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - CommonDisplayFeature that = (CommonDisplayFeature) o; - return mType == that.mType - && Objects.equals(mState, that.mState) - && mRect.equals(that.mRect); - } - - @Override - public int hashCode() { - return Objects.hash(mType, mState, mRect); - } - - private static void assertValidState(@Nullable Integer state) { - if (state != null && state != COMMON_STATE_FLAT && state != COMMON_STATE_HALF_OPENED) { - throw new IllegalArgumentException("Invalid state: " + state - + "must be either COMMON_STATE_FLAT or COMMON_STATE_HALF_OPENED"); - } - } -} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java new file mode 100644 index 000000000000..a0c66fa1ff76 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2021 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.common; + +import static androidx.window.util.ExtensionHelper.isZero; + +import android.annotation.IntDef; +import android.annotation.Nullable; +import android.graphics.Rect; + +import androidx.annotation.NonNull; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** A representation of a folding feature for both Extension and Sidecar. + * For Sidecar this is the same as combining {@link androidx.window.sidecar.SidecarDeviceState} and + * {@link androidx.window.sidecar.SidecarDisplayFeature}. For Extensions this is the mirror of + * {@link androidx.window.extensions.layout.FoldingFeature}. + */ +public final class CommonFoldingFeature { + + /** + * A common type to represent a hinge where the screen is continuous. + */ + public static final int COMMON_TYPE_FOLD = 1; + + /** + * A common type to represent a hinge where there is a physical gap separating multiple + * displays. + */ + public static final int COMMON_TYPE_HINGE = 2; + + @IntDef({COMMON_TYPE_FOLD, COMMON_TYPE_HINGE}) + @Retention(RetentionPolicy.SOURCE) + public @interface Type { + } + + /** + * A common state to represent a FLAT hinge. This is needed because the definitions in Sidecar + * and Extensions do not match exactly. + */ + public static final int COMMON_STATE_FLAT = 3; + /** + * A common state to represent a HALF_OPENED hinge. This is needed because the definitions in + * Sidecar and Extensions do not match exactly. + */ + public static final int COMMON_STATE_HALF_OPENED = 2; + + /** + * The possible states for a folding hinge. + */ + @IntDef({COMMON_STATE_FLAT, COMMON_STATE_HALF_OPENED}) + @Retention(RetentionPolicy.SOURCE) + public @interface State { + } + + private static final Pattern FEATURE_PATTERN = + Pattern.compile("([a-z]+)-\\[(\\d+),(\\d+),(\\d+),(\\d+)]-?(flat|half-opened)?"); + + private static final String FEATURE_TYPE_FOLD = "fold"; + private static final String FEATURE_TYPE_HINGE = "hinge"; + + private static final String PATTERN_STATE_FLAT = "flat"; + private static final String PATTERN_STATE_HALF_OPENED = "half-opened"; + + /** + * Parses a display feature from a string. + * + * @throws IllegalArgumentException if the provided string is improperly formatted or could not + * otherwise be parsed. + * @see #FEATURE_PATTERN + */ + @NonNull + static CommonFoldingFeature parseFromString(@NonNull String string) { + Matcher featureMatcher = FEATURE_PATTERN.matcher(string); + if (!featureMatcher.matches()) { + throw new IllegalArgumentException("Malformed feature description format: " + string); + } + try { + String featureType = featureMatcher.group(1); + featureType = featureType == null ? "" : featureType; + int type; + switch (featureType) { + case FEATURE_TYPE_FOLD: + type = COMMON_TYPE_FOLD; + break; + case FEATURE_TYPE_HINGE: + type = COMMON_TYPE_HINGE; + break; + default: { + throw new IllegalArgumentException("Malformed feature type: " + featureType); + } + } + + 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); + if (isZero(featureRect)) { + throw new IllegalArgumentException("Feature has empty bounds: " + string); + } + String stateString = featureMatcher.group(6); + stateString = stateString == null ? "" : stateString; + Integer state; + switch (stateString) { + case PATTERN_STATE_FLAT: + state = COMMON_STATE_FLAT; + break; + case PATTERN_STATE_HALF_OPENED: + state = COMMON_STATE_HALF_OPENED; + break; + default: + state = null; + break; + } + return new CommonFoldingFeature(type, state, featureRect); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("Malformed feature description: " + string, e); + } + } + + private final int mType; + @Nullable + private final Integer mState; + @NonNull + private final Rect mRect; + + CommonFoldingFeature(int type, @Nullable Integer state, @NonNull Rect rect) { + assertValidState(state); + this.mType = type; + this.mState = state; + if (rect.width() == 0 && rect.height() == 0) { + throw new IllegalArgumentException( + "Display feature rectangle cannot have zero width and height simultaneously."); + } + this.mRect = rect; + } + + /** Returns the type of the feature. */ + @Type + public int getType() { + return mType; + } + + /** Returns the state of the feature, or {@code null} if the feature has no state. */ + @Nullable + @State + public Integer getState() { + return mState; + } + + /** Returns the bounds of the feature. */ + @NonNull + public Rect getRect() { + return mRect; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + CommonFoldingFeature that = (CommonFoldingFeature) o; + return mType == that.mType + && Objects.equals(mState, that.mState) + && mRect.equals(that.mRect); + } + + @Override + public int hashCode() { + return Objects.hash(mType, mState, mRect); + } + + private static void assertValidState(@Nullable Integer state) { + if (state != null && state != COMMON_STATE_FLAT && state != COMMON_STATE_HALF_OPENED) { + throw new IllegalArgumentException("Invalid state: " + state + + "must be either COMMON_STATE_FLAT or COMMON_STATE_HALF_OPENED"); + } + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java deleted file mode 100644 index 573641857b99..000000000000 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/DisplayFeature.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2021 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.common; - -import android.annotation.IntDef; -import android.annotation.Nullable; -import android.graphics.Rect; - -import androidx.annotation.NonNull; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** Wrapper for both Extension and Sidecar versions of DisplayFeature. */ -public interface DisplayFeature { - /** Returns the type of the feature. */ - int getType(); - - /** Returns the state of the feature, or {@code null} if the feature has no state. */ - @Nullable - @State - Integer getState(); - - /** Returns the bounds of the feature. */ - @NonNull - Rect getRect(); - - /** - * A common state to represent a FLAT hinge. This is needed because the definitions in Sidecar - * and Extensions do not match exactly. - */ - int COMMON_STATE_FLAT = 3; - /** - * A common state to represent a HALF_OPENED hinge. This is needed because the definitions in - * Sidecar and Extensions do not match exactly. - */ - int COMMON_STATE_HALF_OPENED = 2; - - /** - * The possible states for a folding hinge. - */ - @IntDef({COMMON_STATE_FLAT, COMMON_STATE_HALF_OPENED}) - @Retention(RetentionPolicy.SOURCE) - @interface State {} - -} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java index cd2cadc082e1..a48d3ad797e6 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java @@ -32,11 +32,11 @@ import java.util.Optional; /** * Implementation of {@link androidx.window.util.DataProducer} that produces - * {@link CommonDisplayFeature} parsed from a string stored in the resources config at + * {@link CommonFoldingFeature} parsed from a string stored in the resources config at * {@link R.string#config_display_features}. */ public final class ResourceConfigDisplayFeatureProducer extends - BaseDataProducer> { + BaseDataProducer> { private static final boolean DEBUG = false; private static final String TAG = "ResourceConfigDisplayFeatureProducer"; @@ -48,19 +48,19 @@ public final class ResourceConfigDisplayFeatureProducer extends @Override @Nullable - public Optional> getData() { + public Optional> getData() { String displayFeaturesString = mContext.getResources().getString( R.string.config_display_features); if (TextUtils.isEmpty(displayFeaturesString)) { return Optional.empty(); } - List features = new ArrayList<>(); + List features = new ArrayList<>(); String[] featureStrings = displayFeaturesString.split(";"); for (String featureString : featureStrings) { - CommonDisplayFeature feature; + CommonFoldingFeature feature; try { - feature = CommonDisplayFeature.parseFromString(featureString); + feature = CommonFoldingFeature.parseFromString(featureString); } catch (IllegalArgumentException e) { if (DEBUG) { Log.w(TAG, "Failed to parse display feature: " + featureString, e); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java index 040662657a74..f3d6150f11d1 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java @@ -36,10 +36,10 @@ import java.util.Optional; /** * Implementation of {@link androidx.window.util.DataProducer} that produces - * {@link CommonDisplayFeature} parsed from a string stored in {@link Settings}. + * {@link CommonFoldingFeature} parsed from a string stored in {@link Settings}. */ public final class SettingsDisplayFeatureProducer - extends BaseDataProducer> { + extends BaseDataProducer> { private static final boolean DEBUG = false; private static final String TAG = "SettingsDisplayFeatureProducer"; private static final String DISPLAY_FEATURES = "display_features"; @@ -58,22 +58,22 @@ public final class SettingsDisplayFeatureProducer @Override @Nullable - public Optional> getData() { + public Optional> getData() { String displayFeaturesString = Settings.Global.getString(mResolver, DISPLAY_FEATURES); if (displayFeaturesString == null) { return Optional.empty(); } - List features = new ArrayList<>(); + List features = new ArrayList<>(); if (TextUtils.isEmpty(displayFeaturesString)) { return Optional.of(features); } String[] featureStrings = displayFeaturesString.split(";"); for (String featureString : featureStrings) { - CommonDisplayFeature feature; + CommonFoldingFeature feature; try { - feature = CommonDisplayFeature.parseFromString(featureString); + feature = CommonFoldingFeature.parseFromString(featureString); } catch (IllegalArgumentException e) { if (DEBUG) { Log.w(TAG, "Failed to parse display feature: " + featureString, e); 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 fe9ce971d4d9..b4b75b4e9ca5 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java @@ -18,8 +18,8 @@ package androidx.window.extensions.layout; import static android.view.Display.DEFAULT_DISPLAY; -import static androidx.window.common.DisplayFeature.COMMON_STATE_FLAT; -import static androidx.window.common.DisplayFeature.COMMON_STATE_HALF_OPENED; +import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_FLAT; +import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_HALF_OPENED; import static androidx.window.util.ExtensionHelper.rotateRectToDisplayRotation; import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect; @@ -30,8 +30,8 @@ import android.graphics.Rect; import android.util.Log; import androidx.annotation.NonNull; +import androidx.window.common.CommonFoldingFeature; import androidx.window.common.DeviceStateManagerPostureProducer; -import androidx.window.common.DisplayFeature; import androidx.window.common.ResourceConfigDisplayFeatureProducer; import androidx.window.common.SettingsDevicePostureProducer; import androidx.window.common.SettingsDisplayFeatureProducer; @@ -56,7 +56,6 @@ import java.util.function.Consumer; */ public class WindowLayoutComponentImpl implements WindowLayoutComponent { private static final String TAG = "SampleExtension"; - private static WindowLayoutComponent sInstance; private final Map> mWindowLayoutChangeListeners = new HashMap<>(); @@ -65,7 +64,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { private final DataProducer mDevicePostureProducer; private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer; - private final DataProducer> mDisplayFeatureProducer; + private final DataProducer> mDisplayFeatureProducer; public WindowLayoutComponentImpl(Context context) { mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context); @@ -123,17 +122,18 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { } /** - * Calculate the {@link DisplayFeature.State} from the feature or the device posture producer. - * If the given {@link DisplayFeature.State} is not valid then {@code null} will be returned. - * The {@link FoldingFeature} should be ignored in the case of an invalid - * {@link DisplayFeature.State}. + * Calculate the {@link CommonFoldingFeature.State} from the feature or the device posture + * producer. + * If the given {@link CommonFoldingFeature.State} is not valid then {@code null} will be + * returned. The {@link FoldingFeature} should be ignored in the case of an invalid + * {@link CommonFoldingFeature.State}. * - * @param feature a {@link DisplayFeature} to provide the feature state if present. - * @return {@link DisplayFeature.State} of the hinge if present or the state from the posture - * produce if present. + * @param feature a {@link CommonFoldingFeature} to provide the feature state if present. + * @return {@link CommonFoldingFeature.State} of the hinge if present or the state from the + * posture produce if present. */ @Nullable - private Integer getFeatureState(DisplayFeature feature) { + private Integer getFeatureState(CommonFoldingFeature feature) { Integer featureState = feature.getState(); Optional posture = mDevicePostureProducer.getData(); Integer state = featureState == null ? posture.orElse(null) : featureState; @@ -142,13 +142,14 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { /** * A convenience method to translate from the common feature state to the extensions feature - * state. More specifically, translates from {@link DisplayFeature.State} to + * state. More specifically, translates from {@link CommonFoldingFeature.State} to * {@link FoldingFeature.STATE_FLAT} or {@link FoldingFeature.STATE_HALF_OPENED}. If it is not * possible to translate, then we will return a {@code null} value. * - * @param state if it matches a value in {@link DisplayFeature.State}, {@code null} otherwise. - * @return a {@link FoldingFeature.STATE_FLAT} or {@link FoldingFeature.STATE_HALF_OPENED} if - * the given state matches a value in {@link DisplayFeature.State} and {@code null} otherwise. + * @param state if it matches a value in {@link CommonFoldingFeature.State}, {@code null} + * otherwise. @return a {@link FoldingFeature.STATE_FLAT} or + * {@link FoldingFeature.STATE_HALF_OPENED} if the given state matches a value in + * {@link CommonFoldingFeature.State} and {@code null} otherwise. */ @Nullable private Integer convertToExtensionState(@Nullable Integer state) { @@ -172,33 +173,32 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { @NonNull private WindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) { - List displayFeatures = + List displayFeatures = getDisplayFeatures(activity); return new WindowLayoutInfo(displayFeatures); } /** - * Translate from the {@link DisplayFeature} to - * {@link androidx.window.extensions.layout.DisplayFeature} for a given {@link Activity}. If a - * {@link DisplayFeature} is not valid then it will be omitted. + * Translate from the {@link CommonFoldingFeature} to + * {@link DisplayFeature} for a given {@link Activity}. If a + * {@link CommonFoldingFeature} is not valid then it will be omitted. * * For a {@link FoldingFeature} the bounds are localized into the {@link Activity} window - * coordinate space and the state is calculated either from {@link DisplayFeature#getState()} or - * {@link #mDisplayFeatureProducer}. The state from {@link #mDisplayFeatureProducer} may not be - * valid since {@link #mDisplayFeatureProducer} is a general state controller. If the state is - * not valid, the {@link FoldingFeature} is omitted from the {@link List} of - * {@link androidx.window.extensions.layout.DisplayFeature}. If the bounds are not valid, - * constructing a {@link FoldingFeature} will throw an {@link IllegalArgumentException} since - * this can cause negative UI effects down stream. + * coordinate space and the state is calculated either from + * {@link CommonFoldingFeature#getState()} or {@link #mDisplayFeatureProducer}. The state from + * {@link #mDisplayFeatureProducer} may not be valid since {@link #mDisplayFeatureProducer} is + * a general state controller. If the state is not valid, the {@link FoldingFeature} is omitted + * from the {@link List} of {@link DisplayFeature}. If the bounds are not valid, constructing a + * {@link FoldingFeature} will throw an {@link IllegalArgumentException} since this can cause + * negative UI effects down stream. * * @param activity a proxy for the {@link android.view.Window} that contains the - * {@link androidx.window.extensions.layout.DisplayFeature}. - * @return a {@link List} of valid {@link androidx.window.extensions.layout.DisplayFeature} that + * {@link DisplayFeature}. + * @return a {@link List} of valid {@link DisplayFeature} that * are within the {@link android.view.Window} of the {@link Activity} */ - private List getDisplayFeatures( - @NonNull Activity activity) { - List features = new ArrayList<>(); + private List getDisplayFeatures(@NonNull Activity activity) { + List features = new ArrayList<>(); int displayId = activity.getDisplay().getDisplayId(); if (displayId != DEFAULT_DISPLAY) { Log.w(TAG, "This sample doesn't support display features on secondary displays"); @@ -211,10 +211,10 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { return features; } - Optional> storedFeatures = mDisplayFeatureProducer.getData(); + Optional> storedFeatures = mDisplayFeatureProducer.getData(); if (storedFeatures.isPresent()) { - for (DisplayFeature baseFeature : storedFeatures.get()) { + for (CommonFoldingFeature baseFeature : storedFeatures.get()) { Integer state = getFeatureState(baseFeature); if (state == null) { continue; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java index aa949f126154..65e2a5e34f19 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java @@ -29,8 +29,8 @@ import android.os.IBinder; import android.util.Log; import androidx.annotation.NonNull; +import androidx.window.common.CommonFoldingFeature; import androidx.window.common.DeviceStateManagerPostureProducer; -import androidx.window.common.DisplayFeature; import androidx.window.common.ResourceConfigDisplayFeatureProducer; import androidx.window.common.SettingsDevicePostureProducer; import androidx.window.common.SettingsDisplayFeatureProducer; @@ -54,7 +54,7 @@ class SampleSidecarImpl extends StubSidecar { private final DataProducer mDevicePostureProducer; private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer; - private final DataProducer> mDisplayFeatureProducer; + private final DataProducer> mDisplayFeatureProducer; SampleSidecarImpl(Context context) { mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context); @@ -95,19 +95,19 @@ class SampleSidecarImpl extends StubSidecar { } private int deviceStateFromFeature() { - List storedFeatures = mDisplayFeatureProducer.getData() + List storedFeatures = mDisplayFeatureProducer.getData() .orElse(Collections.emptyList()); for (int i = 0; i < storedFeatures.size(); i++) { - DisplayFeature feature = storedFeatures.get(i); + CommonFoldingFeature feature = storedFeatures.get(i); final int state = feature.getState() == null ? -1 : feature.getState(); if (DEBUG && feature.getState() == null) { Log.d(TAG, "feature#getState was null for DisplayFeature: " + feature); } switch (state) { - case DisplayFeature.COMMON_STATE_FLAT: + case CommonFoldingFeature.COMMON_STATE_FLAT: return SidecarDeviceState.POSTURE_OPENED; - case DisplayFeature.COMMON_STATE_HALF_OPENED: + case CommonFoldingFeature.COMMON_STATE_HALF_OPENED: return SidecarDeviceState.POSTURE_HALF_OPENED; } } @@ -127,7 +127,7 @@ class SampleSidecarImpl extends StubSidecar { } private List getDisplayFeatures(@NonNull Activity activity) { - List features = new ArrayList(); + List features = new ArrayList<>(); int displayId = activity.getDisplay().getDisplayId(); if (displayId != DEFAULT_DISPLAY) { Log.w(TAG, "This sample doesn't support display features on secondary displays"); @@ -140,9 +140,9 @@ class SampleSidecarImpl extends StubSidecar { return features; } - Optional> storedFeatures = mDisplayFeatureProducer.getData(); + Optional> storedFeatures = mDisplayFeatureProducer.getData(); if (storedFeatures.isPresent()) { - for (DisplayFeature baseFeature : storedFeatures.get()) { + for (CommonFoldingFeature baseFeature : storedFeatures.get()) { SidecarDisplayFeature feature = new SidecarDisplayFeature(); Rect featureRect = baseFeature.getRect(); rotateRectToDisplayRotation(displayId, featureRect); -- cgit v1.2.3-59-g8ed1b From e5c6ad5d79b1e19901185faf35580a96a923e545 Mon Sep 17 00:00:00 2001 From: Diego Vela Date: Wed, 1 Dec 2021 21:23:08 -0800 Subject: DO NOT MERGE Merge state producer and feature producer. Merge device state producer and display feature producer so that FoldingFeature data has a single data pipeline. This should make it easier to retrigger reading data if other signals are necessary. Bug: 205342008 Test: manual - run sample app. Change-Id: I7b66a45be8fcbb1a66d9aa8eab235d83877526ea --- .../window/common/CommonFoldingFeature.java | 60 +++++++++-- .../DeviceStateManagerFoldingFeatureProducer.java | 111 +++++++++++++++++++++ .../common/DeviceStateManagerPostureProducer.java | 93 ----------------- .../ResourceConfigDisplayFeatureProducer.java | 74 -------------- .../common/SettingsDevicePostureProducer.java | 96 ------------------ .../common/SettingsDisplayFeatureProducer.java | 30 ++---- .../layout/WindowLayoutComponentImpl.java | 74 ++++---------- .../androidx/window/sidecar/SampleSidecarImpl.java | 61 ++++------- 8 files changed, 207 insertions(+), 392 deletions(-) create mode 100644 libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java delete mode 100644 libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerPostureProducer.java delete mode 100644 libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java delete mode 100644 libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDevicePostureProducer.java (limited to 'libs') diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java index a0c66fa1ff76..624d130e8698 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/CommonFoldingFeature.java @@ -21,11 +21,14 @@ import static androidx.window.util.ExtensionHelper.isZero; import android.annotation.IntDef; import android.annotation.Nullable; import android.graphics.Rect; +import android.util.Log; import androidx.annotation.NonNull; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -37,6 +40,10 @@ import java.util.regex.Pattern; */ public final class CommonFoldingFeature { + private static final boolean DEBUG = false; + + public static final String TAG = CommonFoldingFeature.class.getSimpleName(); + /** * A common type to represent a hinge where the screen is continuous. */ @@ -53,6 +60,12 @@ public final class CommonFoldingFeature { public @interface Type { } + /** + * A common state to represent when the state is not known. One example is if the device is + * closed. We do not emit this value for developers but is useful for implementation reasons. + */ + public static final int COMMON_STATE_UNKNOWN = -1; + /** * A common state to represent a FLAT hinge. This is needed because the definitions in Sidecar * and Extensions do not match exactly. @@ -81,15 +94,49 @@ public final class CommonFoldingFeature { private static final String PATTERN_STATE_FLAT = "flat"; private static final String PATTERN_STATE_HALF_OPENED = "half-opened"; + /** + * Parse a {@link List} of {@link CommonFoldingFeature} from a {@link String}. + * @param value a {@link String} representation of multiple {@link CommonFoldingFeature} + * separated by a ":". + * @param hingeState a global fallback value for a {@link CommonFoldingFeature} if one is not + * specified in the input. + * @throws IllegalArgumentException if the provided string is improperly formatted or could not + * otherwise be parsed. + * @see #FEATURE_PATTERN + * @return {@link List} of {@link CommonFoldingFeature}. + */ + static List parseListFromString(@NonNull String value, + @State int hingeState) { + List features = new ArrayList<>(); + String[] featureStrings = value.split(";"); + for (String featureString : featureStrings) { + CommonFoldingFeature feature; + try { + feature = CommonFoldingFeature.parseFromString(featureString, hingeState); + } catch (IllegalArgumentException e) { + if (DEBUG) { + Log.w(TAG, "Failed to parse display feature: " + featureString, e); + } + continue; + } + features.add(feature); + } + return features; + } + /** * Parses a display feature from a string. * + * @param string A {@link String} representation of a {@link CommonFoldingFeature}. + * @param hingeState A fallback value for the {@link State} if it is not specified in the input. * @throws IllegalArgumentException if the provided string is improperly formatted or could not * otherwise be parsed. + * @return {@link CommonFoldingFeature} represented by the {@link String} value. * @see #FEATURE_PATTERN */ @NonNull - static CommonFoldingFeature parseFromString(@NonNull String string) { + private static CommonFoldingFeature parseFromString(@NonNull String string, + @State int hingeState) { Matcher featureMatcher = FEATURE_PATTERN.matcher(string); if (!featureMatcher.matches()) { throw new IllegalArgumentException("Malformed feature description format: " + string); @@ -120,7 +167,7 @@ public final class CommonFoldingFeature { } String stateString = featureMatcher.group(6); stateString = stateString == null ? "" : stateString; - Integer state; + final int state; switch (stateString) { case PATTERN_STATE_FLAT: state = COMMON_STATE_FLAT; @@ -129,7 +176,7 @@ public final class CommonFoldingFeature { state = COMMON_STATE_HALF_OPENED; break; default: - state = null; + state = hingeState; break; } return new CommonFoldingFeature(type, state, featureRect); @@ -140,11 +187,11 @@ public final class CommonFoldingFeature { private final int mType; @Nullable - private final Integer mState; + private final int mState; @NonNull private final Rect mRect; - CommonFoldingFeature(int type, @Nullable Integer state, @NonNull Rect rect) { + CommonFoldingFeature(int type, int state, @NonNull Rect rect) { assertValidState(state); this.mType = type; this.mState = state; @@ -162,9 +209,8 @@ public final class CommonFoldingFeature { } /** Returns the state of the feature, or {@code null} if the feature has no state. */ - @Nullable @State - public Integer getState() { + public int getState() { return mState; } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java new file mode 100644 index 000000000000..6987401525b4 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerFoldingFeatureProducer.java @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2021 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.common; + +import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; + +import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_UNKNOWN; +import static androidx.window.common.CommonFoldingFeature.parseListFromString; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.hardware.devicestate.DeviceStateManager; +import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback; +import android.text.TextUtils; +import android.util.Log; +import android.util.SparseIntArray; + +import androidx.window.util.BaseDataProducer; + +import com.android.internal.R; + +import java.util.List; +import java.util.Optional; + +/** + * An implementation of {@link androidx.window.util.DataProducer} that returns the device's posture + * by mapping the state returned from {@link DeviceStateManager} to values provided in the resources + * config at {@link R.array#config_device_state_postures}. + */ +public final class DeviceStateManagerFoldingFeatureProducer extends + BaseDataProducer> { + private static final String TAG = + DeviceStateManagerFoldingFeatureProducer.class.getSimpleName(); + private static final boolean DEBUG = false; + + private final Context mContext; + private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray(); + + private int mCurrentDeviceState = INVALID_DEVICE_STATE; + + private final DeviceStateCallback mDeviceStateCallback = (state) -> { + mCurrentDeviceState = state; + notifyDataChanged(); + }; + + public DeviceStateManagerFoldingFeatureProducer(@NonNull Context context) { + mContext = context; + String[] deviceStatePosturePairs = context.getResources() + .getStringArray(R.array.config_device_state_postures); + for (String deviceStatePosturePair : deviceStatePosturePairs) { + String[] deviceStatePostureMapping = deviceStatePosturePair.split(":"); + if (deviceStatePostureMapping.length != 2) { + if (DEBUG) { + Log.e(TAG, "Malformed device state posture pair: " + deviceStatePosturePair); + } + continue; + } + + int deviceState; + int posture; + try { + deviceState = Integer.parseInt(deviceStatePostureMapping[0]); + posture = Integer.parseInt(deviceStatePostureMapping[1]); + } catch (NumberFormatException e) { + if (DEBUG) { + Log.e(TAG, "Failed to parse device state or posture: " + deviceStatePosturePair, + e); + } + continue; + } + + mDeviceStateToPostureMap.put(deviceState, posture); + } + + if (mDeviceStateToPostureMap.size() > 0) { + context.getSystemService(DeviceStateManager.class) + .registerCallback(context.getMainExecutor(), mDeviceStateCallback); + } + } + + @Override + @Nullable + public Optional> getData() { + final int globalHingeState = globalHingeState(); + String displayFeaturesString = mContext.getResources().getString( + R.string.config_display_features); + if (TextUtils.isEmpty(displayFeaturesString)) { + return Optional.empty(); + } + return Optional.of(parseListFromString(displayFeaturesString, globalHingeState)); + } + + private int globalHingeState() { + return mDeviceStateToPostureMap.get(mCurrentDeviceState, COMMON_STATE_UNKNOWN); + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerPostureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerPostureProducer.java deleted file mode 100644 index fa9a5a8b7a1b..000000000000 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/DeviceStateManagerPostureProducer.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2021 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.common; - -import static android.hardware.devicestate.DeviceStateManager.INVALID_DEVICE_STATE; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.hardware.devicestate.DeviceStateManager; -import android.hardware.devicestate.DeviceStateManager.DeviceStateCallback; -import android.util.Log; -import android.util.SparseIntArray; - -import androidx.window.util.BaseDataProducer; - -import com.android.internal.R; - -import java.util.Optional; - -/** - * An implementation of {@link androidx.window.util.DataProducer} that returns the device's posture - * by mapping the state returned from {@link DeviceStateManager} to values provided in the resources - * config at {@link R.array#config_device_state_postures}. - */ -public final class DeviceStateManagerPostureProducer extends BaseDataProducer { - private static final String TAG = "ConfigDevicePostureProducer"; - private static final boolean DEBUG = false; - - private final SparseIntArray mDeviceStateToPostureMap = new SparseIntArray(); - - private int mCurrentDeviceState = INVALID_DEVICE_STATE; - - private final DeviceStateCallback mDeviceStateCallback = (state) -> { - mCurrentDeviceState = state; - notifyDataChanged(); - }; - - public DeviceStateManagerPostureProducer(@NonNull Context context) { - String[] deviceStatePosturePairs = context.getResources() - .getStringArray(R.array.config_device_state_postures); - for (String deviceStatePosturePair : deviceStatePosturePairs) { - String[] deviceStatePostureMapping = deviceStatePosturePair.split(":"); - if (deviceStatePostureMapping.length != 2) { - if (DEBUG) { - Log.e(TAG, "Malformed device state posture pair: " + deviceStatePosturePair); - } - continue; - } - - int deviceState; - int posture; - try { - deviceState = Integer.parseInt(deviceStatePostureMapping[0]); - posture = Integer.parseInt(deviceStatePostureMapping[1]); - } catch (NumberFormatException e) { - if (DEBUG) { - Log.e(TAG, "Failed to parse device state or posture: " + deviceStatePosturePair, - e); - } - continue; - } - - mDeviceStateToPostureMap.put(deviceState, posture); - } - - if (mDeviceStateToPostureMap.size() > 0) { - context.getSystemService(DeviceStateManager.class) - .registerCallback(context.getMainExecutor(), mDeviceStateCallback); - } - } - - @Override - @Nullable - public Optional getData() { - final int posture = mDeviceStateToPostureMap.get(mCurrentDeviceState, -1); - return posture != -1 ? Optional.of(posture) : Optional.empty(); - } -} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java deleted file mode 100644 index a48d3ad797e6..000000000000 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/ResourceConfigDisplayFeatureProducer.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2021 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.common; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.Context; -import android.text.TextUtils; -import android.util.Log; - -import androidx.window.util.BaseDataProducer; - -import com.android.internal.R; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -/** - * Implementation of {@link androidx.window.util.DataProducer} that produces - * {@link CommonFoldingFeature} parsed from a string stored in the resources config at - * {@link R.string#config_display_features}. - */ -public final class ResourceConfigDisplayFeatureProducer extends - BaseDataProducer> { - private static final boolean DEBUG = false; - private static final String TAG = "ResourceConfigDisplayFeatureProducer"; - - private final Context mContext; - - public ResourceConfigDisplayFeatureProducer(@NonNull Context context) { - mContext = context; - } - - @Override - @Nullable - public Optional> getData() { - String displayFeaturesString = mContext.getResources().getString( - R.string.config_display_features); - if (TextUtils.isEmpty(displayFeaturesString)) { - return Optional.empty(); - } - - List features = new ArrayList<>(); - String[] featureStrings = displayFeaturesString.split(";"); - for (String featureString : featureStrings) { - CommonFoldingFeature feature; - try { - feature = CommonFoldingFeature.parseFromString(featureString); - } catch (IllegalArgumentException e) { - if (DEBUG) { - Log.w(TAG, "Failed to parse display feature: " + featureString, e); - } - continue; - } - features.add(feature); - } - return Optional.of(features); - } -} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDevicePostureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDevicePostureProducer.java deleted file mode 100644 index 2026df3fa979..000000000000 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDevicePostureProducer.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2021 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.common; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.content.ContentResolver; -import android.content.Context; -import android.database.ContentObserver; -import android.net.Uri; -import android.os.Handler; -import android.os.Looper; -import android.provider.Settings; - -import androidx.window.util.BaseDataProducer; - -import java.util.Optional; - -/** - * Implementation of {@link androidx.window.util.DataProducer} that provides the device posture - * as an {@link Integer} from a value stored in {@link Settings}. - */ -public final class SettingsDevicePostureProducer extends BaseDataProducer { - private static final String DEVICE_POSTURE = "device_posture"; - - private final Uri mDevicePostureUri = - Settings.Global.getUriFor(DEVICE_POSTURE); - - private final ContentResolver mResolver; - private final ContentObserver mObserver; - private boolean mRegisteredObservers; - - public SettingsDevicePostureProducer(@NonNull Context context) { - mResolver = context.getContentResolver(); - mObserver = new SettingsObserver(); - } - - @Override - @Nullable - public Optional getData() { - int posture = Settings.Global.getInt(mResolver, DEVICE_POSTURE, -1); - return posture == -1 ? Optional.empty() : Optional.of(posture); - } - - /** - * Registers settings observers, if needed. When settings observers are registered for this - * producer callbacks for changes in data will be triggered. - */ - public void registerObserversIfNeeded() { - if (mRegisteredObservers) { - return; - } - mRegisteredObservers = true; - mResolver.registerContentObserver(mDevicePostureUri, false /* notifyForDescendants */, - mObserver /* ContentObserver */); - } - - /** - * Unregisters settings observers, if needed. When settings observers are unregistered for this - * producer callbacks for changes in data will not be triggered. - */ - public void unregisterObserversIfNeeded() { - if (!mRegisteredObservers) { - return; - } - mRegisteredObservers = false; - mResolver.unregisterContentObserver(mObserver); - } - - private final class SettingsObserver extends ContentObserver { - SettingsObserver() { - super(new Handler(Looper.getMainLooper())); - } - - @Override - public void onChange(boolean selfChange, Uri uri) { - if (mDevicePostureUri.equals(uri)) { - notifyDataChanged(); - } - } - } -} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java b/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java index f3d6150f11d1..e9d213e06fa9 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/SettingsDisplayFeatureProducer.java @@ -16,8 +16,10 @@ package androidx.window.common; +import static androidx.window.common.CommonFoldingFeature.COMMON_STATE_UNKNOWN; +import static androidx.window.common.CommonFoldingFeature.parseListFromString; + import android.annotation.NonNull; -import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Context; import android.database.ContentObserver; @@ -26,11 +28,10 @@ import android.os.Handler; import android.os.Looper; import android.provider.Settings; import android.text.TextUtils; -import android.util.Log; import androidx.window.util.BaseDataProducer; -import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -40,8 +41,6 @@ import java.util.Optional; */ public final class SettingsDisplayFeatureProducer extends BaseDataProducer> { - private static final boolean DEBUG = false; - private static final String TAG = "SettingsDisplayFeatureProducer"; private static final String DISPLAY_FEATURES = "display_features"; private final Uri mDisplayFeaturesUri = @@ -57,32 +56,17 @@ public final class SettingsDisplayFeatureProducer } @Override - @Nullable + @NonNull public Optional> getData() { String displayFeaturesString = Settings.Global.getString(mResolver, DISPLAY_FEATURES); if (displayFeaturesString == null) { return Optional.empty(); } - List features = new ArrayList<>(); if (TextUtils.isEmpty(displayFeaturesString)) { - return Optional.of(features); - } - String[] featureStrings = displayFeaturesString.split(";"); - - for (String featureString : featureStrings) { - CommonFoldingFeature feature; - try { - feature = CommonFoldingFeature.parseFromString(featureString); - } catch (IllegalArgumentException e) { - if (DEBUG) { - Log.w(TAG, "Failed to parse display feature: " + featureString, e); - } - continue; - } - features.add(feature); + return Optional.of(Collections.emptyList()); } - return Optional.of(features); + return Optional.of(parseListFromString(displayFeaturesString, COMMON_STATE_UNKNOWN)); } /** 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 b4b75b4e9ca5..df059bb64dc7 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java @@ -31,9 +31,7 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.window.common.CommonFoldingFeature; -import androidx.window.common.DeviceStateManagerPostureProducer; -import androidx.window.common.ResourceConfigDisplayFeatureProducer; -import androidx.window.common.SettingsDevicePostureProducer; +import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; import androidx.window.common.SettingsDisplayFeatureProducer; import androidx.window.util.DataProducer; import androidx.window.util.PriorityDataProducer; @@ -60,27 +58,16 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { private final Map> mWindowLayoutChangeListeners = new HashMap<>(); - private final SettingsDevicePostureProducer mSettingsDevicePostureProducer; - private final DataProducer mDevicePostureProducer; - private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer; - private final DataProducer> mDisplayFeatureProducer; + private final DataProducer> mFoldingFeatureProducer; public WindowLayoutComponentImpl(Context context) { - mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context); - mDevicePostureProducer = new PriorityDataProducer<>(List.of( - mSettingsDevicePostureProducer, - new DeviceStateManagerPostureProducer(context) - )); - mSettingsDisplayFeatureProducer = new SettingsDisplayFeatureProducer(context); - mDisplayFeatureProducer = new PriorityDataProducer<>(List.of( + mFoldingFeatureProducer = new PriorityDataProducer<>(List.of( mSettingsDisplayFeatureProducer, - new ResourceConfigDisplayFeatureProducer(context) + new DeviceStateManagerFoldingFeatureProducer(context) )); - - mDevicePostureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); - mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); + mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); } /** @@ -121,25 +108,6 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { return !mWindowLayoutChangeListeners.isEmpty(); } - /** - * Calculate the {@link CommonFoldingFeature.State} from the feature or the device posture - * producer. - * If the given {@link CommonFoldingFeature.State} is not valid then {@code null} will be - * returned. The {@link FoldingFeature} should be ignored in the case of an invalid - * {@link CommonFoldingFeature.State}. - * - * @param feature a {@link CommonFoldingFeature} to provide the feature state if present. - * @return {@link CommonFoldingFeature.State} of the hinge if present or the state from the - * posture produce if present. - */ - @Nullable - private Integer getFeatureState(CommonFoldingFeature feature) { - Integer featureState = feature.getState(); - Optional posture = mDevicePostureProducer.getData(); - Integer state = featureState == null ? posture.orElse(null) : featureState; - return convertToExtensionState(state); - } - /** * A convenience method to translate from the common feature state to the extensions feature * state. More specifically, translates from {@link CommonFoldingFeature.State} to @@ -152,10 +120,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { * {@link CommonFoldingFeature.State} and {@code null} otherwise. */ @Nullable - private Integer convertToExtensionState(@Nullable Integer state) { - if (state == null) { // The null check avoids a NullPointerException. - return null; - } else if (state == COMMON_STATE_FLAT) { + private Integer convertToExtensionState(int state) { + if (state == COMMON_STATE_FLAT) { return FoldingFeature.STATE_FLAT; } else if (state == COMMON_STATE_HALF_OPENED) { return FoldingFeature.STATE_HALF_OPENED; @@ -173,8 +139,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { @NonNull private WindowLayoutInfo getWindowLayoutInfo(@NonNull Activity activity) { - List displayFeatures = - getDisplayFeatures(activity); + List displayFeatures = getDisplayFeatures(activity); return new WindowLayoutInfo(displayFeatures); } @@ -184,13 +149,12 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { * {@link CommonFoldingFeature} is not valid then it will be omitted. * * For a {@link FoldingFeature} the bounds are localized into the {@link Activity} window - * coordinate space and the state is calculated either from - * {@link CommonFoldingFeature#getState()} or {@link #mDisplayFeatureProducer}. The state from - * {@link #mDisplayFeatureProducer} may not be valid since {@link #mDisplayFeatureProducer} is - * a general state controller. If the state is not valid, the {@link FoldingFeature} is omitted - * from the {@link List} of {@link DisplayFeature}. If the bounds are not valid, constructing a - * {@link FoldingFeature} will throw an {@link IllegalArgumentException} since this can cause - * negative UI effects down stream. + * coordinate space and the state is calculated from {@link CommonFoldingFeature#getState()}. + * The state from {@link #mFoldingFeatureProducer} may not be valid since + * {@link #mFoldingFeatureProducer} is a general state controller. If the state is not valid, + * the {@link FoldingFeature} is omitted from the {@link List} of {@link DisplayFeature}. If + * the bounds are not valid, constructing a {@link FoldingFeature} will throw an + * {@link IllegalArgumentException} since this can cause negative UI effects down stream. * * @param activity a proxy for the {@link android.view.Window} that contains the * {@link DisplayFeature}. @@ -211,11 +175,10 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { return features; } - Optional> storedFeatures = mDisplayFeatureProducer.getData(); + Optional> storedFeatures = mFoldingFeatureProducer.getData(); if (storedFeatures.isPresent()) { - for (CommonFoldingFeature baseFeature : storedFeatures.get()) { - Integer state = getFeatureState(baseFeature); + Integer state = convertToExtensionState(baseFeature.getState()); if (state == null) { continue; } @@ -223,8 +186,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { rotateRectToDisplayRotation(displayId, featureRect); transformToWindowSpaceRect(activity, featureRect); - features.add(new FoldingFeature(featureRect, baseFeature.getType(), - getFeatureState(baseFeature))); + features.add(new FoldingFeature(featureRect, baseFeature.getType(), state)); } } return features; @@ -232,10 +194,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { private void updateRegistrations() { if (hasListeners()) { - mSettingsDevicePostureProducer.registerObserversIfNeeded(); mSettingsDisplayFeatureProducer.registerObserversIfNeeded(); } else { - mSettingsDevicePostureProducer.unregisterObserversIfNeeded(); mSettingsDisplayFeatureProducer.unregisterObserversIfNeeded(); } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java index 65e2a5e34f19..458bb77b047f 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java @@ -30,9 +30,7 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.window.common.CommonFoldingFeature; -import androidx.window.common.DeviceStateManagerPostureProducer; -import androidx.window.common.ResourceConfigDisplayFeatureProducer; -import androidx.window.common.SettingsDevicePostureProducer; +import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; import androidx.window.common.SettingsDisplayFeatureProducer; import androidx.window.util.DataProducer; import androidx.window.util.PriorityDataProducer; @@ -48,36 +46,23 @@ import java.util.Optional; */ class SampleSidecarImpl extends StubSidecar { private static final String TAG = "SampleSidecar"; - private static final boolean DEBUG = false; - private final SettingsDevicePostureProducer mSettingsDevicePostureProducer; - private final DataProducer mDevicePostureProducer; + private final DataProducer> mFoldingFeatureProducer; - private final SettingsDisplayFeatureProducer mSettingsDisplayFeatureProducer; - private final DataProducer> mDisplayFeatureProducer; + private final SettingsDisplayFeatureProducer mSettingsFoldingFeatureProducer; SampleSidecarImpl(Context context) { - mSettingsDevicePostureProducer = new SettingsDevicePostureProducer(context); - mDevicePostureProducer = new PriorityDataProducer<>(List.of( - mSettingsDevicePostureProducer, - new DeviceStateManagerPostureProducer(context) + mSettingsFoldingFeatureProducer = new SettingsDisplayFeatureProducer(context); + mFoldingFeatureProducer = new PriorityDataProducer<>(List.of( + mSettingsFoldingFeatureProducer, + new DeviceStateManagerFoldingFeatureProducer(context) )); - mSettingsDisplayFeatureProducer = new SettingsDisplayFeatureProducer(context); - mDisplayFeatureProducer = new PriorityDataProducer<>(List.of( - mSettingsDisplayFeatureProducer, - new ResourceConfigDisplayFeatureProducer(context) - )); - - mDevicePostureProducer.addDataChangedCallback(this::onDevicePostureChanged); - mDisplayFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); - } - - private void onDevicePostureChanged() { - updateDeviceState(getDeviceState()); + mFoldingFeatureProducer.addDataChangedCallback(this::onDisplayFeaturesChanged); } private void onDisplayFeaturesChanged() { + updateDeviceState(getDeviceState()); for (IBinder windowToken : getWindowsListeningForLayoutChanges()) { SidecarWindowLayoutInfo newLayout = getWindowLayoutInfo(windowToken); updateWindowLayout(windowToken, newLayout); @@ -87,23 +72,17 @@ class SampleSidecarImpl extends StubSidecar { @NonNull @Override public SidecarDeviceState getDeviceState() { - Optional posture = mDevicePostureProducer.getData(); - SidecarDeviceState deviceState = new SidecarDeviceState(); - deviceState.posture = posture.orElse(deviceStateFromFeature()); + deviceState.posture = deviceStateFromFeature(); return deviceState; } private int deviceStateFromFeature() { - List storedFeatures = mDisplayFeatureProducer.getData() + List storedFeatures = mFoldingFeatureProducer.getData() .orElse(Collections.emptyList()); for (int i = 0; i < storedFeatures.size(); i++) { CommonFoldingFeature feature = storedFeatures.get(i); - final int state = feature.getState() == null ? -1 : feature.getState(); - if (DEBUG && feature.getState() == null) { - Log.d(TAG, "feature#getState was null for DisplayFeature: " + feature); - } - + final int state = feature.getState(); switch (state) { case CommonFoldingFeature.COMMON_STATE_FLAT: return SidecarDeviceState.POSTURE_OPENED; @@ -127,20 +106,20 @@ class SampleSidecarImpl extends StubSidecar { } private List getDisplayFeatures(@NonNull Activity activity) { - List features = new ArrayList<>(); int displayId = activity.getDisplay().getDisplayId(); if (displayId != DEFAULT_DISPLAY) { Log.w(TAG, "This sample doesn't support display features on secondary displays"); - return features; + return Collections.emptyList(); } if (activity.isInMultiWindowMode()) { // 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; + return Collections.emptyList(); } - Optional> storedFeatures = mDisplayFeatureProducer.getData(); + Optional> storedFeatures = mFoldingFeatureProducer.getData(); + List features = new ArrayList<>(); if (storedFeatures.isPresent()) { for (CommonFoldingFeature baseFeature : storedFeatures.get()) { SidecarDisplayFeature feature = new SidecarDisplayFeature(); @@ -152,17 +131,15 @@ class SampleSidecarImpl extends StubSidecar { features.add(feature); } } - return features; + return Collections.unmodifiableList(features); } @Override protected void onListenersChanged() { if (hasListeners()) { - mSettingsDevicePostureProducer.registerObserversIfNeeded(); - mSettingsDisplayFeatureProducer.registerObserversIfNeeded(); + mSettingsFoldingFeatureProducer.registerObserversIfNeeded(); } else { - mSettingsDevicePostureProducer.unregisterObserversIfNeeded(); - mSettingsDisplayFeatureProducer.unregisterObserversIfNeeded(); + mSettingsFoldingFeatureProducer.unregisterObserversIfNeeded(); } } } -- cgit v1.2.3-59-g8ed1b From 396a7965c375ca186749b5ca568db7857ba95d70 Mon Sep 17 00:00:00 2001 From: Diego Vela Date: Thu, 2 Dec 2021 10:01:15 -0800 Subject: DO NOT MERGE Add LifecycleCallback adapter. Add a lifecycle callback adapter to remove empty code blocks. Bug: 205342008 Test: n/a small code clean up. Change-Id: I5454fbcb24c26079369fd20e519c809b00c261ae --- .../common/EmptyLifecycleCallbacksAdapter.java | 55 ++++++++++++++++++++++ .../extensions/embedding/SplitController.java | 33 ++----------- 2 files changed, 58 insertions(+), 30 deletions(-) create mode 100644 libs/WindowManager/Jetpack/src/androidx/window/common/EmptyLifecycleCallbacksAdapter.java (limited to 'libs') diff --git a/libs/WindowManager/Jetpack/src/androidx/window/common/EmptyLifecycleCallbacksAdapter.java b/libs/WindowManager/Jetpack/src/androidx/window/common/EmptyLifecycleCallbacksAdapter.java new file mode 100644 index 000000000000..f2e403b4f792 --- /dev/null +++ b/libs/WindowManager/Jetpack/src/androidx/window/common/EmptyLifecycleCallbacksAdapter.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2021 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.common; + +import android.app.Activity; +import android.app.Application; +import android.os.Bundle; + +/** + * An empty implementation of {@link Application.ActivityLifecycleCallbacks} derived classes can + * implement the methods necessary. + */ +public class EmptyLifecycleCallbacksAdapter implements Application.ActivityLifecycleCallbacks { + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + } + + @Override + public void onActivityStarted(Activity activity) { + } + + @Override + public void onActivityResumed(Activity activity) { + } + + @Override + public void onActivityPaused(Activity activity) { + } + + @Override + public void onActivityStopped(Activity activity) { + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + } + + @Override + public void onActivityDestroyed(Activity activity) { + } +} diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 8f368c2bee22..be8b35d80326 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -28,7 +28,6 @@ import android.app.Activity; import android.app.ActivityClient; import android.app.ActivityOptions; import android.app.ActivityThread; -import android.app.Application.ActivityLifecycleCallbacks; import android.app.Instrumentation; import android.content.Context; import android.content.Intent; @@ -41,6 +40,8 @@ import android.os.Looper; import android.window.TaskFragmentInfo; import android.window.WindowContainerTransaction; +import androidx.window.common.EmptyLifecycleCallbacksAdapter; + import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -759,11 +760,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen return shouldRetainAssociatedContainer(finishingContainer, associatedContainer); } - private final class LifecycleCallbacks implements ActivityLifecycleCallbacks { - - @Override - public void onActivityCreated(Activity activity, Bundle savedInstanceState) { - } + private final class LifecycleCallbacks extends EmptyLifecycleCallbacksAdapter { @Override public void onActivityPostCreated(Activity activity, Bundle savedInstanceState) { @@ -774,30 +771,6 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen SplitController.this.onActivityCreated(activity); } - @Override - public void onActivityStarted(Activity activity) { - } - - @Override - public void onActivityResumed(Activity activity) { - } - - @Override - public void onActivityPaused(Activity activity) { - } - - @Override - public void onActivityStopped(Activity activity) { - } - - @Override - public void onActivitySaveInstanceState(Activity activity, Bundle outState) { - } - - @Override - public void onActivityDestroyed(Activity activity) { - } - @Override public void onActivityConfigurationChanged(Activity activity) { SplitController.this.onActivityConfigurationChanged(activity); -- cgit v1.2.3-59-g8ed1b From 6c39d4fb3c851b024498c271d5660ac773d4f2fc Mon Sep 17 00:00:00 2001 From: Diego Vela Date: Thu, 2 Dec 2021 10:39:05 -0800 Subject: DO NOT MERGE Listen for config changes when updating features. Listen for configuration changes to push out folding feature changes. Fixes a bug where the reference implementation would not emit the correct folding feature if a configuration changed. The effect was that you would see the folding feature for portait while in landscape or vice-versa. Maintains parity with Sidecar providing updates for Activities that do not handle configuration changes. Bug: 205342008, 206697963 Test: Manual - Open the sample app and use the application that && handles configuration changes. && Expect the folding feature to match the physical orientation Test: Manual - open an app that handles PiP and folding features && Flattend the device and put app into PiP && Fold device and put PiP app in full screen. && Expect the app to react to fold. Test: atest CtsWindowManagerJetpackTestCases Change-Id: I726827c9f1482ddd0b55a6839e34660deb340767 --- .../layout/WindowLayoutComponentImpl.java | 46 ++++++++++++++++++++-- .../androidx/window/sidecar/SampleSidecarImpl.java | 27 +++++++++++++ .../src/androidx/window/sidecar/StubSidecar.java | 2 +- 3 files changed, 70 insertions(+), 5 deletions(-) (limited to 'libs') 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 df059bb64dc7..ee8cb48e3c4c 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/layout/WindowLayoutComponentImpl.java @@ -25,13 +25,17 @@ import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect; import android.annotation.Nullable; import android.app.Activity; +import android.app.Application; import android.content.Context; import android.graphics.Rect; +import android.os.Bundle; +import android.os.IBinder; import android.util.Log; import androidx.annotation.NonNull; import androidx.window.common.CommonFoldingFeature; import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; +import androidx.window.common.EmptyLifecycleCallbacksAdapter; import androidx.window.common.SettingsDisplayFeatureProducer; import androidx.window.util.DataProducer; import androidx.window.util.PriorityDataProducer; @@ -62,6 +66,8 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { private final DataProducer> mFoldingFeatureProducer; public WindowLayoutComponentImpl(Context context) { + ((Application) context.getApplicationContext()) + .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged()); mSettingsDisplayFeatureProducer = new SettingsDisplayFeatureProducer(context); mFoldingFeatureProducer = new PriorityDataProducer<>(List.of( mSettingsDisplayFeatureProducer, @@ -72,6 +78,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { /** * Adds a listener interested in receiving updates to {@link WindowLayoutInfo} + * * @param activity hosting a {@link android.view.Window} * @param consumer interested in receiving updates to {@link WindowLayoutInfo} */ @@ -83,6 +90,7 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { /** * Removes a listener no longer interested in receiving updates. + * * @param consumer no longer interested in receiving updates to {@link WindowLayoutInfo} */ public void removeWindowLayoutInfoListener( @@ -104,6 +112,16 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { return mWindowLayoutChangeListeners.keySet(); } + @NonNull + private Boolean isListeningForLayoutChanges(IBinder token) { + for (Activity activity: getActivitiesListeningForLayoutChanges()) { + if (token.equals(activity.getWindow().getAttributes().token)) { + return true; + } + } + return false; + } + protected boolean hasListeners() { return !mWindowLayoutChangeListeners.isEmpty(); } @@ -115,9 +133,9 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { * possible to translate, then we will return a {@code null} value. * * @param state if it matches a value in {@link CommonFoldingFeature.State}, {@code null} - * otherwise. @return a {@link FoldingFeature.STATE_FLAT} or - * {@link FoldingFeature.STATE_HALF_OPENED} if the given state matches a value in - * {@link CommonFoldingFeature.State} and {@code null} otherwise. + * otherwise. @return a {@link FoldingFeature.STATE_FLAT} or + * {@link FoldingFeature.STATE_HALF_OPENED} if the given state matches a value in + * {@link CommonFoldingFeature.State} and {@code null} otherwise. */ @Nullable private Integer convertToExtensionState(int state) { @@ -198,7 +216,27 @@ public class WindowLayoutComponentImpl implements WindowLayoutComponent { } else { mSettingsDisplayFeatureProducer.unregisterObserversIfNeeded(); } - onDisplayFeaturesChanged(); } + + private final class NotifyOnConfigurationChanged extends EmptyLifecycleCallbacksAdapter { + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + super.onActivityCreated(activity, savedInstanceState); + onDisplayFeaturesChangedIfListening(activity); + } + + @Override + public void onActivityConfigurationChanged(Activity activity) { + super.onActivityConfigurationChanged(activity); + onDisplayFeaturesChangedIfListening(activity); + } + + private void onDisplayFeaturesChangedIfListening(Activity activity) { + IBinder token = activity.getWindow().getAttributes().token; + if (token == null || isListeningForLayoutChanges(token)) { + onDisplayFeaturesChanged(); + } + } + } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java index 458bb77b047f..c7b709347060 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/SampleSidecarImpl.java @@ -23,14 +23,17 @@ import static androidx.window.util.ExtensionHelper.transformToWindowSpaceRect; import android.app.Activity; import android.app.ActivityThread; +import android.app.Application; import android.content.Context; import android.graphics.Rect; +import android.os.Bundle; import android.os.IBinder; import android.util.Log; import androidx.annotation.NonNull; import androidx.window.common.CommonFoldingFeature; import androidx.window.common.DeviceStateManagerFoldingFeatureProducer; +import androidx.window.common.EmptyLifecycleCallbacksAdapter; import androidx.window.common.SettingsDisplayFeatureProducer; import androidx.window.util.DataProducer; import androidx.window.util.PriorityDataProducer; @@ -52,6 +55,8 @@ class SampleSidecarImpl extends StubSidecar { private final SettingsDisplayFeatureProducer mSettingsFoldingFeatureProducer; SampleSidecarImpl(Context context) { + ((Application) context.getApplicationContext()) + .registerActivityLifecycleCallbacks(new NotifyOnConfigurationChanged()); mSettingsFoldingFeatureProducer = new SettingsDisplayFeatureProducer(context); mFoldingFeatureProducer = new PriorityDataProducer<>(List.of( mSettingsFoldingFeatureProducer, @@ -138,8 +143,30 @@ class SampleSidecarImpl extends StubSidecar { protected void onListenersChanged() { if (hasListeners()) { mSettingsFoldingFeatureProducer.registerObserversIfNeeded(); + onDisplayFeaturesChanged(); } else { mSettingsFoldingFeatureProducer.unregisterObserversIfNeeded(); } } + + private final class NotifyOnConfigurationChanged extends EmptyLifecycleCallbacksAdapter { + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + super.onActivityCreated(activity, savedInstanceState); + onDisplayFeaturesChangedForActivity(activity); + } + + @Override + public void onActivityConfigurationChanged(Activity activity) { + super.onActivityConfigurationChanged(activity); + onDisplayFeaturesChangedForActivity(activity); + } + + private void onDisplayFeaturesChangedForActivity(@NonNull Activity activity) { + IBinder token = activity.getWindow().getAttributes().token; + if (token == null || mWindowLayoutChangeListenerTokens.contains(token)) { + onDisplayFeaturesChanged(); + } + } + } } diff --git a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java index 199c37315c07..b9c808a6569b 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/sidecar/StubSidecar.java @@ -30,7 +30,7 @@ import java.util.Set; abstract class StubSidecar implements SidecarInterface { private SidecarCallback mSidecarCallback; - private final Set mWindowLayoutChangeListenerTokens = new HashSet<>(); + final Set mWindowLayoutChangeListenerTokens = new HashSet<>(); private boolean mDeviceStateChangeListenerRegistered; StubSidecar() { -- cgit v1.2.3-59-g8ed1b