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);