diff options
9 files changed, 630 insertions, 118 deletions
diff --git a/services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java b/services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java index b9bdc325cf98..caff96ba4a9f 100644 --- a/services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java +++ b/services/core/java/com/android/server/wm/AppCompatReachabilityOverrides.java @@ -35,7 +35,6 @@ import android.annotation.NonNull; import android.content.res.Configuration; import android.graphics.Rect; -import com.android.internal.annotations.VisibleForTesting; import com.android.window.flags.Flags; /** @@ -112,12 +111,10 @@ class AppCompatReachabilityOverrides { : mAppCompatConfiguration.getLetterboxVerticalPositionMultiplier(tabletopMode); } - @VisibleForTesting boolean isHorizontalReachabilityEnabled() { return isHorizontalReachabilityEnabled(mActivityRecord.getParent().getConfiguration()); } - @VisibleForTesting boolean isVerticalReachabilityEnabled() { return isVerticalReachabilityEnabled(mActivityRecord.getParent().getConfiguration()); } diff --git a/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java b/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java index 90bfddb2095f..c3bf116e227d 100644 --- a/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java +++ b/services/core/java/com/android/server/wm/AppCompatReachabilityPolicy.java @@ -31,6 +31,8 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Rect; +import com.android.internal.annotations.VisibleForTesting; + import java.util.function.Supplier; /** @@ -43,7 +45,8 @@ class AppCompatReachabilityPolicy { @NonNull private final AppCompatConfiguration mAppCompatConfiguration; @Nullable - private Supplier<Rect> mLetterboxInnerBoundsSupplier; + @VisibleForTesting + Supplier<Rect> mLetterboxInnerBoundsSupplier; AppCompatReachabilityPolicy(@NonNull ActivityRecord activityRecord, @NonNull AppCompatConfiguration appCompatConfiguration) { diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java index f8cf97e71274..d582b073f90c 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatActivityRobot.java @@ -36,9 +36,12 @@ import android.content.ComponentName; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; import android.content.res.Configuration; +import android.graphics.Rect; import android.view.Surface; +import androidx.annotation.CallSuper; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.android.server.wm.utils.TestComponentStack; @@ -74,6 +77,9 @@ class AppCompatActivityRobot { private final int mDisplayHeight; private DisplayContent mDisplayContent; + @Nullable + private Consumer<ActivityRecord> mOnPostActivityCreation; + AppCompatActivityRobot(@NonNull WindowManagerService wm, @NonNull ActivityTaskManagerService atm, @NonNull ActivityTaskSupervisor supervisor, int displayWidth, int displayHeight) { @@ -96,6 +102,10 @@ class AppCompatActivityRobot { /* inNewDisplay */ false); } + void createActivityWithComponentWithoutTask() { + createActivityWithComponentInNewTask(/* inNewTask */ false, /* inNewDisplay */ false); + } + void createActivityWithComponentInNewTask() { createActivityWithComponentInNewTask(/* inNewTask */ true, /* inNewDisplay */ false); } @@ -130,6 +140,14 @@ class AppCompatActivityRobot { doReturn(naturalOrientation).when(mDisplayContent).getNaturalOrientation(); } + void configureTaskBounds(@NonNull Rect taskBounds) { + doReturn(taskBounds).when(mTaskStack.top()).getBounds(); + } + + void configureTopActivityBounds(@NonNull Rect activityBounds) { + doReturn(activityBounds).when(mActivityStack.top()).getBounds(); + } + @NonNull ActivityRecord top() { return mActivityStack.top(); @@ -169,6 +187,10 @@ class AppCompatActivityRobot { .isActivityEligibleForOrientationOverride(eq(mActivityStack.top())); } + void setTopActivityInTransition(boolean inTransition) { + doReturn(inTransition).when(mActivityStack.top()).isInTransition(); + } + void setShouldApplyUserMinAspectRatioOverride(boolean enabled) { doReturn(enabled).when(mActivityStack.top().mAppCompatController .getAppCompatAspectRatioOverrides()).shouldApplyUserMinAspectRatioOverride(); @@ -378,6 +400,32 @@ class AppCompatActivityRobot { pushActivity(newActivity); } + /** + * Specific Robots can override this method to add operation to run on a newly created + * {@link ActivityRecord}. Common case is to invoke spyOn(). + * + * @param activity The newly created {@link ActivityRecord}. + */ + @CallSuper + void onPostActivityCreation(@NonNull ActivityRecord activity) { + spyOn(activity); + spyOn(activity.mLetterboxUiController); + if (mOnPostActivityCreation != null) { + mOnPostActivityCreation.accept(activity); + } + } + + /** + * Each Robot can specify its own set of operation to execute on a newly created + * {@link ActivityRecord}. Most common the use of spyOn(). + * + * @param onPostActivityCreation The reference to the code to execute after the creation of a + * new {@link ActivityRecord}. + */ + void setOnPostActivityCreation(@Nullable Consumer<ActivityRecord> onPostActivityCreation) { + mOnPostActivityCreation = onPostActivityCreation; + } + private void createActivityWithComponentInNewTask(boolean inNewTask, boolean inNewDisplay) { if (inNewDisplay) { createNewDisplay(); @@ -385,14 +433,16 @@ class AppCompatActivityRobot { if (inNewTask) { createNewTask(); } - final ActivityRecord activity = new WindowTestsBase.ActivityBuilder(mAtm) - .setOnTop(true) - .setTask(mTaskStack.top()) + final WindowTestsBase.ActivityBuilder activityBuilder = + new WindowTestsBase.ActivityBuilder(mAtm).setOnTop(true) // Set the component to be that of the test class in order // to enable compat changes - .setComponent(ComponentName.createRelative(mAtm.mContext, TEST_COMPONENT_NAME)) - .build(); - pushActivity(activity); + .setComponent(ComponentName.createRelative(mAtm.mContext, TEST_COMPONENT_NAME)); + if (!mTaskStack.isEmpty()) { + // We put the Activity in the current task if any. + activityBuilder.setTask(mTaskStack.top()); + } + pushActivity(activityBuilder.build()); } /** @@ -438,14 +488,15 @@ class AppCompatActivityRobot { // We add the activity to the stack and spyOn() on its properties. private void pushActivity(@NonNull ActivityRecord activity) { mActivityStack.push(activity); - spyOn(activity); + onPostActivityCreation(activity); // TODO (b/351763164): Use these spyOn calls only when necessary. spyOn(activity.mAppCompatController.getTransparentPolicy()); spyOn(activity.mAppCompatController.getAppCompatAspectRatioOverrides()); spyOn(activity.mAppCompatController.getAppCompatAspectRatioPolicy()); spyOn(activity.mAppCompatController.getAppCompatFocusOverrides()); spyOn(activity.mAppCompatController.getAppCompatResizeOverrides()); - spyOn(activity.mLetterboxUiController); + spyOn(activity.mAppCompatController.getAppCompatReachabilityPolicy()); + spyOn(activity.mAppCompatController.getAppCompatReachabilityOverrides()); } private void pushTask(@NonNull Task task) { diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java index 6592f2625ab6..40a53479e9ab 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatConfigurationRobot.java @@ -19,6 +19,9 @@ package com.android.server.wm; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import androidx.annotation.NonNull; @@ -80,4 +83,34 @@ class AppCompatConfigurationRobot { doReturn(aspectRatio).when(mAppCompatConfiguration) .getFixedOrientationLetterboxAspectRatio(); } + + void setThinLetterboxWidthPx(int thinWidthPx) { + doReturn(thinWidthPx).when(mAppCompatConfiguration) + .getThinLetterboxWidthPx(); + } + + void setThinLetterboxHeightPx(int thinHeightPx) { + doReturn(thinHeightPx).when(mAppCompatConfiguration) + .getThinLetterboxHeightPx(); + } + + void checkToNextLeftStop(boolean invoked) { + verify(mAppCompatConfiguration, times(invoked ? 1 : 0)) + .movePositionForHorizontalReachabilityToNextLeftStop(anyBoolean()); + } + + void checkToNextRightStop(boolean invoked) { + verify(mAppCompatConfiguration, times(invoked ? 1 : 0)) + .movePositionForHorizontalReachabilityToNextRightStop(anyBoolean()); + } + + void checkToNextBottomStop(boolean invoked) { + verify(mAppCompatConfiguration, times(invoked ? 1 : 0)) + .movePositionForVerticalReachabilityToNextBottomStop(anyBoolean()); + } + + void checkToNextTopStop(boolean invoked) { + verify(mAppCompatConfiguration, times(invoked ? 1 : 0)) + .movePositionForVerticalReachabilityToNextTopStop(anyBoolean()); + } } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java new file mode 100644 index 000000000000..47f584a4951c --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityOverridesTest.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; + +import android.compat.testing.PlatformCompatChangeRule; +import android.graphics.Rect; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.annotations.Presubmit; + +import androidx.annotation.NonNull; + +import com.android.window.flags.Flags; + +import junit.framework.Assert; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * Test class for {@link AppCompatReachabilityOverrides}. + * <p> + * Build/Install/Run: + * atest WmTests:AppCompatReachabilityOverridesTest + */ +@Presubmit +@RunWith(WindowTestRunner.class) +public class AppCompatReachabilityOverridesTest extends WindowTestsBase { + + @Rule + public TestRule compatChangeRule = new PlatformCompatChangeRule(); + + @Test + public void testIsThinLetterboxed_NegativePx_returnsFalse() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponentWithoutTask(); + robot.conf().setThinLetterboxHeightPx(/* thinHeightPx */ -1); + robot.checkIsVerticalThinLetterboxed(/* expected */ false); + + robot.conf().setThinLetterboxWidthPx(/* thinHeightPx */ -1); + robot.checkIsHorizontalThinLetterboxed(/* expected */ false); + }); + } + + @Test + public void testIsThinLetterboxed_noTask_returnsFalse() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponentWithoutTask(); + robot.conf().setThinLetterboxHeightPx(/* thinHeightPx */ 10); + robot.checkIsVerticalThinLetterboxed(/* expected */ false); + + robot.conf().setThinLetterboxWidthPx(/* thinHeightPx */ 10); + robot.checkIsHorizontalThinLetterboxed(/* expected */ false); + }); + } + + @Test + public void testIsVerticalThinLetterboxed() { + runTestScenario((robot) -> { + robot.conf().setThinLetterboxHeightPx(/* thinHeightPx */ 10); + robot.applyOnActivity((a) -> { + a.createActivityWithComponent(); + a.configureTaskBounds(new Rect(0, 0, 100, 100)); + + // (task.width() - act.width()) / 2 = 5 < 10 + a.configureTopActivityBounds(new Rect(5, 5, 95, 95)); + robot.checkIsVerticalThinLetterboxed(/* expected */ true); + + // (task.width() - act.width()) / 2 = 10 = 10 + a.configureTopActivityBounds(new Rect(10, 10, 90, 90)); + robot.checkIsVerticalThinLetterboxed(/* expected */ true); + + // (task.width() - act.width()) / 2 = 11 > 10 + a.configureTopActivityBounds(new Rect(11, 11, 89, 89)); + robot.checkIsVerticalThinLetterboxed(/* expected */ false); + }); + }); + } + + @Test + public void testIsHorizontalThinLetterboxed() { + runTestScenario((robot) -> { + robot.conf().setThinLetterboxWidthPx(/* thinHeightPx */ 10); + robot.applyOnActivity((a) -> { + a.createActivityWithComponent(); + a.configureTaskBounds(new Rect(0, 0, 100, 100)); + + // (task.height() - act.height()) / 2 = 5 < 10 + a.configureTopActivityBounds(new Rect(5, 5, 95, 95)); + robot.checkIsHorizontalThinLetterboxed(/* expected */ true); + + // (task.height() - act.height()) / 2 = 10 = 10 + a.configureTopActivityBounds(new Rect(10, 10, 90, 90)); + robot.checkIsHorizontalThinLetterboxed(/* expected */ true); + + // (task.height() - act.height()) / 2 = 11 > 10 + a.configureTopActivityBounds(new Rect(11, 11, 89, 89)); + robot.checkIsHorizontalThinLetterboxed(/* expected */ false); + }); + }); + } + + @Test + @EnableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY) + public void testAllowReachabilityForThinLetterboxWithFlagEnabled() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + + robot.configureIsVerticalThinLetterboxed(/* isThin */ true); + robot.checkAllowVerticalReachabilityForThinLetterbox(/* expected */ false); + robot.configureIsHorizontalThinLetterboxed(/* isThin */ true); + robot.checkAllowHorizontalReachabilityForThinLetterbox(/* expected */ false); + + robot.configureIsVerticalThinLetterboxed(/* isThin */ false); + robot.checkAllowVerticalReachabilityForThinLetterbox(/* expected */ true); + robot.configureIsHorizontalThinLetterboxed(/* isThin */ false); + robot.checkAllowHorizontalReachabilityForThinLetterbox(/* expected */ true); + }); + } + + @Test + @DisableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY) + public void testAllowReachabilityForThinLetterboxWithFlagDisabled() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + + robot.configureIsVerticalThinLetterboxed(/* isThin */ true); + robot.checkAllowVerticalReachabilityForThinLetterbox(/* expected */ true); + robot.configureIsHorizontalThinLetterboxed(/* isThin */ true); + robot.checkAllowHorizontalReachabilityForThinLetterbox(/* expected */ true); + + robot.configureIsVerticalThinLetterboxed(/* isThin */ false); + robot.checkAllowVerticalReachabilityForThinLetterbox(/* expected */ true); + robot.configureIsHorizontalThinLetterboxed(/* isThin */ false); + robot.checkAllowHorizontalReachabilityForThinLetterbox(/* expected */ true); + }); + } + + /** + * Runs a test scenario providing a Robot. + */ + void runTestScenario(@NonNull Consumer<ReachabilityOverridesRobotTest> consumer) { + spyOn(mWm.mAppCompatConfiguration); + final ReachabilityOverridesRobotTest robot = + new ReachabilityOverridesRobotTest(mWm, mAtm, mSupervisor); + consumer.accept(robot); + } + + private static class ReachabilityOverridesRobotTest extends AppCompatRobotBase { + + private final Supplier<Rect> mLetterboxInnerBoundsSupplier = spy(Rect::new); + + ReachabilityOverridesRobotTest(@NonNull WindowManagerService wm, + @NonNull ActivityTaskManagerService atm, + @NonNull ActivityTaskSupervisor supervisor) { + super(wm, atm, supervisor); + } + + @Override + void onPostActivityCreation(@NonNull ActivityRecord activity) { + super.onPostActivityCreation(activity); + activity.mAppCompatController.getAppCompatReachabilityPolicy() + .setLetterboxInnerBoundsSupplier(mLetterboxInnerBoundsSupplier); + } + + void configureIsVerticalThinLetterboxed(boolean isThin) { + doReturn(isThin).when(getAppCompatReachabilityOverrides()) + .isVerticalThinLetterboxed(); + } + + void configureIsHorizontalThinLetterboxed(boolean isThin) { + doReturn(isThin).when(getAppCompatReachabilityOverrides()) + .isHorizontalThinLetterboxed(); + } + + void checkIsVerticalThinLetterboxed(boolean expected) { + Assert.assertEquals(expected, + getAppCompatReachabilityOverrides().isVerticalThinLetterboxed()); + } + + void checkIsHorizontalThinLetterboxed(boolean expected) { + Assert.assertEquals(expected, + getAppCompatReachabilityOverrides().isHorizontalThinLetterboxed()); + } + + void checkAllowVerticalReachabilityForThinLetterbox(boolean expected) { + Assert.assertEquals(expected, getAppCompatReachabilityOverrides() + .allowVerticalReachabilityForThinLetterbox()); + } + + void checkAllowHorizontalReachabilityForThinLetterbox(boolean expected) { + Assert.assertEquals(expected, getAppCompatReachabilityOverrides() + .allowHorizontalReachabilityForThinLetterbox()); + } + + @NonNull + private AppCompatReachabilityOverrides getAppCompatReachabilityOverrides() { + return activity().top().mAppCompatController.getAppCompatReachabilityOverrides(); + } + + } + +} diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityPolicyTest.java new file mode 100644 index 000000000000..84f89b5eb247 --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatReachabilityPolicyTest.java @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; + +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; + +import android.graphics.Rect; +import android.platform.test.annotations.Presubmit; + +import androidx.annotation.NonNull; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * Test class for {@link AppCompatReachabilityPolicy}. + * <p/> + * Build/Install/Run: + * atest WmTests:AppCompatReachabilityPolicyTest + */ +@Presubmit +@RunWith(WindowTestRunner.class) +public class AppCompatReachabilityPolicyTest extends WindowTestsBase { + + @Test + public void handleHorizontalDoubleTap_reachabilityDisabled_nothingHappen() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.enableHorizontalReachability(/* enabled */ false); + robot.activity().setTopActivityInTransition(/* inTransition */ true); + robot.doubleTapAt(100, 100); + + robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ false); + }); + } + + @Test + public void handleHorizontalDoubleTap_reachabilityEnabledInTransition_nothingHappen() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.enableHorizontalReachability(/* enabled */ true); + robot.activity().setTopActivityInTransition(/* inTransition */ true); + robot.doubleTapAt(100, 100); + + robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ false); + }); + } + + @Test + public void handleHorizontalDoubleTap_reachabilityDisabledNotInTransition_nothingHappen() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.enableHorizontalReachability(/* enabled */ false); + robot.activity().setTopActivityInTransition(/* inTransition */ false); + robot.doubleTapAt(100, 100); + + robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ false); + }); + } + + @Test + public void handleHorizontalDoubleTap_leftInnerFrame_moveToLeft() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.enableHorizontalReachability(/* enabled */ true); + robot.activity().setTopActivityInTransition(/* inTransition */ false); + + robot.configureLetterboxInnerFrameWidth(/* left */ 100, /* right */ 200); + robot.doubleTapAt(99, 100); + + robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ true); + robot.applyOnConf((c) -> { + c.checkToNextLeftStop(/* invoked */ true); + c.checkToNextRightStop(/* invoked */ false); + }); + }); + } + + @Test + public void handleHorizontalDoubleTap_rightInnerFrame_moveToRight() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.enableHorizontalReachability(/* enabled */ true); + robot.activity().setTopActivityInTransition(/* inTransition */ false); + + robot.configureLetterboxInnerFrameWidth(/* left */ 100, /* right */ 200); + robot.doubleTapAt(201, 100); + + robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ true); + robot.applyOnConf((c) -> { + c.checkToNextLeftStop(/* invoked */ false); + c.checkToNextRightStop(/* invoked */ true); + }); + }); + } + + @Test + public void handleHorizontalDoubleTap_intoInnerFrame_noMove() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.enableHorizontalReachability(/* enabled */ true); + robot.activity().setTopActivityInTransition(/* inTransition */ false); + + robot.configureLetterboxInnerFrameWidth(/* left */ 100, /* right */ 200); + robot.doubleTapAt(150, 100); + + robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ true); + robot.applyOnConf((c) -> { + c.checkToNextLeftStop(/* invoked */ false); + c.checkToNextRightStop(/* invoked */ false); + }); + }); + } + + + @Test + public void handleVerticalDoubleTap_reachabilityDisabled_nothingHappen() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.enableVerticalReachability(/* enabled */ false); + robot.activity().setTopActivityInTransition(/* inTransition */ true); + robot.doubleTapAt(100, 100); + + robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ false); + }); + } + + @Test + public void handleVerticalDoubleTap_reachabilityEnabledInTransition_nothingHappen() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.enableVerticalReachability(/* enabled */ true); + robot.activity().setTopActivityInTransition(/* inTransition */ true); + + robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ false); + }); + } + + @Test + public void handleVerticalDoubleTap_reachabilityDisabledNotInTransition_nothingHappen() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.enableVerticalReachability(/* enabled */ false); + robot.activity().setTopActivityInTransition(/* inTransition */ false); + + robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ false); + }); + } + + @Test + public void handleVerticalDoubleTap_topInnerFrame_moveToTop() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.enableVerticalReachability(/* enabled */ true); + robot.activity().setTopActivityInTransition(/* inTransition */ false); + + robot.configureLetterboxInnerFrameHeight(/* top */ 100, /* bottom */ 200); + robot.doubleTapAt(100, 99); + + robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ true); + robot.applyOnConf((c) -> { + c.checkToNextTopStop(/* invoked */ true); + c.checkToNextBottomStop(/* invoked */ false); + }); + }); + } + + @Test + public void handleVerticalDoubleTap_bottomInnerFrame_moveToBottom() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.enableVerticalReachability(/* enabled */ true); + robot.activity().setTopActivityInTransition(/* inTransition */ false); + + robot.configureLetterboxInnerFrameHeight(/* top */ 100, /* bottom */ 200); + robot.doubleTapAt(100, 201); + + robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ true); + robot.applyOnConf((c) -> { + c.checkToNextTopStop(/* invoked */ false); + c.checkToNextBottomStop(/* invoked */ true); + }); + }); + } + + @Test + public void handleVerticalDoubleTap_intoInnerFrame_noMove() { + runTestScenario((robot) -> { + robot.activity().createActivityWithComponent(); + robot.enableVerticalReachability(/* enabled */ true); + robot.activity().setTopActivityInTransition(/* inTransition */ false); + + robot.configureLetterboxInnerFrameHeight(/* top */ 100, /* bottom */ 200); + robot.doubleTapAt(100, 150); + + robot.checkLetterboxInnerFrameProvidedInvoked(/* invoked */ true); + robot.applyOnConf((c) -> { + c.checkToNextTopStop(/* invoked */ false); + c.checkToNextBottomStop(/* invoked */ false); + }); + }); + } + + + /** + * Runs a test scenario providing a Robot. + */ + void runTestScenario(@NonNull Consumer<ReachabilityPolicyRobotTest> consumer) { + spyOn(mWm.mAppCompatConfiguration); + final ReachabilityPolicyRobotTest robot = + new ReachabilityPolicyRobotTest(mWm, mAtm, mSupervisor); + consumer.accept(robot); + } + + private static class ReachabilityPolicyRobotTest extends AppCompatRobotBase { + + private final Supplier<Rect> mLetterboxInnerBoundsSupplier = spy(Rect::new); + + ReachabilityPolicyRobotTest(@NonNull WindowManagerService wm, + @NonNull ActivityTaskManagerService atm, + @NonNull ActivityTaskSupervisor supervisor) { + super(wm, atm, supervisor); + } + + @Override + void onPostActivityCreation(@NonNull ActivityRecord activity) { + super.onPostActivityCreation(activity); + activity.mAppCompatController.getAppCompatReachabilityPolicy() + .setLetterboxInnerBoundsSupplier(mLetterboxInnerBoundsSupplier); + } + + void configureLetterboxInnerFrameWidth(int left, int right) { + doReturn(new Rect(left, /* top */ 0, right, /* bottom */ 100)) + .when(mLetterboxInnerBoundsSupplier).get(); + } + + void configureLetterboxInnerFrameHeight(int top, int bottom) { + doReturn(new Rect(/* left */ 0, top, /* right */ 100, bottom)) + .when(mLetterboxInnerBoundsSupplier).get(); + } + + void enableHorizontalReachability(boolean enabled) { + doReturn(enabled).when(getAppCompatReachabilityOverrides()) + .isHorizontalReachabilityEnabled(); + } + + void enableVerticalReachability(boolean enabled) { + doReturn(enabled).when(getAppCompatReachabilityOverrides()) + .isVerticalReachabilityEnabled(); + } + + void doubleTapAt(int x, int y) { + getAppCompatReachabilityPolicy().handleDoubleTap(x, y); + } + + void checkLetterboxInnerFrameProvidedInvoked(boolean invoked) { + verify(mLetterboxInnerBoundsSupplier, times(invoked ? 1 : 0)).get(); + } + + @NonNull + private AppCompatReachabilityOverrides getAppCompatReachabilityOverrides() { + return activity().top().mAppCompatController.getAppCompatReachabilityOverrides(); + } + + @NonNull + private AppCompatReachabilityPolicy getAppCompatReachabilityPolicy() { + return activity().top().mAppCompatController.getAppCompatReachabilityPolicy(); + } + + } + +} diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java index 8fc1a77bd5e3..cade213ca3d7 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatResizeOverridesTest.java @@ -39,7 +39,7 @@ import java.util.function.Consumer; /** * Test class for {@link AppCompatResizeOverrides}. - * <p> + * <p/> * Build/Install/Run: * atest WmTests:AppCompatResizeOverridesTest */ diff --git a/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java b/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java index 6939f97e1799..57ff4f69c713 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppCompatRobotBase.java @@ -16,6 +16,7 @@ package com.android.server.wm; +import androidx.annotation.CallSuper; import androidx.annotation.NonNull; import java.util.function.Consumer; @@ -43,6 +44,7 @@ abstract class AppCompatRobotBase { int displayWidth, int displayHeight) { mActivityRobot = new AppCompatActivityRobot(wm, atm, supervisor, displayWidth, displayHeight); + mActivityRobot.setOnPostActivityCreation(this::onPostActivityCreation); mConfigurationRobot = new AppCompatConfigurationRobot(wm.mAppCompatConfiguration); mOptPropRobot = new AppCompatComponentPropRobot(wm); @@ -54,6 +56,16 @@ abstract class AppCompatRobotBase { this(wm, atm, supervisor, DEFAULT_DISPLAY_WIDTH, DEFAULT_DISPLAY_HEIGHT); } + /** + * Specific Robots can override this method to add operation to run on a newly created + * {@link ActivityRecord}. Common case is to invoke spyOn(). + * + * @param activity THe newly created {@link ActivityRecord}. + */ + @CallSuper + void onPostActivityCreation(@NonNull ActivityRecord activity) { + } + @NonNull AppCompatConfigurationRobot conf() { return mConfigurationRobot; diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java index 33df5d896f7f..695068a5842a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxUiControllerTest.java @@ -23,11 +23,9 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -36,8 +34,6 @@ import android.compat.testing.PlatformCompatChangeRule; import android.content.ComponentName; import android.content.res.Resources; import android.graphics.Rect; -import android.platform.test.annotations.DisableFlags; -import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.view.InsetsSource; import android.view.InsetsState; @@ -49,7 +45,6 @@ import android.view.WindowManager; import androidx.test.filters.SmallTest; import com.android.internal.R; -import com.android.window.flags.Flags; import org.junit.Before; import org.junit.Rule; @@ -296,106 +291,6 @@ public class LetterboxUiControllerTest extends WindowTestsBase { } @Test - public void testIsVerticalThinLetterboxed() { - // Vertical thin letterbox disabled - doReturn(-1).when(mActivity.mWmService.mAppCompatConfiguration) - .getThinLetterboxHeightPx(); - final AppCompatReachabilityOverrides reachabilityOverrides = mActivity.mAppCompatController - .getAppCompatReachabilityOverrides(); - assertFalse(reachabilityOverrides.isVerticalThinLetterboxed()); - // Define a Task 100x100 - final Task task = mock(Task.class); - doReturn(new Rect(0, 0, 100, 100)).when(task).getBounds(); - doReturn(10).when(mActivity.mWmService.mAppCompatConfiguration) - .getThinLetterboxHeightPx(); - - // Vertical thin letterbox disabled without Task - doReturn(null).when(mActivity).getTask(); - assertFalse(reachabilityOverrides.isVerticalThinLetterboxed()); - // Assign a Task for the Activity - doReturn(task).when(mActivity).getTask(); - - // (task.width() - act.width()) / 2 = 5 < 10 - doReturn(new Rect(5, 5, 95, 95)).when(mActivity).getBounds(); - assertTrue(reachabilityOverrides.isVerticalThinLetterboxed()); - - // (task.width() - act.width()) / 2 = 10 = 10 - doReturn(new Rect(10, 10, 90, 90)).when(mActivity).getBounds(); - assertTrue(reachabilityOverrides.isVerticalThinLetterboxed()); - - // (task.width() - act.width()) / 2 = 11 > 10 - doReturn(new Rect(11, 11, 89, 89)).when(mActivity).getBounds(); - assertFalse(reachabilityOverrides.isVerticalThinLetterboxed()); - } - - @Test - public void testIsHorizontalThinLetterboxed() { - // Horizontal thin letterbox disabled - doReturn(-1).when(mActivity.mWmService.mAppCompatConfiguration) - .getThinLetterboxWidthPx(); - final AppCompatReachabilityOverrides reachabilityOverrides = mActivity.mAppCompatController - .getAppCompatReachabilityOverrides(); - assertFalse(reachabilityOverrides.isHorizontalThinLetterboxed()); - // Define a Task 100x100 - final Task task = mock(Task.class); - doReturn(new Rect(0, 0, 100, 100)).when(task).getBounds(); - doReturn(10).when(mActivity.mWmService.mAppCompatConfiguration) - .getThinLetterboxWidthPx(); - - // Vertical thin letterbox disabled without Task - doReturn(null).when(mActivity).getTask(); - assertFalse(reachabilityOverrides.isHorizontalThinLetterboxed()); - // Assign a Task for the Activity - doReturn(task).when(mActivity).getTask(); - - // (task.height() - act.height()) / 2 = 5 < 10 - doReturn(new Rect(5, 5, 95, 95)).when(mActivity).getBounds(); - assertTrue(reachabilityOverrides.isHorizontalThinLetterboxed()); - - // (task.height() - act.height()) / 2 = 10 = 10 - doReturn(new Rect(10, 10, 90, 90)).when(mActivity).getBounds(); - assertTrue(reachabilityOverrides.isHorizontalThinLetterboxed()); - - // (task.height() - act.height()) / 2 = 11 > 10 - doReturn(new Rect(11, 11, 89, 89)).when(mActivity).getBounds(); - assertFalse(reachabilityOverrides.isHorizontalThinLetterboxed()); - } - - @Test - @EnableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY) - public void testAllowReachabilityForThinLetterboxWithFlagEnabled() { - final AppCompatReachabilityOverrides reachabilityOverrides = - mActivity.mAppCompatController.getAppCompatReachabilityOverrides(); - spyOn(reachabilityOverrides); - doReturn(true).when(reachabilityOverrides).isVerticalThinLetterboxed(); - assertFalse(reachabilityOverrides.allowVerticalReachabilityForThinLetterbox()); - doReturn(true).when(reachabilityOverrides).isHorizontalThinLetterboxed(); - assertFalse(reachabilityOverrides.allowHorizontalReachabilityForThinLetterbox()); - - doReturn(false).when(reachabilityOverrides).isVerticalThinLetterboxed(); - assertTrue(reachabilityOverrides.allowVerticalReachabilityForThinLetterbox()); - doReturn(false).when(reachabilityOverrides).isHorizontalThinLetterboxed(); - assertTrue(reachabilityOverrides.allowHorizontalReachabilityForThinLetterbox()); - } - - @Test - @DisableFlags(Flags.FLAG_DISABLE_THIN_LETTERBOXING_POLICY) - public void testAllowReachabilityForThinLetterboxWithFlagDisabled() { - final AppCompatReachabilityOverrides reachabilityOverrides = - mActivity.mAppCompatController.getAppCompatReachabilityOverrides(); - spyOn(reachabilityOverrides); - doReturn(true).when(reachabilityOverrides).isVerticalThinLetterboxed(); - assertTrue(reachabilityOverrides.allowVerticalReachabilityForThinLetterbox()); - doReturn(true).when(reachabilityOverrides).isHorizontalThinLetterboxed(); - assertTrue(reachabilityOverrides.allowHorizontalReachabilityForThinLetterbox()); - - doReturn(false).when(reachabilityOverrides).isVerticalThinLetterboxed(); - assertTrue(reachabilityOverrides.allowVerticalReachabilityForThinLetterbox()); - doReturn(false).when(reachabilityOverrides).isHorizontalThinLetterboxed(); - assertTrue(reachabilityOverrides.allowHorizontalReachabilityForThinLetterbox()); - } - - @Test public void testIsLetterboxEducationEnabled() { mController.isLetterboxEducationEnabled(); verify(mAppCompatConfiguration).getIsEducationEnabled(); |