Loading packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java +16 −3 Original line number Diff line number Diff line Loading @@ -65,6 +65,10 @@ public class PipAnimationController { @Retention(RetentionPolicy.SOURCE) public @interface TransitionDirection {} public static boolean isInPipDirection(@TransitionDirection int direction) { return direction == TRANSITION_DIRECTION_TO_PIP; } public static boolean isOutPipDirection(@TransitionDirection int direction) { return direction == TRANSITION_DIRECTION_TO_FULLSCREEN || direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN; Loading Loading @@ -104,6 +108,12 @@ public class PipAnimationController { if (mCurrentAnimator == null) { mCurrentAnimator = setupPipTransitionAnimator( PipTransitionAnimator.ofBounds(leash, startBounds, endBounds)); } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA && mCurrentAnimator.isRunning()) { // If we are still animating the fade into pip, then just move the surface and ensure // we update with the new destination bounds, but don't interrupt the existing animation // with a new bounds mCurrentAnimator.setDestinationBounds(endBounds); } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_BOUNDS && mCurrentAnimator.isRunning()) { mCurrentAnimator.setDestinationBounds(endBounds); Loading Loading @@ -265,8 +275,7 @@ public class PipAnimationController { boolean inScaleTransition() { if (mAnimationType != ANIM_TYPE_BOUNDS) return false; final int direction = getTransitionDirection(); return !isOutPipDirection(direction) && direction != TRANSITION_DIRECTION_TO_PIP; return !isInPipDirection(getTransitionDirection()); } /** Loading Loading @@ -354,7 +363,11 @@ public class PipAnimationController { getCastedFractionValue(start.bottom, end.bottom, fraction)); setCurrentValue(mTmpRect); if (inScaleTransition()) { if (isOutPipDirection(getTransitionDirection())) { getSurfaceTransactionHelper().scale(tx, leash, end, mTmpRect); } else { getSurfaceTransactionHelper().scale(tx, leash, start, mTmpRect); } } else { getSurfaceTransactionHelper().crop(tx, leash, mTmpRect); } Loading packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java +4 −0 Original line number Diff line number Diff line Loading @@ -209,6 +209,10 @@ public class PipBoundsHandler { return new Rect(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); } public int getDisplayRotation() { return mDisplayInfo.rotation; } /** * Responds to IPinnedStackListener on {@link DisplayInfo} change. * It will normally follow up with a Loading packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +147 −38 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.systemui.pip; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA; Loading @@ -25,25 +28,30 @@ import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTI import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_FULLSCREEN; import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_SPLIT_SCREEN; import static com.android.systemui.pip.PipAnimationController.isInPipDirection; import static com.android.systemui.pip.PipAnimationController.isOutPipDirection; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.PictureInPictureParams; import android.content.ComponentName; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.util.Log; import android.util.Size; import android.view.SurfaceControl; import android.window.TaskOrganizer; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import android.window.WindowContainerTransactionCallback; import android.window.WindowOrganizer; import com.android.internal.os.SomeArgs; Loading @@ -51,6 +59,7 @@ import com.android.systemui.R; import com.android.systemui.pip.phone.PipUpdateThread; import com.android.systemui.stackdivider.Divider; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; Loading Loading @@ -90,7 +99,7 @@ public class PipTaskOrganizer extends TaskOrganizer { private final Rect mLastReportedBounds = new Rect(); private final int mEnterExitAnimationDuration; private final PipSurfaceTransactionHelper mSurfaceTransactionHelper; private final Map<IBinder, Rect> mBoundsToRestore = new HashMap<>(); private final Map<IBinder, Configuration> mInitialState = new HashMap<>(); private final Divider mSplitDivider; // These callbacks are called on the update thread Loading @@ -110,7 +119,8 @@ public class PipTaskOrganizer extends TaskOrganizer { @Override public void onPipAnimationEnd(SurfaceControl.Transaction tx, PipAnimationController.PipTransitionAnimator animator) { finishResize(tx, animator.getDestinationBounds(), animator.getTransitionDirection()); finishResize(tx, animator.getDestinationBounds(), animator.getTransitionDirection(), animator.getAnimationType()); mMainHandler.post(() -> { for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); Loading Loading @@ -170,7 +180,7 @@ public class PipTaskOrganizer extends TaskOrganizer { case MSG_FINISH_RESIZE: { SurfaceControl.Transaction tx = (SurfaceControl.Transaction) args.arg2; Rect toBounds = (Rect) args.arg3; finishResize(tx, toBounds, args.argi1 /* direction */); finishResize(tx, toBounds, args.argi1 /* direction */, -1); if (updateBoundsCallback != null) { updateBoundsCallback.accept(toBounds); } Loading Loading @@ -240,29 +250,77 @@ public class PipTaskOrganizer extends TaskOrganizer { } /** * Dismiss PiP, this is done in two phases using {@link WindowContainerTransaction} * - setActivityWindowingMode to undefined at beginning of the transaction. without changing * the windowing mode of the Task itself. This makes sure the activity render it's final * configuration while the Task is still in PiP. * Expands PiP to the previous bounds, this is done in two phases using * {@link WindowContainerTransaction} * - setActivityWindowingMode to either fullscreen or split-secondary at beginning of the * transaction. without changing the windowing mode of the Task itself. This makes sure the * activity render it's final configuration while the Task is still in PiP. * - setWindowingMode to undefined at the end of transition * @param animationDurationMs duration in millisecond for the exiting PiP transition */ public void dismissPip(int animationDurationMs) { public void exitPip(int animationDurationMs) { if (!mInPip || mToken == null) { Log.wtf(TAG, "Not allowed to dismissPip in current state" Log.wtf(TAG, "Not allowed to exitPip in current state" + " mInPip=" + mInPip + " mToken=" + mToken); return; } final Configuration initialConfig = mInitialState.remove(mToken.asBinder()); final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation() != mPipBoundsHandler.getDisplayRotation(); final WindowContainerTransaction wct = new WindowContainerTransaction(); if (orientationDiffers) { // Don't bother doing an animation if the display rotation differs or if it's in // a non-supported windowing mode wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); WindowOrganizer.applyTransaction(wct); final Rect destinationBounds = mBoundsToRestore.remove(mToken.asBinder()); mInPip = false; } else { final Rect destinationBounds = initialConfig.windowConfiguration.getBounds(); final int direction = syncWithSplitScreenBounds(destinationBounds) ? TRANSITION_DIRECTION_TO_SPLIT_SCREEN : TRANSITION_DIRECTION_TO_FULLSCREEN; ? TRANSITION_DIRECTION_TO_SPLIT_SCREEN : TRANSITION_DIRECTION_TO_FULLSCREEN; final SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mLastReportedBounds); tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height()); wct.setActivityWindowingMode(mToken, direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY : WINDOWING_MODE_FULLSCREEN); wct.setBounds(mToken, destinationBounds); wct.setBoundsChangeTransaction(mToken, tx); applySyncTransaction(wct, new WindowContainerTransactionCallback() { @Override public void onTransactionReady(int id, SurfaceControl.Transaction t) { t.apply(); scheduleAnimateResizePip(mLastReportedBounds, destinationBounds, direction, animationDurationMs, null /* updateBoundsCallback */); mInPip = false; } }); } } /** * Removes PiP immediately. */ public void removePip() { if (!mInPip || mToken == null) { Log.wtf(TAG, "Not allowed to removePip in current state" + " mInPip=" + mInPip + " mToken=" + mToken); return; } getUpdateHandler().post(() -> { try { ActivityTaskManager.getService().removeStacksInWindowingModes( new int[]{ WINDOWING_MODE_PINNED }); } catch (RemoteException e) { Log.e(TAG, "Failed to remove PiP", e); } }); mInitialState.remove(mToken.asBinder()); } @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) { Loading @@ -277,19 +335,37 @@ public class PipTaskOrganizer extends TaskOrganizer { mInPip = true; mLeash = leash; // TODO: Skip enter animation when entering pip from another orientation final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); mBoundsToRestore.put(mToken.asBinder(), currentBounds); mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration)); if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { scheduleAnimateResizePip(currentBounds, destinationBounds, TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration, null /* updateBoundsCallback */); } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { // 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 // while to process final SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); tx.setAlpha(mLeash, 0f); tx.apply(); final WindowContainerTransaction wct = new WindowContainerTransaction(); wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); wct.setBounds(mToken, destinationBounds); wct.scheduleFinishEnterPip(mToken, destinationBounds); applySyncTransaction(wct, new WindowContainerTransactionCallback() { @Override public void onTransactionReady(int id, SurfaceControl.Transaction t) { t.apply(); mUpdateHandler.post(() -> mPipAnimationController .getAnimator(mLeash, destinationBounds, 0f, 1f) .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration) .start()); } }); mOneShotAnimationType = ANIM_TYPE_BOUNDS; } else { throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType); Loading @@ -297,7 +373,7 @@ public class PipTaskOrganizer extends TaskOrganizer { } /** * Note that dismissing PiP is now originated from SystemUI, see {@link #dismissPip(int)}. * Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int)}. * Meanwhile this callback is invoked whenever the task is removed. For instance: * - as a result of removeStacksInWindowingModes from WM * - activity itself is died Loading Loading @@ -411,6 +487,7 @@ public class PipTaskOrganizer extends TaskOrganizer { // can be initiated in other component, ignore if we are no longer in PIP return; } SomeArgs args = SomeArgs.obtain(); args.arg1 = updateBoundsCallback; args.arg2 = currentBounds; Loading Loading @@ -518,7 +595,7 @@ public class PipTaskOrganizer extends TaskOrganizer { throw new RuntimeException("Callers should call scheduleResizePip() instead of this " + "directly"); } // Could happen when dismissPip // Could happen when exitPip if (mToken == null || mLeash == null) { Log.w(TAG, "Abort animation, invalid leash"); return; Loading @@ -535,7 +612,7 @@ public class PipTaskOrganizer extends TaskOrganizer { throw new RuntimeException("Callers should call scheduleUserResizePip() instead of " + "this directly"); } // Could happen when dismissPip // Could happen when exitPip if (mToken == null || mLeash == null) { Log.w(TAG, "Abort animation, invalid leash"); return; Loading @@ -546,32 +623,43 @@ public class PipTaskOrganizer extends TaskOrganizer { } private void finishResize(SurfaceControl.Transaction tx, Rect destinationBounds, @PipAnimationController.TransitionDirection int direction) { @PipAnimationController.TransitionDirection int direction, @PipAnimationController.AnimationType int type) { if (Looper.myLooper() != mUpdateHandler.getLooper()) { throw new RuntimeException("Callers should call scheduleResizePip() instead of this " + "directly"); } mLastReportedBounds.set(destinationBounds); if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA) { return; } final WindowContainerTransaction wct = new WindowContainerTransaction(); final Rect taskBounds; if (isOutPipDirection(direction)) { if (isInPipDirection(direction)) { // If we are animating from fullscreen using a bounds animation, then reset the // activity windowing mode set by WM, and set the task bounds to the final bounds taskBounds = destinationBounds; wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); wct.scheduleFinishEnterPip(mToken, destinationBounds); } else if (isOutPipDirection(direction)) { // If we are animating to fullscreen, then we need to reset the override bounds // on the task to ensure that the task "matches" the parent's bounds. taskBounds = (direction == TRANSITION_DIRECTION_TO_FULLSCREEN) ? null : destinationBounds; // As for the final windowing mode, simply reset it to undefined. // As for the final windowing mode, simply reset it to undefined and reset the activity // mode set prior to the animation running wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); if (mSplitDivider != null && direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN) { wct.reparent(mToken, mSplitDivider.getSecondaryRoot(), true /* onTop */); } } else { // Just a resize in PIP taskBounds = destinationBounds; } if (direction == TRANSITION_DIRECTION_TO_PIP) { wct.scheduleFinishEnterPip(mToken, taskBounds); } else { wct.setBounds(mToken, taskBounds); } wct.setBoundsChangeTransaction(mToken, tx); WindowOrganizer.applyTransaction(wct); } Loading @@ -582,17 +670,17 @@ public class PipTaskOrganizer extends TaskOrganizer { throw new RuntimeException("Callers should call scheduleAnimateResizePip() instead of " + "this directly"); } // Could happen when dismissPip // Could happen when exitPip if (mToken == null || mLeash == null) { Log.w(TAG, "Abort animation, invalid leash"); return; } mUpdateHandler.post(() -> mPipAnimationController mPipAnimationController .getAnimator(mLeash, currentBounds, destinationBounds) .setTransitionDirection(direction) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(durationMs) .start()); .start(); } private Size getMinimalSize(ActivityInfo activityInfo) { Loading Loading @@ -632,6 +720,27 @@ public class PipTaskOrganizer extends TaskOrganizer { return true; } /** * Dumps internal states. */ public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); pw.println(innerPrefix + "mTaskInfo=" + mTaskInfo); pw.println(innerPrefix + "mToken=" + mToken + " binder=" + (mToken != null ? mToken.asBinder() : null)); pw.println(innerPrefix + "mLeash=" + mLeash); pw.println(innerPrefix + "mInPip=" + mInPip); pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType); pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams); pw.println(innerPrefix + "mLastReportedBounds=" + mLastReportedBounds); pw.println(innerPrefix + "mInitialState:"); for (Map.Entry<IBinder, Configuration> e : mInitialState.entrySet()) { pw.println(innerPrefix + " binder=" + e.getKey() + " winConfig=" + e.getValue().windowConfiguration); } } /** * Callback interface for PiP transitions (both from and to PiP mode) */ Loading packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java +1 −1 Original line number Diff line number Diff line Loading @@ -101,7 +101,7 @@ public class PipAccessibilityInteractionConnection result = true; break; case AccessibilityNodeInfo.ACTION_EXPAND: mMotionHelper.expandPip(); mMotionHelper.expandPipToFullscreen(); result = true; break; default: Loading packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +5 −6 Original line number Diff line number Diff line Loading @@ -25,7 +25,6 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.IActivityManager; import android.app.IActivityTaskManager; import android.content.ComponentName; import android.content.Context; import android.content.pm.ParceledListSlice; Loading Loading @@ -140,7 +139,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio != WINDOWING_MODE_PINNED) { return; } mTouchHandler.getMotionHelper().expandPip(clearedTask /* skipAnimation */); mTouchHandler.getMotionHelper().expandPipToFullscreen(clearedTask /* skipAnimation */); } }; Loading Loading @@ -214,7 +213,6 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio } ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); final IActivityTaskManager activityTaskManager = ActivityTaskManager.getService(); mPipBoundsHandler = pipBoundsHandler; mPipTaskOrganizer = pipTaskOrganizer; mPipTaskOrganizer.registerPipTransitionCallback(this); Loading @@ -222,7 +220,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher); mMenuController = new PipMenuActivityController(context, mMediaController, mInputConsumerController); mTouchHandler = new PipTouchHandler(context, mActivityManager, activityTaskManager, mTouchHandler = new PipTouchHandler(context, mActivityManager, mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer, floatingContentCoordinator, deviceConfig, pipSnapAlgorithm); mAppOpsListener = new PipAppOpsListener(context, mActivityManager, Loading @@ -237,7 +235,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio try { mPipTaskOrganizer.registerOrganizer(WINDOWING_MODE_PINNED); ActivityManager.StackInfo stackInfo = activityTaskManager.getStackInfo( ActivityManager.StackInfo stackInfo = ActivityTaskManager.getService().getStackInfo( WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); if (stackInfo != null) { // If SystemUI restart, and it already existed a pinned stack, Loading @@ -261,7 +259,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio */ @Override public void expandPip() { mTouchHandler.getMotionHelper().expandPip(false /* skipAnimation */); mTouchHandler.getMotionHelper().expandPipToFullscreen(false /* skipAnimation */); } /** Loading Loading @@ -378,5 +376,6 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mMenuController.dump(pw, innerPrefix); mTouchHandler.dump(pw, innerPrefix); mPipBoundsHandler.dump(pw, innerPrefix); mPipTaskOrganizer.dump(pw, innerPrefix); } } Loading
packages/SystemUI/src/com/android/systemui/pip/PipAnimationController.java +16 −3 Original line number Diff line number Diff line Loading @@ -65,6 +65,10 @@ public class PipAnimationController { @Retention(RetentionPolicy.SOURCE) public @interface TransitionDirection {} public static boolean isInPipDirection(@TransitionDirection int direction) { return direction == TRANSITION_DIRECTION_TO_PIP; } public static boolean isOutPipDirection(@TransitionDirection int direction) { return direction == TRANSITION_DIRECTION_TO_FULLSCREEN || direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN; Loading Loading @@ -104,6 +108,12 @@ public class PipAnimationController { if (mCurrentAnimator == null) { mCurrentAnimator = setupPipTransitionAnimator( PipTransitionAnimator.ofBounds(leash, startBounds, endBounds)); } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA && mCurrentAnimator.isRunning()) { // If we are still animating the fade into pip, then just move the surface and ensure // we update with the new destination bounds, but don't interrupt the existing animation // with a new bounds mCurrentAnimator.setDestinationBounds(endBounds); } else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_BOUNDS && mCurrentAnimator.isRunning()) { mCurrentAnimator.setDestinationBounds(endBounds); Loading Loading @@ -265,8 +275,7 @@ public class PipAnimationController { boolean inScaleTransition() { if (mAnimationType != ANIM_TYPE_BOUNDS) return false; final int direction = getTransitionDirection(); return !isOutPipDirection(direction) && direction != TRANSITION_DIRECTION_TO_PIP; return !isInPipDirection(getTransitionDirection()); } /** Loading Loading @@ -354,7 +363,11 @@ public class PipAnimationController { getCastedFractionValue(start.bottom, end.bottom, fraction)); setCurrentValue(mTmpRect); if (inScaleTransition()) { if (isOutPipDirection(getTransitionDirection())) { getSurfaceTransactionHelper().scale(tx, leash, end, mTmpRect); } else { getSurfaceTransactionHelper().scale(tx, leash, start, mTmpRect); } } else { getSurfaceTransactionHelper().crop(tx, leash, mTmpRect); } Loading
packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java +4 −0 Original line number Diff line number Diff line Loading @@ -209,6 +209,10 @@ public class PipBoundsHandler { return new Rect(0, 0, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight); } public int getDisplayRotation() { return mDisplayInfo.rotation; } /** * Responds to IPinnedStackListener on {@link DisplayInfo} change. * It will normally follow up with a Loading
packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +147 −38 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.systemui.pip; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static com.android.systemui.pip.PipAnimationController.ANIM_TYPE_ALPHA; Loading @@ -25,25 +28,30 @@ import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTI import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_FULLSCREEN; import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_PIP; import static com.android.systemui.pip.PipAnimationController.TRANSITION_DIRECTION_TO_SPLIT_SCREEN; import static com.android.systemui.pip.PipAnimationController.isInPipDirection; import static com.android.systemui.pip.PipAnimationController.isOutPipDirection; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.PictureInPictureParams; import android.content.ComponentName; import android.content.Context; import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.util.Log; import android.util.Size; import android.view.SurfaceControl; import android.window.TaskOrganizer; import android.window.WindowContainerToken; import android.window.WindowContainerTransaction; import android.window.WindowContainerTransactionCallback; import android.window.WindowOrganizer; import com.android.internal.os.SomeArgs; Loading @@ -51,6 +59,7 @@ import com.android.systemui.R; import com.android.systemui.pip.phone.PipUpdateThread; import com.android.systemui.stackdivider.Divider; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; Loading Loading @@ -90,7 +99,7 @@ public class PipTaskOrganizer extends TaskOrganizer { private final Rect mLastReportedBounds = new Rect(); private final int mEnterExitAnimationDuration; private final PipSurfaceTransactionHelper mSurfaceTransactionHelper; private final Map<IBinder, Rect> mBoundsToRestore = new HashMap<>(); private final Map<IBinder, Configuration> mInitialState = new HashMap<>(); private final Divider mSplitDivider; // These callbacks are called on the update thread Loading @@ -110,7 +119,8 @@ public class PipTaskOrganizer extends TaskOrganizer { @Override public void onPipAnimationEnd(SurfaceControl.Transaction tx, PipAnimationController.PipTransitionAnimator animator) { finishResize(tx, animator.getDestinationBounds(), animator.getTransitionDirection()); finishResize(tx, animator.getDestinationBounds(), animator.getTransitionDirection(), animator.getAnimationType()); mMainHandler.post(() -> { for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); Loading Loading @@ -170,7 +180,7 @@ public class PipTaskOrganizer extends TaskOrganizer { case MSG_FINISH_RESIZE: { SurfaceControl.Transaction tx = (SurfaceControl.Transaction) args.arg2; Rect toBounds = (Rect) args.arg3; finishResize(tx, toBounds, args.argi1 /* direction */); finishResize(tx, toBounds, args.argi1 /* direction */, -1); if (updateBoundsCallback != null) { updateBoundsCallback.accept(toBounds); } Loading Loading @@ -240,29 +250,77 @@ public class PipTaskOrganizer extends TaskOrganizer { } /** * Dismiss PiP, this is done in two phases using {@link WindowContainerTransaction} * - setActivityWindowingMode to undefined at beginning of the transaction. without changing * the windowing mode of the Task itself. This makes sure the activity render it's final * configuration while the Task is still in PiP. * Expands PiP to the previous bounds, this is done in two phases using * {@link WindowContainerTransaction} * - setActivityWindowingMode to either fullscreen or split-secondary at beginning of the * transaction. without changing the windowing mode of the Task itself. This makes sure the * activity render it's final configuration while the Task is still in PiP. * - setWindowingMode to undefined at the end of transition * @param animationDurationMs duration in millisecond for the exiting PiP transition */ public void dismissPip(int animationDurationMs) { public void exitPip(int animationDurationMs) { if (!mInPip || mToken == null) { Log.wtf(TAG, "Not allowed to dismissPip in current state" Log.wtf(TAG, "Not allowed to exitPip in current state" + " mInPip=" + mInPip + " mToken=" + mToken); return; } final Configuration initialConfig = mInitialState.remove(mToken.asBinder()); final boolean orientationDiffers = initialConfig.windowConfiguration.getRotation() != mPipBoundsHandler.getDisplayRotation(); final WindowContainerTransaction wct = new WindowContainerTransaction(); if (orientationDiffers) { // Don't bother doing an animation if the display rotation differs or if it's in // a non-supported windowing mode wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); WindowOrganizer.applyTransaction(wct); final Rect destinationBounds = mBoundsToRestore.remove(mToken.asBinder()); mInPip = false; } else { final Rect destinationBounds = initialConfig.windowConfiguration.getBounds(); final int direction = syncWithSplitScreenBounds(destinationBounds) ? TRANSITION_DIRECTION_TO_SPLIT_SCREEN : TRANSITION_DIRECTION_TO_FULLSCREEN; ? TRANSITION_DIRECTION_TO_SPLIT_SCREEN : TRANSITION_DIRECTION_TO_FULLSCREEN; final SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); mSurfaceTransactionHelper.scale(tx, mLeash, destinationBounds, mLastReportedBounds); tx.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height()); wct.setActivityWindowingMode(mToken, direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY : WINDOWING_MODE_FULLSCREEN); wct.setBounds(mToken, destinationBounds); wct.setBoundsChangeTransaction(mToken, tx); applySyncTransaction(wct, new WindowContainerTransactionCallback() { @Override public void onTransactionReady(int id, SurfaceControl.Transaction t) { t.apply(); scheduleAnimateResizePip(mLastReportedBounds, destinationBounds, direction, animationDurationMs, null /* updateBoundsCallback */); mInPip = false; } }); } } /** * Removes PiP immediately. */ public void removePip() { if (!mInPip || mToken == null) { Log.wtf(TAG, "Not allowed to removePip in current state" + " mInPip=" + mInPip + " mToken=" + mToken); return; } getUpdateHandler().post(() -> { try { ActivityTaskManager.getService().removeStacksInWindowingModes( new int[]{ WINDOWING_MODE_PINNED }); } catch (RemoteException e) { Log.e(TAG, "Failed to remove PiP", e); } }); mInitialState.remove(mToken.asBinder()); } @Override public void onTaskAppeared(ActivityManager.RunningTaskInfo info, SurfaceControl leash) { Loading @@ -277,19 +335,37 @@ public class PipTaskOrganizer extends TaskOrganizer { mInPip = true; mLeash = leash; // TODO: Skip enter animation when entering pip from another orientation final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds(); mBoundsToRestore.put(mToken.asBinder(), currentBounds); mInitialState.put(mToken.asBinder(), new Configuration(mTaskInfo.configuration)); if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) { scheduleAnimateResizePip(currentBounds, destinationBounds, TRANSITION_DIRECTION_TO_PIP, mEnterExitAnimationDuration, null /* updateBoundsCallback */); } else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) { // 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 // while to process final SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); tx.setAlpha(mLeash, 0f); tx.apply(); final WindowContainerTransaction wct = new WindowContainerTransaction(); wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); wct.setBounds(mToken, destinationBounds); wct.scheduleFinishEnterPip(mToken, destinationBounds); applySyncTransaction(wct, new WindowContainerTransactionCallback() { @Override public void onTransactionReady(int id, SurfaceControl.Transaction t) { t.apply(); mUpdateHandler.post(() -> mPipAnimationController .getAnimator(mLeash, destinationBounds, 0f, 1f) .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration) .start()); } }); mOneShotAnimationType = ANIM_TYPE_BOUNDS; } else { throw new RuntimeException("Unrecognized animation type: " + mOneShotAnimationType); Loading @@ -297,7 +373,7 @@ public class PipTaskOrganizer extends TaskOrganizer { } /** * Note that dismissing PiP is now originated from SystemUI, see {@link #dismissPip(int)}. * Note that dismissing PiP is now originated from SystemUI, see {@link #exitPip(int)}. * Meanwhile this callback is invoked whenever the task is removed. For instance: * - as a result of removeStacksInWindowingModes from WM * - activity itself is died Loading Loading @@ -411,6 +487,7 @@ public class PipTaskOrganizer extends TaskOrganizer { // can be initiated in other component, ignore if we are no longer in PIP return; } SomeArgs args = SomeArgs.obtain(); args.arg1 = updateBoundsCallback; args.arg2 = currentBounds; Loading Loading @@ -518,7 +595,7 @@ public class PipTaskOrganizer extends TaskOrganizer { throw new RuntimeException("Callers should call scheduleResizePip() instead of this " + "directly"); } // Could happen when dismissPip // Could happen when exitPip if (mToken == null || mLeash == null) { Log.w(TAG, "Abort animation, invalid leash"); return; Loading @@ -535,7 +612,7 @@ public class PipTaskOrganizer extends TaskOrganizer { throw new RuntimeException("Callers should call scheduleUserResizePip() instead of " + "this directly"); } // Could happen when dismissPip // Could happen when exitPip if (mToken == null || mLeash == null) { Log.w(TAG, "Abort animation, invalid leash"); return; Loading @@ -546,32 +623,43 @@ public class PipTaskOrganizer extends TaskOrganizer { } private void finishResize(SurfaceControl.Transaction tx, Rect destinationBounds, @PipAnimationController.TransitionDirection int direction) { @PipAnimationController.TransitionDirection int direction, @PipAnimationController.AnimationType int type) { if (Looper.myLooper() != mUpdateHandler.getLooper()) { throw new RuntimeException("Callers should call scheduleResizePip() instead of this " + "directly"); } mLastReportedBounds.set(destinationBounds); if (isInPipDirection(direction) && type == ANIM_TYPE_ALPHA) { return; } final WindowContainerTransaction wct = new WindowContainerTransaction(); final Rect taskBounds; if (isOutPipDirection(direction)) { if (isInPipDirection(direction)) { // If we are animating from fullscreen using a bounds animation, then reset the // activity windowing mode set by WM, and set the task bounds to the final bounds taskBounds = destinationBounds; wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); wct.scheduleFinishEnterPip(mToken, destinationBounds); } else if (isOutPipDirection(direction)) { // If we are animating to fullscreen, then we need to reset the override bounds // on the task to ensure that the task "matches" the parent's bounds. taskBounds = (direction == TRANSITION_DIRECTION_TO_FULLSCREEN) ? null : destinationBounds; // As for the final windowing mode, simply reset it to undefined. // As for the final windowing mode, simply reset it to undefined and reset the activity // mode set prior to the animation running wct.setWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); if (mSplitDivider != null && direction == TRANSITION_DIRECTION_TO_SPLIT_SCREEN) { wct.reparent(mToken, mSplitDivider.getSecondaryRoot(), true /* onTop */); } } else { // Just a resize in PIP taskBounds = destinationBounds; } if (direction == TRANSITION_DIRECTION_TO_PIP) { wct.scheduleFinishEnterPip(mToken, taskBounds); } else { wct.setBounds(mToken, taskBounds); } wct.setBoundsChangeTransaction(mToken, tx); WindowOrganizer.applyTransaction(wct); } Loading @@ -582,17 +670,17 @@ public class PipTaskOrganizer extends TaskOrganizer { throw new RuntimeException("Callers should call scheduleAnimateResizePip() instead of " + "this directly"); } // Could happen when dismissPip // Could happen when exitPip if (mToken == null || mLeash == null) { Log.w(TAG, "Abort animation, invalid leash"); return; } mUpdateHandler.post(() -> mPipAnimationController mPipAnimationController .getAnimator(mLeash, currentBounds, destinationBounds) .setTransitionDirection(direction) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(durationMs) .start()); .start(); } private Size getMinimalSize(ActivityInfo activityInfo) { Loading Loading @@ -632,6 +720,27 @@ public class PipTaskOrganizer extends TaskOrganizer { return true; } /** * Dumps internal states. */ public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); pw.println(innerPrefix + "mTaskInfo=" + mTaskInfo); pw.println(innerPrefix + "mToken=" + mToken + " binder=" + (mToken != null ? mToken.asBinder() : null)); pw.println(innerPrefix + "mLeash=" + mLeash); pw.println(innerPrefix + "mInPip=" + mInPip); pw.println(innerPrefix + "mOneShotAnimationType=" + mOneShotAnimationType); pw.println(innerPrefix + "mPictureInPictureParams=" + mPictureInPictureParams); pw.println(innerPrefix + "mLastReportedBounds=" + mLastReportedBounds); pw.println(innerPrefix + "mInitialState:"); for (Map.Entry<IBinder, Configuration> e : mInitialState.entrySet()) { pw.println(innerPrefix + " binder=" + e.getKey() + " winConfig=" + e.getValue().windowConfiguration); } } /** * Callback interface for PiP transitions (both from and to PiP mode) */ Loading
packages/SystemUI/src/com/android/systemui/pip/phone/PipAccessibilityInteractionConnection.java +1 −1 Original line number Diff line number Diff line Loading @@ -101,7 +101,7 @@ public class PipAccessibilityInteractionConnection result = true; break; case AccessibilityNodeInfo.ACTION_EXPAND: mMotionHelper.expandPip(); mMotionHelper.expandPipToFullscreen(); result = true; break; default: Loading
packages/SystemUI/src/com/android/systemui/pip/phone/PipManager.java +5 −6 Original line number Diff line number Diff line Loading @@ -25,7 +25,6 @@ import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityTaskManager; import android.app.IActivityManager; import android.app.IActivityTaskManager; import android.content.ComponentName; import android.content.Context; import android.content.pm.ParceledListSlice; Loading Loading @@ -140,7 +139,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio != WINDOWING_MODE_PINNED) { return; } mTouchHandler.getMotionHelper().expandPip(clearedTask /* skipAnimation */); mTouchHandler.getMotionHelper().expandPipToFullscreen(clearedTask /* skipAnimation */); } }; Loading Loading @@ -214,7 +213,6 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio } ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); final IActivityTaskManager activityTaskManager = ActivityTaskManager.getService(); mPipBoundsHandler = pipBoundsHandler; mPipTaskOrganizer = pipTaskOrganizer; mPipTaskOrganizer.registerPipTransitionCallback(this); Loading @@ -222,7 +220,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mMediaController = new PipMediaController(context, mActivityManager, broadcastDispatcher); mMenuController = new PipMenuActivityController(context, mMediaController, mInputConsumerController); mTouchHandler = new PipTouchHandler(context, mActivityManager, activityTaskManager, mTouchHandler = new PipTouchHandler(context, mActivityManager, mMenuController, mInputConsumerController, mPipBoundsHandler, mPipTaskOrganizer, floatingContentCoordinator, deviceConfig, pipSnapAlgorithm); mAppOpsListener = new PipAppOpsListener(context, mActivityManager, Loading @@ -237,7 +235,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio try { mPipTaskOrganizer.registerOrganizer(WINDOWING_MODE_PINNED); ActivityManager.StackInfo stackInfo = activityTaskManager.getStackInfo( ActivityManager.StackInfo stackInfo = ActivityTaskManager.getService().getStackInfo( WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED); if (stackInfo != null) { // If SystemUI restart, and it already existed a pinned stack, Loading @@ -261,7 +259,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio */ @Override public void expandPip() { mTouchHandler.getMotionHelper().expandPip(false /* skipAnimation */); mTouchHandler.getMotionHelper().expandPipToFullscreen(false /* skipAnimation */); } /** Loading Loading @@ -378,5 +376,6 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio mMenuController.dump(pw, innerPrefix); mTouchHandler.dump(pw, innerPrefix); mPipBoundsHandler.dump(pw, innerPrefix); mPipTaskOrganizer.dump(pw, innerPrefix); } }