Loading services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java +11 −3 Original line number Diff line number Diff line Loading @@ -55,7 +55,8 @@ class AppCompatLetterboxPolicy { @NonNull private final ActivityRecord mActivityRecord; @NonNull private final AppCompatLetterboxPolicyState mLetterboxPolicyState; @VisibleForTesting final AppCompatLetterboxPolicyState mLetterboxPolicyState; @NonNull private final AppCompatRoundedCorners mAppCompatRoundedCorners; @NonNull Loading Loading @@ -333,7 +334,8 @@ class AppCompatLetterboxPolicy { * Existing {@link AppCompatLetterboxPolicyState} implementation. * TODO(b/375339716): Clean code for legacy implementation. */ private class LegacyLetterboxPolicyState implements AppCompatLetterboxPolicyState { @VisibleForTesting class LegacyLetterboxPolicyState implements AppCompatLetterboxPolicyState { @Nullable private Letterbox mLetterbox; Loading Loading @@ -460,7 +462,8 @@ class AppCompatLetterboxPolicy { /** * {@link AppCompatLetterboxPolicyState} implementation for the letterbox presentation on shell. */ private class ShellLetterboxPolicyState implements AppCompatLetterboxPolicyState { @VisibleForTesting class ShellLetterboxPolicyState implements AppCompatLetterboxPolicyState { private final Rect mInnerBounds = new Rect(); private final Rect mOuterBounds = new Rect(); Loading @@ -485,6 +488,11 @@ class AppCompatLetterboxPolicy { mActivityRecord.mAppCompatController.getReachabilityPolicy() .setLetterboxInnerBoundsSupplier(() -> mInnerBounds); updateSurfacesBounds(); if (mActivityRecord.mAppCompatController.getReachabilityOverrides() .isDoubleTapEvent()) { // We need to notify Shell that letterbox position has changed. mActivityRecord.getTask().dispatchTaskInfoChangedIfNeeded(true /* force */); } } @Override Loading services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxPolicyStateTest.java 0 → 100644 +151 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; 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 androidx.test.filters.SmallTest; import com.android.window.flags.Flags; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import java.util.function.Consumer; /** * Test class for {@link AppCompatLetterboxPolicy}. * * Build/Install/Run: * atest WmTests:AppCompatLetterboxPolicyStateTest */ @SmallTest @Presubmit @RunWith(WindowTestRunner.class) public class AppCompatLetterboxPolicyStateTest extends WindowTestsBase { @Test @EnableFlags(Flags.FLAG_APP_COMPAT_REFACTORING) public void taskInfoDispatchedIfEventIsDoubleTapOnShellImplementation() { runTestScenario((robot) -> { robot.activity().createActivityWithComponent(); robot.configureIsDoubleTapEvent(/* isDoubleTapEvent */ true); robot.invokeLayoutLetterboxIfNeeded(); robot.verifyDispatchTaskInfoChangedIfNeeded(/* expected */ true, /* withValue */ true); }); } @Test @EnableFlags(Flags.FLAG_APP_COMPAT_REFACTORING) public void taskInfoNotDispatchedIfEventIsNotDoubleTapOnShellImplementation() { runTestScenario((robot) -> { robot.activity().createActivityWithComponent(); robot.configureIsDoubleTapEvent(/* isDoubleTapEvent */ false); robot.invokeLayoutLetterboxIfNeeded(); robot.verifyDispatchTaskInfoChangedIfNeeded(/* expected */ false, /* withValue */ true); }); } @Test @DisableFlags(Flags.FLAG_APP_COMPAT_REFACTORING) public void taskInfoDispatchedIfEventIsDoubleTapOnLegacyImplementation() { runTestScenario((robot) -> { robot.activity().createActivityWithComponent(); robot.configureIsDoubleTapEvent(/* isDoubleTapEvent */ true); robot.invokeLayoutLetterboxIfNeeded(); robot.verifyDispatchTaskInfoChangedIfNeeded(/* expected */ true, /* withValue */ true); }); } @Test @DisableFlags(Flags.FLAG_APP_COMPAT_REFACTORING) public void taskInfoNotDispatchedIfEventIsNotDoubleTapOnLegacyImplementation() { runTestScenario((robot) -> { robot.activity().createActivityWithComponent(); robot.configureIsDoubleTapEvent(/* isDoubleTapEvent */ false); robot.invokeLayoutLetterboxIfNeeded(); robot.verifyDispatchTaskInfoChangedIfNeeded(/* expected */ false, /* withValue */ true); }); } /** * Runs a test scenario providing a Robot. */ void runTestScenario(@NonNull Consumer<LetterboxPolicyStateRobotTest> consumer) { consumer.accept(new LetterboxPolicyStateRobotTest(this)); } private static class LetterboxPolicyStateRobotTest extends AppCompatRobotBase { private final WindowState mWindowState = Mockito.mock(WindowState.class); LetterboxPolicyStateRobotTest(@NonNull WindowTestsBase windowTestBase) { super(windowTestBase); doReturn(new Rect(0, 0, 1000, 2000)).when(mWindowState).getFrame(); } @Override void onPostActivityCreation(@NonNull ActivityRecord activity) { super.onPostActivityCreation(activity); spyOn(getReachabilityOverrides()); spyOn(activity.getTask()); } void invokeLayoutLetterboxIfNeeded() { getLetterboxPolicyState().layoutLetterboxIfNeeded(mWindowState); } void configureIsDoubleTapEvent(boolean isDoubleTapEvent) { doReturn(isDoubleTapEvent).when(getReachabilityOverrides()).isDoubleTapEvent(); } void verifyDispatchTaskInfoChangedIfNeeded(boolean expected, boolean withValue) { verify(activity().top().getTask(), times(expected ? 1 : 0)).dispatchTaskInfoChangedIfNeeded(eq(withValue)); } @NonNull private AppCompatReachabilityOverrides getReachabilityOverrides() { return activity().top().mAppCompatController.getReachabilityOverrides(); } @NonNull private AppCompatLetterboxPolicyState getLetterboxPolicyState() { return activity().top().mAppCompatController.getLetterboxPolicy().mLetterboxPolicyState; } } } Loading
services/core/java/com/android/server/wm/AppCompatLetterboxPolicy.java +11 −3 Original line number Diff line number Diff line Loading @@ -55,7 +55,8 @@ class AppCompatLetterboxPolicy { @NonNull private final ActivityRecord mActivityRecord; @NonNull private final AppCompatLetterboxPolicyState mLetterboxPolicyState; @VisibleForTesting final AppCompatLetterboxPolicyState mLetterboxPolicyState; @NonNull private final AppCompatRoundedCorners mAppCompatRoundedCorners; @NonNull Loading Loading @@ -333,7 +334,8 @@ class AppCompatLetterboxPolicy { * Existing {@link AppCompatLetterboxPolicyState} implementation. * TODO(b/375339716): Clean code for legacy implementation. */ private class LegacyLetterboxPolicyState implements AppCompatLetterboxPolicyState { @VisibleForTesting class LegacyLetterboxPolicyState implements AppCompatLetterboxPolicyState { @Nullable private Letterbox mLetterbox; Loading Loading @@ -460,7 +462,8 @@ class AppCompatLetterboxPolicy { /** * {@link AppCompatLetterboxPolicyState} implementation for the letterbox presentation on shell. */ private class ShellLetterboxPolicyState implements AppCompatLetterboxPolicyState { @VisibleForTesting class ShellLetterboxPolicyState implements AppCompatLetterboxPolicyState { private final Rect mInnerBounds = new Rect(); private final Rect mOuterBounds = new Rect(); Loading @@ -485,6 +488,11 @@ class AppCompatLetterboxPolicy { mActivityRecord.mAppCompatController.getReachabilityPolicy() .setLetterboxInnerBoundsSupplier(() -> mInnerBounds); updateSurfacesBounds(); if (mActivityRecord.mAppCompatController.getReachabilityOverrides() .isDoubleTapEvent()) { // We need to notify Shell that letterbox position has changed. mActivityRecord.getTask().dispatchTaskInfoChangedIfNeeded(true /* force */); } } @Override Loading
services/tests/wmtests/src/com/android/server/wm/AppCompatLetterboxPolicyStateTest.java 0 → 100644 +151 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 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.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; 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 androidx.test.filters.SmallTest; import com.android.window.flags.Flags; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import java.util.function.Consumer; /** * Test class for {@link AppCompatLetterboxPolicy}. * * Build/Install/Run: * atest WmTests:AppCompatLetterboxPolicyStateTest */ @SmallTest @Presubmit @RunWith(WindowTestRunner.class) public class AppCompatLetterboxPolicyStateTest extends WindowTestsBase { @Test @EnableFlags(Flags.FLAG_APP_COMPAT_REFACTORING) public void taskInfoDispatchedIfEventIsDoubleTapOnShellImplementation() { runTestScenario((robot) -> { robot.activity().createActivityWithComponent(); robot.configureIsDoubleTapEvent(/* isDoubleTapEvent */ true); robot.invokeLayoutLetterboxIfNeeded(); robot.verifyDispatchTaskInfoChangedIfNeeded(/* expected */ true, /* withValue */ true); }); } @Test @EnableFlags(Flags.FLAG_APP_COMPAT_REFACTORING) public void taskInfoNotDispatchedIfEventIsNotDoubleTapOnShellImplementation() { runTestScenario((robot) -> { robot.activity().createActivityWithComponent(); robot.configureIsDoubleTapEvent(/* isDoubleTapEvent */ false); robot.invokeLayoutLetterboxIfNeeded(); robot.verifyDispatchTaskInfoChangedIfNeeded(/* expected */ false, /* withValue */ true); }); } @Test @DisableFlags(Flags.FLAG_APP_COMPAT_REFACTORING) public void taskInfoDispatchedIfEventIsDoubleTapOnLegacyImplementation() { runTestScenario((robot) -> { robot.activity().createActivityWithComponent(); robot.configureIsDoubleTapEvent(/* isDoubleTapEvent */ true); robot.invokeLayoutLetterboxIfNeeded(); robot.verifyDispatchTaskInfoChangedIfNeeded(/* expected */ true, /* withValue */ true); }); } @Test @DisableFlags(Flags.FLAG_APP_COMPAT_REFACTORING) public void taskInfoNotDispatchedIfEventIsNotDoubleTapOnLegacyImplementation() { runTestScenario((robot) -> { robot.activity().createActivityWithComponent(); robot.configureIsDoubleTapEvent(/* isDoubleTapEvent */ false); robot.invokeLayoutLetterboxIfNeeded(); robot.verifyDispatchTaskInfoChangedIfNeeded(/* expected */ false, /* withValue */ true); }); } /** * Runs a test scenario providing a Robot. */ void runTestScenario(@NonNull Consumer<LetterboxPolicyStateRobotTest> consumer) { consumer.accept(new LetterboxPolicyStateRobotTest(this)); } private static class LetterboxPolicyStateRobotTest extends AppCompatRobotBase { private final WindowState mWindowState = Mockito.mock(WindowState.class); LetterboxPolicyStateRobotTest(@NonNull WindowTestsBase windowTestBase) { super(windowTestBase); doReturn(new Rect(0, 0, 1000, 2000)).when(mWindowState).getFrame(); } @Override void onPostActivityCreation(@NonNull ActivityRecord activity) { super.onPostActivityCreation(activity); spyOn(getReachabilityOverrides()); spyOn(activity.getTask()); } void invokeLayoutLetterboxIfNeeded() { getLetterboxPolicyState().layoutLetterboxIfNeeded(mWindowState); } void configureIsDoubleTapEvent(boolean isDoubleTapEvent) { doReturn(isDoubleTapEvent).when(getReachabilityOverrides()).isDoubleTapEvent(); } void verifyDispatchTaskInfoChangedIfNeeded(boolean expected, boolean withValue) { verify(activity().top().getTask(), times(expected ? 1 : 0)).dispatchTaskInfoChangedIfNeeded(eq(withValue)); } @NonNull private AppCompatReachabilityOverrides getReachabilityOverrides() { return activity().top().mAppCompatController.getReachabilityOverrides(); } @NonNull private AppCompatLetterboxPolicyState getLetterboxPolicyState() { return activity().top().mAppCompatController.getLetterboxPolicy().mLetterboxPolicyState; } } }