Loading core/java/android/view/IDisplayWindowListener.aidl +9 −0 Original line number Original line Diff line number Diff line Loading @@ -46,4 +46,13 @@ oneway interface IDisplayWindowListener { */ */ void onDisplayRemoved(int displayId); void onDisplayRemoved(int displayId); /** * Called when fixed rotation is started on a display. */ void onFixedRotationStarted(int displayId, int newRotation); /** * Called when the previous fixed rotation on a display is finished. */ void onFixedRotationFinished(int displayId); } } packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +84 −32 Original line number Original line Diff line number Diff line Loading @@ -58,6 +58,7 @@ import com.android.internal.os.SomeArgs; import com.android.systemui.R; import com.android.systemui.R; import com.android.systemui.pip.phone.PipUpdateThread; import com.android.systemui.pip.phone.PipUpdateThread; import com.android.systemui.stackdivider.Divider; import com.android.systemui.stackdivider.Divider; import com.android.systemui.wm.DisplayController; import java.io.PrintWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.ArrayList; Loading @@ -82,8 +83,10 @@ import javax.inject.Singleton; * see also {@link com.android.systemui.pip.phone.PipMotionHelper}. * see also {@link com.android.systemui.pip.phone.PipMotionHelper}. */ */ @Singleton @Singleton public class PipTaskOrganizer extends TaskOrganizer { public class PipTaskOrganizer extends TaskOrganizer implements DisplayController.OnDisplaysChangedListener { private static final String TAG = PipTaskOrganizer.class.getSimpleName(); private static final String TAG = PipTaskOrganizer.class.getSimpleName(); private static final boolean DEBUG = false; private static final int MSG_RESIZE_IMMEDIATE = 1; private static final int MSG_RESIZE_IMMEDIATE = 1; private static final int MSG_RESIZE_ANIMATE = 2; private static final int MSG_RESIZE_ANIMATE = 2; Loading Loading @@ -206,10 +209,17 @@ public class PipTaskOrganizer extends TaskOrganizer { mSurfaceControlTransactionFactory; mSurfaceControlTransactionFactory; private PictureInPictureParams mPictureInPictureParams; private PictureInPictureParams mPictureInPictureParams; /** * If set to {@code true}, the entering animation will be skipped and we will wait for * {@link #onFixedRotationFinished(int)} callback to actually enter PiP. */ private boolean mShouldDeferEnteringPip; @Inject @Inject public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler, public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler, @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, @Nullable Divider divider) { @Nullable Divider divider, @NonNull DisplayController displayController) { mMainHandler = new Handler(Looper.getMainLooper()); mMainHandler = new Handler(Looper.getMainLooper()); mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks); mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks); mPipBoundsHandler = boundsHandler; mPipBoundsHandler = boundsHandler; Loading @@ -219,6 +229,7 @@ public class PipTaskOrganizer extends TaskOrganizer { mPipAnimationController = new PipAnimationController(context, surfaceTransactionHelper); mPipAnimationController = new PipAnimationController(context, surfaceTransactionHelper); mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; mSplitDivider = divider; mSplitDivider = divider; displayController.addDisplayWindowListener(this); } } public Handler getUpdateHandler() { public Handler getUpdateHandler() { Loading Loading @@ -281,7 +292,8 @@ public class PipTaskOrganizer extends TaskOrganizer { final int direction = syncWithSplitScreenBounds(destinationBounds) final int direction = syncWithSplitScreenBounds(destinationBounds) ? TRANSITION_DIRECTION_TO_SPLIT_SCREEN ? TRANSITION_DIRECTION_TO_SPLIT_SCREEN : TRANSITION_DIRECTION_TO_FULLSCREEN; : TRANSITION_DIRECTION_TO_FULLSCREEN; final SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mLastReportedBounds); mLastReportedBounds); tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height()); tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height()); Loading Loading @@ -325,29 +337,47 @@ public class PipTaskOrganizer extends TaskOrganizer { @Override @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) { public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) { Objects.requireNonNull(info, "Requires RunningTaskInfo"); Objects.requireNonNull(info, "Requires RunningTaskInfo"); mPictureInPictureParams = info.pictureInPictureParams; final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( info.topActivity, getAspectRatioOrDefault(mPictureInPictureParams), null /* bounds */, getMinimalSize(info.topActivityInfo)); Objects.requireNonNull(destinationBounds, "Missing destination bounds"); mTaskInfo = info; mTaskInfo = info; mToken = mTaskInfo.token; mToken = mTaskInfo.token; mInPip = true; mInPip = true; mLeash = leash; mLeash = leash; mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration)); mPictureInPictureParams = mTaskInfo.pictureInPictureParams; // TODO: Skip enter animation when entering pip from another orientation if (mShouldDeferEnteringPip) { if (DEBUG) Log.d(TAG, "Defer entering PiP animation, fixed rotation is ongoing"); // if deferred, hide the surface till fixed rotation is completed final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); tx.setAlpha(mLeash, 0f); tx.apply(); return; } final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams), null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo)); Objects.requireNonNull(destinationBounds, "Missing destination bounds"); final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration)); if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { scheduleAnimateResizePip(currentBounds, destinationBounds, scheduleAnimateResizePip(currentBounds, destinationBounds, TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration, TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration, null /* updateBoundsCallback */); null /* updateBoundsCallback */); } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { enterPipWithAlphaAnimation(destinationBounds, mEnterExitAnimationDuration); mOneShotAnimationType = ANIM_TYPE_BOUNDS; } else { throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType); } } private void enterPipWithAlphaAnimation(Rect destinationBounds, long durationMs) { // If we are fading the PIP in, then we should move the pip to the final location as // If we are fading the PIP in, then we should move the pip to the final location as // soon as possible, but set the alpha immediately since the transaction can take a // soon as possible, but set the alpha immediately since the transaction can take a // while to process // while to process final SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); tx.setAlpha(mLeash, 0f); tx.setAlpha(mLeash, 0f); tx.apply(); tx.apply(); final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction wct = new WindowContainerTransaction(); Loading @@ -362,14 +392,10 @@ public class PipTaskOrganizer extends TaskOrganizer { .getAnimator(mLeash, destinationBounds, 0f, 1f) .getAnimator(mLeash, destinationBounds, 0f, 1f) .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) .setPipAnimationCallback(mPipAnimationCallback) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration) .setDuration(durationMs) .start()); .start()); } } }); }); mOneShotAnimationType = ANIM_TYPE_BOUNDS; } else { throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType); } } } /** /** Loading @@ -391,6 +417,7 @@ public class PipTaskOrganizer extends TaskOrganizer { Log.wtf(TAG, "Unrecognized token: " + token); Log.wtf(TAG, "Unrecognized token: " + token); return; return; } } mShouldDeferEnteringPip = false; mPictureInPictureParams = null; mPictureInPictureParams = null; mInPip = false; mInPip = false; } } Loading @@ -416,6 +443,23 @@ public class PipTaskOrganizer extends TaskOrganizer { // Do nothing // Do nothing } } @Override public void onFixedRotationStarted(int displayId, int newRotation) { mShouldDeferEnteringPip = true; } @Override public void onFixedRotationFinished(int displayId) { if (mShouldDeferEnteringPip && mInPip) { final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams), null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo)); // schedule a regular animation to ensure all the callbacks are still being sent enterPipWithAlphaAnimation(destinationBounds, 0 /* durationMs */); } mShouldDeferEnteringPip = false; } /** /** * TODO(b/152809058): consolidate the display info handling logic in SysUI * TODO(b/152809058): consolidate the display info handling logic in SysUI * * Loading Loading @@ -476,6 +520,10 @@ public class PipTaskOrganizer extends TaskOrganizer { */ */ public void scheduleAnimateResizePip(Rect toBounds, int duration, public void scheduleAnimateResizePip(Rect toBounds, int duration, Consumer<Rect> updateBoundsCallback) { Consumer<Rect> updateBoundsCallback) { if (mShouldDeferEnteringPip) { Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred"); return; } scheduleAnimateResizePip(mLastReportedBounds, toBounds, scheduleAnimateResizePip(mLastReportedBounds, toBounds, TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback); TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback); } } Loading Loading @@ -567,6 +615,10 @@ public class PipTaskOrganizer extends TaskOrganizer { // can be initiated in other component, ignore if we are no longer in PIP // can be initiated in other component, ignore if we are no longer in PIP return; return; } } if (mShouldDeferEnteringPip) { Log.d(TAG, "skip scheduleOffsetPip, entering pip deferred"); return; } SomeArgs args = SomeArgs.obtain(); SomeArgs args = SomeArgs.obtain(); args.arg1 = updateBoundsCallback; args.arg1 = updateBoundsCallback; args.arg2 = originalBounds; args.arg2 = originalBounds; Loading packages/SystemUI/src/com/android/systemui/wm/DisplayController.java +43 −0 Original line number Original line Diff line number Diff line Loading @@ -134,6 +134,39 @@ public class DisplayController { } } }); }); } } @Override public void onFixedRotationStarted(int displayId, int newRotation) { mHandler.post(() -> { synchronized (mDisplays) { if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { Slog.w(TAG, "Skipping onFixedRotationStarted on unknown" + " display, displayId=" + displayId); return; } for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { mDisplayChangedListeners.get(i).onFixedRotationStarted( displayId, newRotation); } } }); } @Override public void onFixedRotationFinished(int displayId) { mHandler.post(() -> { synchronized (mDisplays) { if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { Slog.w(TAG, "Skipping onFixedRotationFinished on unknown" + " display, displayId=" + displayId); return; } for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { mDisplayChangedListeners.get(i).onFixedRotationFinished(displayId); } } }); } }; }; @Inject @Inject Loading Loading @@ -232,5 +265,15 @@ public class DisplayController { * Called when a display is removed. * Called when a display is removed. */ */ default void onDisplayRemoved(int displayId) {} default void onDisplayRemoved(int displayId) {} /** * Called when fixed rotation on a display is started. */ default void onFixedRotationStarted(int displayId, int newRotation) {} /** * Called when fixed rotation on a display is finished. */ default void onFixedRotationFinished(int displayId) {} } } } } services/core/java/com/android/server/wm/DisplayContent.java +24 −7 Original line number Original line Diff line number Diff line Loading @@ -493,10 +493,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * The launching activity which is using fixed rotation transformation. * The launching activity which is using fixed rotation transformation. * * * @see #handleTopActivityLaunchingInDifferentOrientation * @see #handleTopActivityLaunchingInDifferentOrientation * @see #setFixedRotationLaunchingApp * @see #setFixedRotationLaunchingApp(ActivityRecord, int) * @see DisplayRotation#shouldRotateSeamlessly * @see DisplayRotation#shouldRotateSeamlessly */ */ ActivityRecord mFixedRotationLaunchingApp; private ActivityRecord mFixedRotationLaunchingApp; final FixedRotationTransitionListener mFixedRotationTransitionListener = final FixedRotationTransitionListener mFixedRotationTransitionListener = new FixedRotationTransitionListener(); new FixedRotationTransitionListener(); Loading Loading @@ -1475,6 +1475,23 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return true; return true; } } @Nullable ActivityRecord getFixedRotationLaunchingApp() { return mFixedRotationLaunchingApp; } void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r) { setFixedRotationLaunchingAppUnchecked(r, ROTATION_UNDEFINED); } void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r, int rotation) { if (mFixedRotationLaunchingApp == null && r != null) { mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this, rotation); } else if (mFixedRotationLaunchingApp != null && r == null) { mWmService.mDisplayNotificationController.dispatchFixedRotationFinished(this); } mFixedRotationLaunchingApp = r; } /** /** * Sets the provided record to {@link mFixedRotationLaunchingApp} if possible to apply fixed * Sets the provided record to {@link mFixedRotationLaunchingApp} if possible to apply fixed * rotation transform to it and indicate that the display may be rotated after it is launched. * rotation transform to it and indicate that the display may be rotated after it is launched. Loading @@ -1496,7 +1513,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (!r.hasFixedRotationTransform()) { if (!r.hasFixedRotationTransform()) { startFixedRotationTransform(r, rotation); startFixedRotationTransform(r, rotation); } } mFixedRotationLaunchingApp = r; setFixedRotationLaunchingAppUnchecked(r, rotation); if (prevRotatedLaunchingApp != null) { if (prevRotatedLaunchingApp != null) { prevRotatedLaunchingApp.finishFixedRotationTransform(); prevRotatedLaunchingApp.finishFixedRotationTransform(); } } Loading Loading @@ -1524,7 +1541,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } /** /** * Clears the {@link mFixedRotationLaunchingApp} without applying rotation to display. It is * Clears the {@link #mFixedRotationLaunchingApp} without applying rotation to display. It is * used when the display won't rotate (e.g. the orientation from sensor has updated again before * used when the display won't rotate (e.g. the orientation from sensor has updated again before * applying rotation to display) but the launching app has been transformed. So the record need * applying rotation to display) but the launching app has been transformed. So the record need * to be cleared and restored to stop using seamless rotation and rotated configuration. * to be cleared and restored to stop using seamless rotation and rotated configuration. Loading @@ -1534,7 +1551,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return; return; } } mFixedRotationLaunchingApp.finishFixedRotationTransform(); mFixedRotationLaunchingApp.finishFixedRotationTransform(); mFixedRotationLaunchingApp = null; setFixedRotationLaunchingAppUnchecked(null); } } private void startFixedRotationTransform(WindowToken token, int rotation) { private void startFixedRotationTransform(WindowToken token, int rotation) { Loading Loading @@ -5260,7 +5277,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo rotatedLaunchingApp.finishFixedRotationTransform( rotatedLaunchingApp.finishFixedRotationTransform( () -> applyRotation(oldRotation, newRotation)); () -> applyRotation(oldRotation, newRotation)); mFixedRotationLaunchingApp = null; setFixedRotationLaunchingAppUnchecked(null); } } /** Checks whether the given activity is in size compatibility mode and notifies the change. */ /** Checks whether the given activity is in size compatibility mode and notifies the change. */ Loading Loading @@ -5574,7 +5591,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (animatingRecents != null && animatingRecents == mFixedRotationLaunchingApp) { if (animatingRecents != null && animatingRecents == mFixedRotationLaunchingApp) { // Because it won't affect display orientation, just finish the transform. // Because it won't affect display orientation, just finish the transform. animatingRecents.finishFixedRotationTransform(); animatingRecents.finishFixedRotationTransform(); mFixedRotationLaunchingApp = null; setFixedRotationLaunchingAppUnchecked(null); } else { } else { // If there is already a launching activity that is not the recents, before its // If there is already a launching activity that is not the recents, before its // transition is completed, the recents animation may be started. So if the recents // transition is completed, the recents animation may be started. So if the recents Loading services/core/java/com/android/server/wm/DisplayRotation.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -577,7 +577,7 @@ public class DisplayRotation { boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) { boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) { // Display doesn't need to be frozen because application has been started in correct // Display doesn't need to be frozen because application has been started in correct // rotation already, so the rest of the windows can use seamless rotation. // rotation already, so the rest of the windows can use seamless rotation. if (mDisplayContent.mFixedRotationLaunchingApp != null) { if (mDisplayContent.getFixedRotationLaunchingApp() != null) { return true; return true; } } Loading Loading
core/java/android/view/IDisplayWindowListener.aidl +9 −0 Original line number Original line Diff line number Diff line Loading @@ -46,4 +46,13 @@ oneway interface IDisplayWindowListener { */ */ void onDisplayRemoved(int displayId); void onDisplayRemoved(int displayId); /** * Called when fixed rotation is started on a display. */ void onFixedRotationStarted(int displayId, int newRotation); /** * Called when the previous fixed rotation on a display is finished. */ void onFixedRotationFinished(int displayId); } }
packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +84 −32 Original line number Original line Diff line number Diff line Loading @@ -58,6 +58,7 @@ import com.android.internal.os.SomeArgs; import com.android.systemui.R; import com.android.systemui.R; import com.android.systemui.pip.phone.PipUpdateThread; import com.android.systemui.pip.phone.PipUpdateThread; import com.android.systemui.stackdivider.Divider; import com.android.systemui.stackdivider.Divider; import com.android.systemui.wm.DisplayController; import java.io.PrintWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.ArrayList; Loading @@ -82,8 +83,10 @@ import javax.inject.Singleton; * see also {@link com.android.systemui.pip.phone.PipMotionHelper}. * see also {@link com.android.systemui.pip.phone.PipMotionHelper}. */ */ @Singleton @Singleton public class PipTaskOrganizer extends TaskOrganizer { public class PipTaskOrganizer extends TaskOrganizer implements DisplayController.OnDisplaysChangedListener { private static final String TAG = PipTaskOrganizer.class.getSimpleName(); private static final String TAG = PipTaskOrganizer.class.getSimpleName(); private static final boolean DEBUG = false; private static final int MSG_RESIZE_IMMEDIATE = 1; private static final int MSG_RESIZE_IMMEDIATE = 1; private static final int MSG_RESIZE_ANIMATE = 2; private static final int MSG_RESIZE_ANIMATE = 2; Loading Loading @@ -206,10 +209,17 @@ public class PipTaskOrganizer extends TaskOrganizer { mSurfaceControlTransactionFactory; mSurfaceControlTransactionFactory; private PictureInPictureParams mPictureInPictureParams; private PictureInPictureParams mPictureInPictureParams; /** * If set to {@code true}, the entering animation will be skipped and we will wait for * {@link #onFixedRotationFinished(int)} callback to actually enter PiP. */ private boolean mShouldDeferEnteringPip; @Inject @Inject public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler, public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler, @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, @NonNull PipSurfaceTransactionHelper surfaceTransactionHelper, @Nullable Divider divider) { @Nullable Divider divider, @NonNull DisplayController displayController) { mMainHandler = new Handler(Looper.getMainLooper()); mMainHandler = new Handler(Looper.getMainLooper()); mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks); mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks); mPipBoundsHandler = boundsHandler; mPipBoundsHandler = boundsHandler; Loading @@ -219,6 +229,7 @@ public class PipTaskOrganizer extends TaskOrganizer { mPipAnimationController = new PipAnimationController(context, surfaceTransactionHelper); mPipAnimationController = new PipAnimationController(context, surfaceTransactionHelper); mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; mSplitDivider = divider; mSplitDivider = divider; displayController.addDisplayWindowListener(this); } } public Handler getUpdateHandler() { public Handler getUpdateHandler() { Loading Loading @@ -281,7 +292,8 @@ public class PipTaskOrganizer extends TaskOrganizer { final int direction = syncWithSplitScreenBounds(destinationBounds) final int direction = syncWithSplitScreenBounds(destinationBounds) ? TRANSITION_DIRECTION_TO_SPLIT_SCREEN ? TRANSITION_DIRECTION_TO_SPLIT_SCREEN : TRANSITION_DIRECTION_TO_FULLSCREEN; : TRANSITION_DIRECTION_TO_FULLSCREEN; final SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mLastReportedBounds); mLastReportedBounds); tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height()); tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height()); Loading Loading @@ -325,29 +337,47 @@ public class PipTaskOrganizer extends TaskOrganizer { @Override @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) { public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) { Objects.requireNonNull(info, "Requires RunningTaskInfo"); Objects.requireNonNull(info, "Requires RunningTaskInfo"); mPictureInPictureParams = info.pictureInPictureParams; final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( info.topActivity, getAspectRatioOrDefault(mPictureInPictureParams), null /* bounds */, getMinimalSize(info.topActivityInfo)); Objects.requireNonNull(destinationBounds, "Missing destination bounds"); mTaskInfo = info; mTaskInfo = info; mToken = mTaskInfo.token; mToken = mTaskInfo.token; mInPip = true; mInPip = true; mLeash = leash; mLeash = leash; mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration)); mPictureInPictureParams = mTaskInfo.pictureInPictureParams; // TODO: Skip enter animation when entering pip from another orientation if (mShouldDeferEnteringPip) { if (DEBUG) Log.d(TAG, "Defer entering PiP animation, fixed rotation is ongoing"); // if deferred, hide the surface till fixed rotation is completed final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); tx.setAlpha(mLeash, 0f); tx.apply(); return; } final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams), null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo)); Objects.requireNonNull(destinationBounds, "Missing destination bounds"); final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration)); if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { scheduleAnimateResizePip(currentBounds, destinationBounds, scheduleAnimateResizePip(currentBounds, destinationBounds, TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration, TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration, null /* updateBoundsCallback */); null /* updateBoundsCallback */); } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { enterPipWithAlphaAnimation(destinationBounds, mEnterExitAnimationDuration); mOneShotAnimationType = ANIM_TYPE_BOUNDS; } else { throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType); } } private void enterPipWithAlphaAnimation(Rect destinationBounds, long durationMs) { // If we are fading the PIP in, then we should move the pip to the final location as // If we are fading the PIP in, then we should move the pip to the final location as // soon as possible, but set the alpha immediately since the transaction can take a // soon as possible, but set the alpha immediately since the transaction can take a // while to process // while to process final SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); tx.setAlpha(mLeash, 0f); tx.setAlpha(mLeash, 0f); tx.apply(); tx.apply(); final WindowContainerTransaction wct = new WindowContainerTransaction(); final WindowContainerTransaction wct = new WindowContainerTransaction(); Loading @@ -362,14 +392,10 @@ public class PipTaskOrganizer extends TaskOrganizer { .getAnimator(mLeash, destinationBounds, 0f, 1f) .getAnimator(mLeash, destinationBounds, 0f, 1f) .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) .setPipAnimationCallback(mPipAnimationCallback) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration) .setDuration(durationMs) .start()); .start()); } } }); }); mOneShotAnimationType = ANIM_TYPE_BOUNDS; } else { throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType); } } } /** /** Loading @@ -391,6 +417,7 @@ public class PipTaskOrganizer extends TaskOrganizer { Log.wtf(TAG, "Unrecognized token: " + token); Log.wtf(TAG, "Unrecognized token: " + token); return; return; } } mShouldDeferEnteringPip = false; mPictureInPictureParams = null; mPictureInPictureParams = null; mInPip = false; mInPip = false; } } Loading @@ -416,6 +443,23 @@ public class PipTaskOrganizer extends TaskOrganizer { // Do nothing // Do nothing } } @Override public void onFixedRotationStarted(int displayId, int newRotation) { mShouldDeferEnteringPip = true; } @Override public void onFixedRotationFinished(int displayId) { if (mShouldDeferEnteringPip && mInPip) { final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds( mTaskInfo.topActivity, getAspectRatioOrDefault(mPictureInPictureParams), null /* bounds */, getMinimalSize(mTaskInfo.topActivityInfo)); // schedule a regular animation to ensure all the callbacks are still being sent enterPipWithAlphaAnimation(destinationBounds, 0 /* durationMs */); } mShouldDeferEnteringPip = false; } /** /** * TODO(b/152809058): consolidate the display info handling logic in SysUI * TODO(b/152809058): consolidate the display info handling logic in SysUI * * Loading Loading @@ -476,6 +520,10 @@ public class PipTaskOrganizer extends TaskOrganizer { */ */ public void scheduleAnimateResizePip(Rect toBounds, int duration, public void scheduleAnimateResizePip(Rect toBounds, int duration, Consumer<Rect> updateBoundsCallback) { Consumer<Rect> updateBoundsCallback) { if (mShouldDeferEnteringPip) { Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred"); return; } scheduleAnimateResizePip(mLastReportedBounds, toBounds, scheduleAnimateResizePip(mLastReportedBounds, toBounds, TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback); TRANSITION_DIRECTION_NONE, duration, updateBoundsCallback); } } Loading Loading @@ -567,6 +615,10 @@ public class PipTaskOrganizer extends TaskOrganizer { // can be initiated in other component, ignore if we are no longer in PIP // can be initiated in other component, ignore if we are no longer in PIP return; return; } } if (mShouldDeferEnteringPip) { Log.d(TAG, "skip scheduleOffsetPip, entering pip deferred"); return; } SomeArgs args = SomeArgs.obtain(); SomeArgs args = SomeArgs.obtain(); args.arg1 = updateBoundsCallback; args.arg1 = updateBoundsCallback; args.arg2 = originalBounds; args.arg2 = originalBounds; Loading
packages/SystemUI/src/com/android/systemui/wm/DisplayController.java +43 −0 Original line number Original line Diff line number Diff line Loading @@ -134,6 +134,39 @@ public class DisplayController { } } }); }); } } @Override public void onFixedRotationStarted(int displayId, int newRotation) { mHandler.post(() -> { synchronized (mDisplays) { if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { Slog.w(TAG, "Skipping onFixedRotationStarted on unknown" + " display, displayId=" + displayId); return; } for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { mDisplayChangedListeners.get(i).onFixedRotationStarted( displayId, newRotation); } } }); } @Override public void onFixedRotationFinished(int displayId) { mHandler.post(() -> { synchronized (mDisplays) { if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { Slog.w(TAG, "Skipping onFixedRotationFinished on unknown" + " display, displayId=" + displayId); return; } for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { mDisplayChangedListeners.get(i).onFixedRotationFinished(displayId); } } }); } }; }; @Inject @Inject Loading Loading @@ -232,5 +265,15 @@ public class DisplayController { * Called when a display is removed. * Called when a display is removed. */ */ default void onDisplayRemoved(int displayId) {} default void onDisplayRemoved(int displayId) {} /** * Called when fixed rotation on a display is started. */ default void onFixedRotationStarted(int displayId, int newRotation) {} /** * Called when fixed rotation on a display is finished. */ default void onFixedRotationFinished(int displayId) {} } } } }
services/core/java/com/android/server/wm/DisplayContent.java +24 −7 Original line number Original line Diff line number Diff line Loading @@ -493,10 +493,10 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo * The launching activity which is using fixed rotation transformation. * The launching activity which is using fixed rotation transformation. * * * @see #handleTopActivityLaunchingInDifferentOrientation * @see #handleTopActivityLaunchingInDifferentOrientation * @see #setFixedRotationLaunchingApp * @see #setFixedRotationLaunchingApp(ActivityRecord, int) * @see DisplayRotation#shouldRotateSeamlessly * @see DisplayRotation#shouldRotateSeamlessly */ */ ActivityRecord mFixedRotationLaunchingApp; private ActivityRecord mFixedRotationLaunchingApp; final FixedRotationTransitionListener mFixedRotationTransitionListener = final FixedRotationTransitionListener mFixedRotationTransitionListener = new FixedRotationTransitionListener(); new FixedRotationTransitionListener(); Loading Loading @@ -1475,6 +1475,23 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return true; return true; } } @Nullable ActivityRecord getFixedRotationLaunchingApp() { return mFixedRotationLaunchingApp; } void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r) { setFixedRotationLaunchingAppUnchecked(r, ROTATION_UNDEFINED); } void setFixedRotationLaunchingAppUnchecked(@Nullable ActivityRecord r, int rotation) { if (mFixedRotationLaunchingApp == null && r != null) { mWmService.mDisplayNotificationController.dispatchFixedRotationStarted(this, rotation); } else if (mFixedRotationLaunchingApp != null && r == null) { mWmService.mDisplayNotificationController.dispatchFixedRotationFinished(this); } mFixedRotationLaunchingApp = r; } /** /** * Sets the provided record to {@link mFixedRotationLaunchingApp} if possible to apply fixed * Sets the provided record to {@link mFixedRotationLaunchingApp} if possible to apply fixed * rotation transform to it and indicate that the display may be rotated after it is launched. * rotation transform to it and indicate that the display may be rotated after it is launched. Loading @@ -1496,7 +1513,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (!r.hasFixedRotationTransform()) { if (!r.hasFixedRotationTransform()) { startFixedRotationTransform(r, rotation); startFixedRotationTransform(r, rotation); } } mFixedRotationLaunchingApp = r; setFixedRotationLaunchingAppUnchecked(r, rotation); if (prevRotatedLaunchingApp != null) { if (prevRotatedLaunchingApp != null) { prevRotatedLaunchingApp.finishFixedRotationTransform(); prevRotatedLaunchingApp.finishFixedRotationTransform(); } } Loading Loading @@ -1524,7 +1541,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo } } /** /** * Clears the {@link mFixedRotationLaunchingApp} without applying rotation to display. It is * Clears the {@link #mFixedRotationLaunchingApp} without applying rotation to display. It is * used when the display won't rotate (e.g. the orientation from sensor has updated again before * used when the display won't rotate (e.g. the orientation from sensor has updated again before * applying rotation to display) but the launching app has been transformed. So the record need * applying rotation to display) but the launching app has been transformed. So the record need * to be cleared and restored to stop using seamless rotation and rotated configuration. * to be cleared and restored to stop using seamless rotation and rotated configuration. Loading @@ -1534,7 +1551,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return; return; } } mFixedRotationLaunchingApp.finishFixedRotationTransform(); mFixedRotationLaunchingApp.finishFixedRotationTransform(); mFixedRotationLaunchingApp = null; setFixedRotationLaunchingAppUnchecked(null); } } private void startFixedRotationTransform(WindowToken token, int rotation) { private void startFixedRotationTransform(WindowToken token, int rotation) { Loading Loading @@ -5260,7 +5277,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo rotatedLaunchingApp.finishFixedRotationTransform( rotatedLaunchingApp.finishFixedRotationTransform( () -> applyRotation(oldRotation, newRotation)); () -> applyRotation(oldRotation, newRotation)); mFixedRotationLaunchingApp = null; setFixedRotationLaunchingAppUnchecked(null); } } /** Checks whether the given activity is in size compatibility mode and notifies the change. */ /** Checks whether the given activity is in size compatibility mode and notifies the change. */ Loading Loading @@ -5574,7 +5591,7 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (animatingRecents != null && animatingRecents == mFixedRotationLaunchingApp) { if (animatingRecents != null && animatingRecents == mFixedRotationLaunchingApp) { // Because it won't affect display orientation, just finish the transform. // Because it won't affect display orientation, just finish the transform. animatingRecents.finishFixedRotationTransform(); animatingRecents.finishFixedRotationTransform(); mFixedRotationLaunchingApp = null; setFixedRotationLaunchingAppUnchecked(null); } else { } else { // If there is already a launching activity that is not the recents, before its // If there is already a launching activity that is not the recents, before its // transition is completed, the recents animation may be started. So if the recents // transition is completed, the recents animation may be started. So if the recents Loading
services/core/java/com/android/server/wm/DisplayRotation.java +1 −1 Original line number Original line Diff line number Diff line Loading @@ -577,7 +577,7 @@ public class DisplayRotation { boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) { boolean shouldRotateSeamlessly(int oldRotation, int newRotation, boolean forceUpdate) { // Display doesn't need to be frozen because application has been started in correct // Display doesn't need to be frozen because application has been started in correct // rotation already, so the rest of the windows can use seamless rotation. // rotation already, so the rest of the windows can use seamless rotation. if (mDisplayContent.mFixedRotationLaunchingApp != null) { if (mDisplayContent.getFixedRotationLaunchingApp() != null) { return true; return true; } } Loading