diff options
| author | 2024-06-07 09:12:23 +0000 | |
|---|---|---|
| committer | 2024-06-07 09:12:23 +0000 | |
| commit | e1616d9b60cac83b8f3b7f57981e824a450670b7 (patch) | |
| tree | 01f01796fb3bd0e6f6685dcd86c831f0beb71868 | |
| parent | 93c46654f2612edf60fd0f626d073bf625a4a672 (diff) | |
| parent | 52ff494c5ebc922c26e7757c2886d370c42ef01f (diff) | |
Merge "[2/n] TransparentPolicyState definition" into main
3 files changed, 223 insertions, 136 deletions
diff --git a/services/core/java/com/android/server/wm/LetterboxUiController.java b/services/core/java/com/android/server/wm/LetterboxUiController.java index 88946a983473..ce71f81bff94 100644 --- a/services/core/java/com/android/server/wm/LetterboxUiController.java +++ b/services/core/java/com/android/server/wm/LetterboxUiController.java @@ -132,6 +132,7 @@ import java.io.PrintWriter; import java.util.Optional; import java.util.function.BooleanSupplier; import java.util.function.Consumer; +import java.util.function.Predicate; /** Controls behaviour of the letterbox UI for {@link mActivityRecord}. */ // TODO(b/185262487): Improve test coverage of this class. Parts of it are tested in @@ -244,7 +245,24 @@ final class LetterboxUiController { // to use it after since controller is only used in ActivityRecord. mActivityRecord = activityRecord; - mTransparentPolicy = new TransparentPolicy(activityRecord, mLetterboxConfiguration); + mTransparentPolicy = new TransparentPolicy(activityRecord, mLetterboxConfiguration, + new Predicate<ActivityRecord>() { + @Override + public boolean test(ActivityRecord opaqueActivity) { + if (opaqueActivity == null || opaqueActivity.isEmbedded()) { + // We skip letterboxing if the translucent activity doesn't have any + // opaque activities beneath or the activity below is embedded which + // never has letterbox. + mActivityRecord.recomputeConfiguration(); + return true; + } + if (mActivityRecord.getTask() == null || mActivityRecord.fillsParent() + || mActivityRecord.hasCompatDisplayInsetsWithoutInheritance()) { + return true; + } + return false; + } + }); PackageManager packageManager = wmService.mContext.getPackageManager(); final OptPropFactory optPropBuilder = new OptPropFactory(packageManager, @@ -322,7 +340,7 @@ final class LetterboxUiController { mLetterbox.destroy(); mLetterbox = null; } - mTransparentPolicy.destroy(); + mTransparentPolicy.stop(); } void onMovedToDisplay(int displayId) { @@ -1303,7 +1321,8 @@ final class LetterboxUiController { // Use screen resolved bounds which uses resolved bounds or size compat bounds // as activity bounds can sometimes be empty final Rect opaqueActivityBounds = hasInheritedLetterboxBehavior() - ? getTransparentPolicy().mFirstOpaqueActivityBeneath.getScreenResolvedBounds() + ? getTransparentPolicy().getTransparentPolicyState() + .mFirstOpaqueActivity.getScreenResolvedBounds() : mActivityRecord.getScreenResolvedBounds(); return mLetterboxConfiguration.getIsHorizontalReachabilityEnabled() && parentConfiguration.windowConfiguration.getWindowingMode() @@ -1341,7 +1360,8 @@ final class LetterboxUiController { // Use screen resolved bounds which uses resolved bounds or size compat bounds // as activity bounds can sometimes be empty final Rect opaqueActivityBounds = hasInheritedLetterboxBehavior() - ? getTransparentPolicy().mFirstOpaqueActivityBeneath.getScreenResolvedBounds() + ? getTransparentPolicy().getTransparentPolicyState() + .mFirstOpaqueActivity.getScreenResolvedBounds() : mActivityRecord.getScreenResolvedBounds(); return mLetterboxConfiguration.getIsVerticalReachabilityEnabled() && parentConfiguration.windowConfiguration.getWindowingMode() @@ -1758,7 +1778,7 @@ final class LetterboxUiController { * first opaque activity beneath. */ void updateInheritedLetterbox() { - mTransparentPolicy.updateInheritedLetterbox(); + mTransparentPolicy.start(); } /** diff --git a/services/core/java/com/android/server/wm/TransparentPolicy.java b/services/core/java/com/android/server/wm/TransparentPolicy.java index ffc476e0d029..f8a6fe1c825c 100644 --- a/services/core/java/com/android/server/wm/TransparentPolicy.java +++ b/services/core/java/com/android/server/wm/TransparentPolicy.java @@ -31,11 +31,10 @@ import android.annotation.Nullable; import android.content.res.Configuration; import android.graphics.Rect; -import com.android.internal.annotations.VisibleForTesting; - import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.function.BooleanSupplier; import java.util.function.Consumer; import java.util.function.Predicate; @@ -46,126 +45,75 @@ class TransparentPolicy { private static final String TAG = TAG_WITH_CLASS_NAME ? "TransparentPolicy" : TAG_ATM; - // Aspect ratio value to consider as undefined. - private static final float UNDEFINED_ASPECT_RATIO = 0f; - // The predicate used to find the first opaque not finishing activity below the potential // transparent activity. private static final Predicate<ActivityRecord> FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE = ActivityRecord::occludesParent; + // The predicate to check to skip the policy + @NonNull + private final Predicate<ActivityRecord> mSkipLetterboxPredicate; + // The ActivityRecord this policy relates to. private final ActivityRecord mActivityRecord; - // The LetterboxConfiguration - private final LetterboxConfiguration mLetterboxConfiguration; + // If transparent activity policy is enabled. + private final BooleanSupplier mIsTranslucentLetterboxingEnabledSupplier; // The list of observers for the destroy event of candidate opaque activities // when dealing with translucent activities. private final List<TransparentPolicy> mDestroyListeners = new ArrayList<>(); - // In case of transparent activities we might need to access the aspectRatio of the - // first opaque activity beneath. - private float mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO; - private float mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO; - - @Configuration.Orientation - private int mInheritedOrientation = ORIENTATION_UNDEFINED; - - // The app compat state for the opaque activity if any - private int mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN; - - // The CompatDisplayInsets of the opaque activity beneath the translucent one. - private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets; - - /* - * WindowContainerListener responsible to make translucent activities inherit - * constraints from the first opaque activity beneath them. It's null for not - * translucent activities. - */ - @Nullable - private WindowContainerListener mLetterboxConfigListener; - - - @Nullable - @VisibleForTesting - ActivityRecord mFirstOpaqueActivityBeneath; + // THe current state for the possible transparent activity + private final TransparentPolicyState mTransparentPolicyState; TransparentPolicy(@NonNull ActivityRecord activityRecord, - @NonNull LetterboxConfiguration letterboxConfiguration) { + @NonNull LetterboxConfiguration letterboxConfiguration, + @NonNull Predicate<ActivityRecord> skipLetterboxPredicate) { mActivityRecord = activityRecord; - mLetterboxConfiguration = letterboxConfiguration; + mIsTranslucentLetterboxingEnabledSupplier = () -> letterboxConfiguration + .isTranslucentLetterboxingEnabled(); + mSkipLetterboxPredicate = skipLetterboxPredicate; + mTransparentPolicyState = new TransparentPolicyState(activityRecord); } /** * Handles translucent activities letterboxing inheriting constraints from the * first opaque activity beneath. */ - void updateInheritedLetterbox() { - final WindowContainer<?> parent = mActivityRecord.getParent(); - if (parent == null) { + void start() { + if (!mIsTranslucentLetterboxingEnabledSupplier.getAsBoolean()) { return; } - if (!mLetterboxConfiguration.isTranslucentLetterboxingEnabled()) { + final WindowContainer<?> parent = mActivityRecord.getParent(); + if (parent == null) { return; } - if (mLetterboxConfigListener != null) { - mLetterboxConfigListener.onRemoved(); - clearInheritedConfig(); - } + mTransparentPolicyState.reset(); // In case mActivityRecord.hasCompatDisplayInsetsWithoutOverride() we don't apply the // opaque activity constraints because we're expecting the activity is already letterboxed. - mFirstOpaqueActivityBeneath = mActivityRecord.getTask().getActivity( + final ActivityRecord firstOpaqueActivity = mActivityRecord.getTask().getActivity( FIRST_OPAQUE_NOT_FINISHING_ACTIVITY_PREDICATE /* callback */, mActivityRecord /* boundary */, false /* includeBoundary */, true /* traverseTopToBottom */); - if (mFirstOpaqueActivityBeneath == null || mFirstOpaqueActivityBeneath.isEmbedded()) { - // We skip letterboxing if the translucent activity doesn't have any opaque - // activities beneath or the activity below is embedded which never has letterbox. - mActivityRecord.recomputeConfiguration(); - return; - } - if (mActivityRecord.getTask() == null || mActivityRecord.fillsParent() - || mActivityRecord.hasCompatDisplayInsetsWithoutInheritance()) { + // We check if we need for some reason to skip the policy gievn the specific first + // opaque activity + if (mSkipLetterboxPredicate.test(firstOpaqueActivity)) { return; } - mFirstOpaqueActivityBeneath.mLetterboxUiController.getTransparentPolicy() - .mDestroyListeners.add(this); - inheritConfiguration(mFirstOpaqueActivityBeneath); - mLetterboxConfigListener = WindowContainer.overrideConfigurationPropagation( - mActivityRecord, mFirstOpaqueActivityBeneath, - (opaqueConfig, transparentOverrideConfig) -> { - resetTranslucentOverrideConfig(transparentOverrideConfig); - final Rect parentBounds = parent.getWindowConfiguration().getBounds(); - final Rect bounds = transparentOverrideConfig.windowConfiguration.getBounds(); - final Rect letterboxBounds = opaqueConfig.windowConfiguration.getBounds(); - // We cannot use letterboxBounds directly here because the position relies on - // letterboxing. Using letterboxBounds directly, would produce a double offset. - bounds.set(parentBounds.left, parentBounds.top, - parentBounds.left + letterboxBounds.width(), - parentBounds.top + letterboxBounds.height()); - // We need to initialize appBounds to avoid NPE. The actual value will - // be set ahead when resolving the Configuration for the activity. - transparentOverrideConfig.windowConfiguration.setAppBounds(new Rect()); - inheritConfiguration(mFirstOpaqueActivityBeneath); - return transparentOverrideConfig; - }); - + mTransparentPolicyState.start(firstOpaqueActivity); } - void destroy() { + void stop() { for (int i = mDestroyListeners.size() - 1; i >= 0; i--) { - mDestroyListeners.get(i).updateInheritedLetterbox(); + mDestroyListeners.get(i).start(); } mDestroyListeners.clear(); - if (mLetterboxConfigListener != null) { - mLetterboxConfigListener.onRemoved(); - mLetterboxConfigListener = null; - } + mTransparentPolicyState.reset(); } boolean hasInheritedLetterboxBehavior() { - return mLetterboxConfigListener != null; + return mTransparentPolicyState.isRunning(); } /** @@ -184,28 +132,32 @@ class TransparentPolicy { } float getInheritedMinAspectRatio() { - return mInheritedMinAspectRatio; + return mTransparentPolicyState.getInheritedMinAspectRatio(); } float getInheritedMaxAspectRatio() { - return mInheritedMaxAspectRatio; + return mTransparentPolicyState.getInheritedMaxAspectRatio(); } int getInheritedAppCompatState() { - return mInheritedAppCompatState; + return mTransparentPolicyState.getInheritedAppCompatState(); } @Configuration.Orientation int getInheritedOrientation() { - return mInheritedOrientation; + return mTransparentPolicyState.getInheritedOrientation(); } ActivityRecord.CompatDisplayInsets getInheritedCompatDisplayInsets() { - return mInheritedCompatDisplayInsets; + return mTransparentPolicyState.getInheritedCompatDisplayInsets(); } void clearInheritedCompatDisplayInsets() { - mInheritedCompatDisplayInsets = null; + mTransparentPolicyState.clearInheritedCompatDisplayInsets(); + } + + TransparentPolicyState getTransparentPolicyState() { + return mTransparentPolicyState; } /** @@ -213,11 +165,7 @@ class TransparentPolicy { * activity beneath using the given consumer and returns {@code true}. */ boolean applyOnOpaqueActivityBelow(@NonNull Consumer<ActivityRecord> consumer) { - return findOpaqueNotFinishingActivityBelow() - .map(activityRecord -> { - consumer.accept(activityRecord); - return true; - }).orElse(false); + return mTransparentPolicyState.applyOnOpaqueActivityBelow(consumer); } /** @@ -225,10 +173,7 @@ class TransparentPolicy { * if it exists and the strategy is enabled. */ Optional<ActivityRecord> findOpaqueNotFinishingActivityBelow() { - if (!hasInheritedLetterboxBehavior() || mActivityRecord.getTask() == null) { - return Optional.empty(); - } - return Optional.ofNullable(mFirstOpaqueActivityBeneath); + return mTransparentPolicyState.findOpaqueNotFinishingActivityBelow(); } /** Resets the screen size related fields so they can be resolved by requested bounds later. */ @@ -244,33 +189,158 @@ class TransparentPolicy { } private void inheritConfiguration(ActivityRecord firstOpaque) { - // To avoid wrong behaviour, we're not forcing a specific aspect ratio to activities - // which are not already providing one (e.g. permission dialogs) and presumably also - // not resizable. - if (mActivityRecord.getMinAspectRatio() != UNDEFINED_ASPECT_RATIO) { - mInheritedMinAspectRatio = firstOpaque.getMinAspectRatio(); + mTransparentPolicyState.inheritFromOpaque(firstOpaque); + } + + /** + * Encapsulate the state for the current translucent activity when the transparent policy + * has started. + */ + static class TransparentPolicyState { + // Aspect ratio value to consider as undefined. + private static final float UNDEFINED_ASPECT_RATIO = 0f; + + @NonNull + private final ActivityRecord mActivityRecord; + + @Configuration.Orientation + private int mInheritedOrientation = ORIENTATION_UNDEFINED; + private float mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO; + private float mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO; + + // The app compat state for the opaque activity if any + private int mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN; + + // The CompatDisplayInsets of the opaque activity beneath the translucent one. + private ActivityRecord.CompatDisplayInsets mInheritedCompatDisplayInsets; + + @Nullable + ActivityRecord mFirstOpaqueActivity; + + /* + * WindowContainerListener responsible to make translucent activities inherit + * constraints from the first opaque activity beneath them. It's null for not + * translucent activities. + */ + @Nullable + private WindowContainerListener mLetterboxConfigListener; + + TransparentPolicyState(@NonNull ActivityRecord activityRecord) { + mActivityRecord = activityRecord; } - if (mActivityRecord.getMaxAspectRatio() != UNDEFINED_ASPECT_RATIO) { - mInheritedMaxAspectRatio = firstOpaque.getMaxAspectRatio(); + + private void start(@NonNull ActivityRecord firstOpaqueActivity) { + mFirstOpaqueActivity = firstOpaqueActivity; + mFirstOpaqueActivity.mLetterboxUiController.getTransparentPolicy() + .mDestroyListeners.add(mActivityRecord.mLetterboxUiController + .getTransparentPolicy()); + inheritFromOpaque(firstOpaqueActivity); + final WindowContainer<?> parent = mActivityRecord.getParent(); + mLetterboxConfigListener = WindowContainer.overrideConfigurationPropagation( + mActivityRecord, mFirstOpaqueActivity, + (opaqueConfig, transparentOverrideConfig) -> { + resetTranslucentOverrideConfig(transparentOverrideConfig); + final Rect parentBounds = parent.getWindowConfiguration().getBounds(); + final Rect bounds = transparentOverrideConfig + .windowConfiguration.getBounds(); + final Rect letterboxBounds = opaqueConfig.windowConfiguration.getBounds(); + // We cannot use letterboxBounds directly here because the position relies + // on letterboxing. Using letterboxBounds directly, would produce a + // double offset. + bounds.set(parentBounds.left, parentBounds.top, + parentBounds.left + letterboxBounds.width(), + parentBounds.top + letterboxBounds.height()); + // We need to initialize appBounds to avoid NPE. The actual value will + // be set ahead when resolving the Configuration for the activity. + transparentOverrideConfig.windowConfiguration.setAppBounds(new Rect()); + inheritFromOpaque(mFirstOpaqueActivity); + return transparentOverrideConfig; + }); } - mInheritedOrientation = firstOpaque.getRequestedConfigurationOrientation(); - mInheritedAppCompatState = firstOpaque.getAppCompatState(); - mInheritedCompatDisplayInsets = firstOpaque.getCompatDisplayInsets(); - } - private void clearInheritedConfig() { - if (mFirstOpaqueActivityBeneath != null) { - mFirstOpaqueActivityBeneath.mLetterboxUiController.getTransparentPolicy() - .mDestroyListeners.remove(this); + private void inheritFromOpaque(@NonNull ActivityRecord opaqueActivity) { + // To avoid wrong behaviour, we're not forcing a specific aspect ratio to activities + // which are not already providing one (e.g. permission dialogs) and presumably also + // not resizable. + if (mActivityRecord.getMinAspectRatio() != UNDEFINED_ASPECT_RATIO) { + mInheritedMinAspectRatio = opaqueActivity.getMinAspectRatio(); + } + if (mActivityRecord.getMaxAspectRatio() != UNDEFINED_ASPECT_RATIO) { + mInheritedMaxAspectRatio = opaqueActivity.getMaxAspectRatio(); + } + mInheritedOrientation = opaqueActivity.getRequestedConfigurationOrientation(); + mInheritedAppCompatState = opaqueActivity.getAppCompatState(); + mInheritedCompatDisplayInsets = opaqueActivity.getCompatDisplayInsets(); + } + + private void reset() { + if (mLetterboxConfigListener != null) { + mLetterboxConfigListener.onRemoved(); + } + mLetterboxConfigListener = null; + mInheritedOrientation = ORIENTATION_UNDEFINED; + mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO; + mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO; + mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN; + mInheritedCompatDisplayInsets = null; + if (mFirstOpaqueActivity != null) { + mFirstOpaqueActivity.mLetterboxUiController.getTransparentPolicy() + .mDestroyListeners.remove(mActivityRecord.mLetterboxUiController + .getTransparentPolicy()); + } + mFirstOpaqueActivity = null; + } + + private boolean isRunning() { + return mLetterboxConfigListener != null; + } + + private int getInheritedOrientation() { + return mInheritedOrientation; } - mFirstOpaqueActivityBeneath = null; - mLetterboxConfigListener = null; - mInheritedMinAspectRatio = UNDEFINED_ASPECT_RATIO; - mInheritedMaxAspectRatio = UNDEFINED_ASPECT_RATIO; - mInheritedOrientation = ORIENTATION_UNDEFINED; - mInheritedAppCompatState = APP_COMPAT_STATE_CHANGED__STATE__UNKNOWN; - mInheritedCompatDisplayInsets = null; - } + private float getInheritedMinAspectRatio() { + return mInheritedMinAspectRatio; + } + + private float getInheritedMaxAspectRatio() { + return mInheritedMaxAspectRatio; + } + + private int getInheritedAppCompatState() { + return mInheritedAppCompatState; + } + + private ActivityRecord.CompatDisplayInsets getInheritedCompatDisplayInsets() { + return mInheritedCompatDisplayInsets; + } + + private void clearInheritedCompatDisplayInsets() { + mInheritedCompatDisplayInsets = null; + } + + /** + * @return The first not finishing opaque activity beneath the current translucent activity + * if it exists and the strategy is enabled. + */ + private Optional<ActivityRecord> findOpaqueNotFinishingActivityBelow() { + if (!isRunning() || mActivityRecord.getTask() == null) { + return Optional.empty(); + } + return Optional.ofNullable(mFirstOpaqueActivity); + } + + /** + * In case of translucent activities, it consumes the {@link ActivityRecord} of the first + * opaque activity beneath using the given consumer and returns {@code true}. + */ + private boolean applyOnOpaqueActivityBelow(@NonNull Consumer<ActivityRecord> consumer) { + return findOpaqueNotFinishingActivityBelow() + .map(activityRecord -> { + consumer.accept(activityRecord); + return true; + }).orElse(false); + } + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java index f898ed25e4b1..b4ab0caa1071 100644 --- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java @@ -396,8 +396,7 @@ public class SizeCompatTests extends WindowTestsBase { opaqueActivity.removeImmediately(); // Check that updateInheritedLetterbox() is invoked again - verify(translucentActivity.mLetterboxUiController.getTransparentPolicy()) - .updateInheritedLetterbox(); + verify(translucentActivity.mLetterboxUiController.getTransparentPolicy()).start(); } // TODO(b/333663877): Enable test after fix @@ -466,8 +465,6 @@ public class SizeCompatTests extends WindowTestsBase { mTask.addChild(translucentActivity); // Transparent strategy applied assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior()); - assertNotNull(translucentActivity.mLetterboxUiController - .getTransparentPolicy().mFirstOpaqueActivityBeneath); spyOn(translucentActivity.mLetterboxUiController.getTransparentPolicy()); clearInvocations(translucentActivity.mLetterboxUiController.getTransparentPolicy()); @@ -475,11 +472,10 @@ public class SizeCompatTests extends WindowTestsBase { // We destroy the first opaque activity mActivity.removeImmediately(); - // Check that updateInheritedLetterbox() is invoked again on the TransparentPolicy - verify(translucentActivity.mLetterboxUiController.getTransparentPolicy()) - .updateInheritedLetterbox(); - assertNull(translucentActivity.mLetterboxUiController - .getTransparentPolicy().mFirstOpaqueActivityBeneath); + // Check that start() is invoked again on the TransparentPolicy + verify(translucentActivity.mLetterboxUiController.getTransparentPolicy()).start(); + assertFalse(translucentActivity.mLetterboxUiController + .getTransparentPolicy().hasInheritedLetterboxBehavior()); } @Test @@ -507,11 +503,12 @@ public class SizeCompatTests extends WindowTestsBase { // Transparent strategy applied assertTrue(translucentActivity.mLetterboxUiController.hasInheritedLetterboxBehavior()); - spyOn(translucentActivity.mLetterboxUiController); - clearInvocations(translucentActivity.mLetterboxUiController); + spyOn(translucentActivity.mLetterboxUiController.getTransparentPolicy()); + clearInvocations(translucentActivity.mLetterboxUiController.getTransparentPolicy()); // Check that updateInheritedLetterbox() is invoked again - verify(translucentActivity.mLetterboxUiController, never()).updateInheritedLetterbox(); + verify(translucentActivity.mLetterboxUiController.getTransparentPolicy(), never()) + .start(); } @Test |