From 2a9ccc9d2a5f2d17872051dca7ee55de3dc25a11 Mon Sep 17 00:00:00 2001 From: Vadim Caen Date: Wed, 29 Jan 2020 12:21:41 +0100 Subject: [PATCH] Fixed rotation: rotate wallpaper The wallpaper should be rotated if its target WindowState is rotated. The fixed rotation is applied to the WallpaperWindowToken and the offsets are computed using the overriden DisplayInfo contained in the WindowToken.ForcedRotationState. Test: atest WmTests:RecentsAnimationControllerTest#testWallpaperHasFixedRotationApplied Bug: 143053092, 148000485, 147999946 Change-Id: I244b3cb81dd29995a1a9181949e4f3f973816825 --- .../server/wm/RecentsAnimationController.java | 14 ++++ .../server/wm/WallpaperWindowToken.java | 31 ++++++++- .../com/android/server/wm/WindowToken.java | 13 ++++ .../wm/RecentsAnimationControllerTest.java | 69 +++++++++++++++++++ 4 files changed, 125 insertions(+), 2 deletions(-) diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index e59446617860e..53dd32633a2f3 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -34,6 +34,7 @@ import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_RECENTS; import static com.android.server.wm.WindowManagerInternal.AppTransitionListener; import android.annotation.IntDef; +import android.annotation.NonNull; import android.app.ActivityManager.TaskSnapshot; import android.app.WindowConfiguration; import android.graphics.Point; @@ -836,6 +837,19 @@ public class RecentsAnimationController implements DeathRecipient { return task != null && isAnimatingTask(task) && !isTargetApp(windowState.mActivityRecord); } + /** + * If the animation target ActivityRecord has a fixed rotation ({@link + * WindowToken#hasFixedRotationTransform()}, the provided wallpaper will be rotated accordingly. + * + * This avoids any screen rotation animation when animating to the Recents view. + */ + void applyFixedRotationTransformIfNeeded(@NonNull WindowToken wallpaper) { + if (mTargetActivityRecord == null) { + return; + } + wallpaper.applyFixedRotationTransform(mTargetActivityRecord); + } + @VisibleForTesting class TaskAnimationAdapter implements AnimationAdapter { diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java index d23bf978cbaba..1e22141f232a4 100644 --- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java +++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java @@ -122,10 +122,37 @@ class WallpaperWindowToken extends WindowToken { mDisplayContent.setLayoutNeeded(); } - final DisplayInfo displayInfo = mDisplayContent.getDisplayInfo(); + final WallpaperController wallpaperController = mDisplayContent.mWallpaperController; + + if (visible) { + final WindowState wallpaperTarget = wallpaperController.getWallpaperTarget(); + final RecentsAnimationController recentsAnimationController = + mWmService.getRecentsAnimationController(); + if (wallpaperTarget != null + && recentsAnimationController != null + && recentsAnimationController.isAnimatingTask(wallpaperTarget.getTask())) { + // If the Recents animation is running, and the wallpaper target is the animating + // task we want the wallpaper to be rotated in the same orientation as the + // RecentsAnimation's target (e.g the launcher) + recentsAnimationController.applyFixedRotationTransformIfNeeded(this); + } else if (wallpaperTarget != null + && wallpaperTarget.mToken.hasFixedRotationTransform()) { + // If the wallpaper target has a fixed rotation, we want the wallpaper to follow its + // rotation + applyFixedRotationTransform(wallpaperTarget.mToken); + } else if (hasFixedRotationTransform()) { + clearFixedRotationTransform(); + } + } + + DisplayInfo displayInfo = getFixedRotationTransformDisplayInfo(); + if (displayInfo == null) { + displayInfo = mDisplayContent.getDisplayInfo(); + } + final int dw = displayInfo.logicalWidth; final int dh = displayInfo.logicalHeight; - final WallpaperController wallpaperController = mDisplayContent.mWallpaperController; + for (int wallpaperNdx = mChildren.size() - 1; wallpaperNdx >= 0; wallpaperNdx--) { final WindowState wallpaper = mChildren.get(wallpaperNdx); diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 118056631cb16..48c7812afec07 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -377,6 +377,19 @@ class WindowToken extends WindowContainer { onConfigurationChanged(getParent().getConfiguration()); } + /** + * Copies the {@link FixedRotationTransformState} (if any) from the other WindowToken to this + * one. + */ + void applyFixedRotationTransform(WindowToken other) { + final FixedRotationTransformState fixedRotationState = other.mFixedRotationTransformState; + if (fixedRotationState != null) { + applyFixedRotationTransform(fixedRotationState.mDisplayInfo, + fixedRotationState.mDisplayFrames, + fixedRotationState.mRotatedOverrideConfiguration); + } + } + /** Clears the transformation and continue updating the orientation change of display. */ void clearFixedRotationTransform() { if (mFixedRotationTransformState == null) { diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java index 66180a488ab15..6a8917cda9fd5 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -20,7 +20,9 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; +import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManager.TRANSIT_ACTIVITY_CLOSE; import static com.android.dx.mockito.inline.extended.ExtendedMockito.atLeast; @@ -67,12 +69,16 @@ import androidx.test.filters.SmallTest; import com.android.server.wm.SurfaceAnimator.OnAnimationFinishedCallback; +import com.google.common.truth.Truth; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.ArrayList; + /** * Build/Install/Run: * atest WmTests:RecentsAnimationControllerTest @@ -370,6 +376,69 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { homeAppWindow.getConfiguration().orientation); } + @Test + public void testWallpaperHasFixedRotationApplied() { + mWm.mIsFixedRotationTransformEnabled = true; + mWm.setRecentsAnimationController(mController); + + // Create a portrait home stack, a wallpaper and a landscape application displayed on top. + + // Home stack + final ActivityStack homeStack = mDisplayContent.getOrCreateStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP); + final ActivityRecord homeActivity = + new ActivityTestsBase.ActivityBuilder(mWm.mAtmService) + .setStack(homeStack) + .setCreateTask(true) + .build(); + homeActivity.setOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); + + final WindowState homeWindow = createWindow(null, TYPE_BASE_APPLICATION, homeActivity, + "homeWindow"); + homeActivity.addWindow(homeWindow); + homeWindow.getAttrs().flags |= FLAG_SHOW_WALLPAPER; + + // Landscape application + final ActivityRecord activity = createActivityRecord(mDisplayContent, + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD); + final WindowState applicationWindow = createWindow(null, TYPE_BASE_APPLICATION, activity, + "applicationWindow"); + activity.addWindow(applicationWindow); + activity.setOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); + + // Wallpaper + final WallpaperWindowToken wallpaperWindowToken = new WallpaperWindowToken(mWm, + mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */); + final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken, + "wallpaperWindow"); + + // Make sure the landscape activity is on top and the display is in landscape + activity.moveFocusableActivityToTop("test"); + mDisplayContent.getConfiguration().windowConfiguration.setRotation( + mDisplayContent.getRotation()); + + + spyOn(mDisplayContent.mWallpaperController); + doReturn(true).when(mDisplayContent.mWallpaperController).isWallpaperVisible(); + + // Start the recents animation + mController + .initialize(ACTIVITY_TYPE_HOME, new SparseBooleanArray(), homeActivity); + + mDisplayContent.mWallpaperController.adjustWallpaperWindows(); + + // Check preconditions + ArrayList wallpapers = new ArrayList<>(1); + mDisplayContent.forAllWallpaperWindows(wallpapers::add); + + Truth.assertThat(wallpapers).hasSize(1); + Truth.assertThat(wallpapers.get(0).getTopChild()).isEqualTo(wallpaperWindow); + + // Actual check + assertEquals(Configuration.ORIENTATION_PORTRAIT, + wallpapers.get(0).getConfiguration().orientation); + } + private static void verifyNoMoreInteractionsExceptAsBinder(IInterface binder) { verify(binder, atLeast(0)).asBinder(); verifyNoMoreInteractions(binder);