Loading services/core/java/com/android/server/wm/ActivityStack.java +16 −0 Original line number Diff line number Diff line Loading @@ -819,6 +819,22 @@ class ActivityStack extends Task { mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); mRootWindowContainer.resumeFocusedStacksTopActivities(); final boolean pinnedToFullscreen = currentMode == WINDOWING_MODE_PINNED && windowingMode == WINDOWING_MODE_FULLSCREEN; if (pinnedToFullscreen && topActivity != null && !isForceHidden()) { mDisplayContent.getPinnedStackController().setPipWindowingModeChanging(true); try { // Report orientation as soon as possible so that the display can freeze earlier if // the display orientation will be changed. Because the surface bounds of activity // may have been set to fullscreen but the activity hasn't redrawn its content yet, // the rotation animation needs to capture snapshot earlier to avoid animating from // an intermediate state. topActivity.reportDescendantOrientationChangeIfNeeded(); } finally { mDisplayContent.getPinnedStackController().setPipWindowingModeChanging(false); } } } @Override Loading services/core/java/com/android/server/wm/DisplayContent.java +4 −0 Original line number Diff line number Diff line Loading @@ -1481,6 +1481,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // has it own policy for bounds, the activity bounds based on parent is unknown. return false; } if (mPinnedStackControllerLocked.isPipActiveOrWindowingModeChanging()) { // Use normal rotation animation because seamless PiP rotation is not supported yet. return false; } setFixedRotationLaunchingApp(r, rotation); return true; Loading services/core/java/com/android/server/wm/PinnedStackController.java +17 −4 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import android.app.RemoteAction; import android.content.ComponentName; import android.content.pm.ParceledListSlice; import android.content.res.Resources; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.util.DisplayMetrics; Loading @@ -33,8 +32,6 @@ import android.view.DisplayInfo; import android.view.IPinnedStackController; import android.view.IPinnedStackListener; import com.android.server.UiThread; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; Loading @@ -61,7 +58,6 @@ class PinnedStackController { private final WindowManagerService mService; private final DisplayContent mDisplayContent; private final Handler mHandler = UiThread.getHandler(); private IPinnedStackListener mPinnedStackListener; private final PinnedStackListenerDeathHandler mPinnedStackListenerDeathHandler = Loading @@ -69,6 +65,9 @@ class PinnedStackController { private final PinnedStackControllerCallback mCallbacks = new PinnedStackControllerCallback(); /** Whether the PiP is entering or leaving. */ private boolean mIsPipWindowingModeChanging; private boolean mIsImeShowing; private int mImeHeight; Loading Loading @@ -161,6 +160,20 @@ class PinnedStackController { Float.compare(aspectRatio, mMaxAspectRatio) <= 0; } /** Returns {@code true} if the PiP is on screen or is changing windowing mode. */ boolean isPipActiveOrWindowingModeChanging() { if (mIsPipWindowingModeChanging) { return true; } final Task pinnedTask = mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask(); return pinnedTask != null && pinnedTask.hasChild(); } /** Sets whether a visible stack is changing from or to pinned mode. */ void setPipWindowingModeChanging(boolean isPipWindowingModeChanging) { mIsPipWindowingModeChanging = isPipWindowingModeChanging; } /** * Activity is hidden (either stopped or removed), resets the last saved snap fraction * so that the default bounds will be returned for the next session. Loading services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +56 −0 Original line number Diff line number Diff line Loading @@ -16,7 +16,10 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; Loading Loading @@ -73,6 +76,8 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import android.annotation.SuppressLint; Loading Loading @@ -1167,6 +1172,57 @@ public class DisplayContentTests extends WindowTestsBase { ROTATION_90 /* newRotation */, false /* forceUpdate */)); } @Test public void testNoFixedRotationWithPip() { mWm.mIsFixedRotationTransformEnabled = true; // Make resume-top really update the activity state. doReturn(false).when(mWm.mAtmService).isBooting(); doReturn(true).when(mWm.mAtmService).isBooted(); // Speed up the test by a few seconds. mWm.mAtmService.deferWindowLayout(); doNothing().when(mWm).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt()); final DisplayContent displayContent = mWm.mRoot.getDefaultDisplay(); final Configuration displayConfig = displayContent.getConfiguration(); final ActivityRecord pinnedActivity = createActivityRecord(displayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD); final Task pinnedTask = pinnedActivity.getRootTask(); final ActivityRecord homeActivity = WindowTestUtils.createTestActivityRecord( displayContent.getDefaultTaskDisplayArea().getOrCreateRootHomeTask()); if (displayConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { homeActivity.setOrientation(SCREEN_ORIENTATION_PORTRAIT); pinnedActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); } else { homeActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); pinnedActivity.setOrientation(SCREEN_ORIENTATION_PORTRAIT); } final int homeConfigOrientation = homeActivity.getRequestedConfigurationOrientation(); final int pinnedConfigOrientation = pinnedActivity.getRequestedConfigurationOrientation(); assertEquals(homeConfigOrientation, displayConfig.orientation); clearInvocations(mWm); // Leave PiP to fullscreen. The orientation can be updated from // ActivityRecord#reportDescendantOrientationChangeIfNeeded. pinnedTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); homeActivity.setState(ActivityStack.ActivityState.STOPPED, "test"); assertFalse(displayContent.hasTopFixedRotationLaunchingApp()); verify(mWm, atLeastOnce()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt()); assertEquals(pinnedConfigOrientation, displayConfig.orientation); assertFalse(displayContent.getPinnedStackController().isPipActiveOrWindowingModeChanging()); clearInvocations(mWm); // Enter PiP from fullscreen. The orientation can be updated from // ensure-visibility/resume-focused-stack -> ActivityRecord#makeActiveIfNeeded -> resume. pinnedTask.setWindowingMode(WINDOWING_MODE_PINNED); assertFalse(displayContent.hasTopFixedRotationLaunchingApp()); verify(mWm, atLeastOnce()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt()); assertEquals(homeConfigOrientation, displayConfig.orientation); assertTrue(displayContent.getPinnedStackController().isPipActiveOrWindowingModeChanging()); } @Test public void testRemoteRotation() { DisplayContent dc = createNewDisplay(); Loading Loading
services/core/java/com/android/server/wm/ActivityStack.java +16 −0 Original line number Diff line number Diff line Loading @@ -819,6 +819,22 @@ class ActivityStack extends Task { mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS); mRootWindowContainer.resumeFocusedStacksTopActivities(); final boolean pinnedToFullscreen = currentMode == WINDOWING_MODE_PINNED && windowingMode == WINDOWING_MODE_FULLSCREEN; if (pinnedToFullscreen && topActivity != null && !isForceHidden()) { mDisplayContent.getPinnedStackController().setPipWindowingModeChanging(true); try { // Report orientation as soon as possible so that the display can freeze earlier if // the display orientation will be changed. Because the surface bounds of activity // may have been set to fullscreen but the activity hasn't redrawn its content yet, // the rotation animation needs to capture snapshot earlier to avoid animating from // an intermediate state. topActivity.reportDescendantOrientationChangeIfNeeded(); } finally { mDisplayContent.getPinnedStackController().setPipWindowingModeChanging(false); } } } @Override Loading
services/core/java/com/android/server/wm/DisplayContent.java +4 −0 Original line number Diff line number Diff line Loading @@ -1481,6 +1481,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo // has it own policy for bounds, the activity bounds based on parent is unknown. return false; } if (mPinnedStackControllerLocked.isPipActiveOrWindowingModeChanging()) { // Use normal rotation animation because seamless PiP rotation is not supported yet. return false; } setFixedRotationLaunchingApp(r, rotation); return true; Loading
services/core/java/com/android/server/wm/PinnedStackController.java +17 −4 Original line number Diff line number Diff line Loading @@ -23,7 +23,6 @@ import android.app.RemoteAction; import android.content.ComponentName; import android.content.pm.ParceledListSlice; import android.content.res.Resources; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.util.DisplayMetrics; Loading @@ -33,8 +32,6 @@ import android.view.DisplayInfo; import android.view.IPinnedStackController; import android.view.IPinnedStackListener; import com.android.server.UiThread; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; Loading @@ -61,7 +58,6 @@ class PinnedStackController { private final WindowManagerService mService; private final DisplayContent mDisplayContent; private final Handler mHandler = UiThread.getHandler(); private IPinnedStackListener mPinnedStackListener; private final PinnedStackListenerDeathHandler mPinnedStackListenerDeathHandler = Loading @@ -69,6 +65,9 @@ class PinnedStackController { private final PinnedStackControllerCallback mCallbacks = new PinnedStackControllerCallback(); /** Whether the PiP is entering or leaving. */ private boolean mIsPipWindowingModeChanging; private boolean mIsImeShowing; private int mImeHeight; Loading Loading @@ -161,6 +160,20 @@ class PinnedStackController { Float.compare(aspectRatio, mMaxAspectRatio) <= 0; } /** Returns {@code true} if the PiP is on screen or is changing windowing mode. */ boolean isPipActiveOrWindowingModeChanging() { if (mIsPipWindowingModeChanging) { return true; } final Task pinnedTask = mDisplayContent.getDefaultTaskDisplayArea().getRootPinnedTask(); return pinnedTask != null && pinnedTask.hasChild(); } /** Sets whether a visible stack is changing from or to pinned mode. */ void setPipWindowingModeChanging(boolean isPipWindowingModeChanging) { mIsPipWindowingModeChanging = isPipWindowingModeChanging; } /** * Activity is hidden (either stopped or removed), resets the last saved snap fraction * so that the default bounds will be returned for the next session. Loading
services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +56 −0 Original line number Diff line number Diff line Loading @@ -16,7 +16,10 @@ package com.android.server.wm; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; Loading Loading @@ -73,6 +76,8 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import android.annotation.SuppressLint; Loading Loading @@ -1167,6 +1172,57 @@ public class DisplayContentTests extends WindowTestsBase { ROTATION_90 /* newRotation */, false /* forceUpdate */)); } @Test public void testNoFixedRotationWithPip() { mWm.mIsFixedRotationTransformEnabled = true; // Make resume-top really update the activity state. doReturn(false).when(mWm.mAtmService).isBooting(); doReturn(true).when(mWm.mAtmService).isBooted(); // Speed up the test by a few seconds. mWm.mAtmService.deferWindowLayout(); doNothing().when(mWm).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt()); final DisplayContent displayContent = mWm.mRoot.getDefaultDisplay(); final Configuration displayConfig = displayContent.getConfiguration(); final ActivityRecord pinnedActivity = createActivityRecord(displayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD); final Task pinnedTask = pinnedActivity.getRootTask(); final ActivityRecord homeActivity = WindowTestUtils.createTestActivityRecord( displayContent.getDefaultTaskDisplayArea().getOrCreateRootHomeTask()); if (displayConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { homeActivity.setOrientation(SCREEN_ORIENTATION_PORTRAIT); pinnedActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); } else { homeActivity.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); pinnedActivity.setOrientation(SCREEN_ORIENTATION_PORTRAIT); } final int homeConfigOrientation = homeActivity.getRequestedConfigurationOrientation(); final int pinnedConfigOrientation = pinnedActivity.getRequestedConfigurationOrientation(); assertEquals(homeConfigOrientation, displayConfig.orientation); clearInvocations(mWm); // Leave PiP to fullscreen. The orientation can be updated from // ActivityRecord#reportDescendantOrientationChangeIfNeeded. pinnedTask.setWindowingMode(WINDOWING_MODE_FULLSCREEN); homeActivity.setState(ActivityStack.ActivityState.STOPPED, "test"); assertFalse(displayContent.hasTopFixedRotationLaunchingApp()); verify(mWm, atLeastOnce()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt()); assertEquals(pinnedConfigOrientation, displayConfig.orientation); assertFalse(displayContent.getPinnedStackController().isPipActiveOrWindowingModeChanging()); clearInvocations(mWm); // Enter PiP from fullscreen. The orientation can be updated from // ensure-visibility/resume-focused-stack -> ActivityRecord#makeActiveIfNeeded -> resume. pinnedTask.setWindowingMode(WINDOWING_MODE_PINNED); assertFalse(displayContent.hasTopFixedRotationLaunchingApp()); verify(mWm, atLeastOnce()).startFreezingDisplay(anyInt(), anyInt(), any(), anyInt()); assertEquals(homeConfigOrientation, displayConfig.orientation); assertTrue(displayContent.getPinnedStackController().isPipActiveOrWindowingModeChanging()); } @Test public void testRemoteRotation() { DisplayContent dc = createNewDisplay(); Loading