Loading libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java +10 −4 Original line number Diff line number Diff line Loading @@ -132,6 +132,7 @@ public abstract class Pip2Module { PhonePipMenuController menuPhoneController, PipBoundsAlgorithm pipBoundsAlgorithm, @NonNull PipBoundsState pipBoundsState, @NonNull PipTransitionState pipTransitionState, @NonNull SizeSpecSource sizeSpecSource, PipMotionHelper pipMotionHelper, FloatingContentCoordinator floatingContentCoordinator, Loading @@ -139,8 +140,9 @@ public abstract class Pip2Module { @ShellMainThread ShellExecutor mainExecutor, Optional<PipPerfHintController> pipPerfHintControllerOptional) { return new PipTouchHandler(context, shellInit, menuPhoneController, pipBoundsAlgorithm, pipBoundsState, sizeSpecSource, pipMotionHelper, floatingContentCoordinator, pipUiEventLogger, mainExecutor, pipPerfHintControllerOptional); pipBoundsState, pipTransitionState, sizeSpecSource, pipMotionHelper, floatingContentCoordinator, pipUiEventLogger, mainExecutor, pipPerfHintControllerOptional); } @WMSingleton Loading @@ -149,9 +151,13 @@ public abstract class Pip2Module { PipBoundsState pipBoundsState, PhonePipMenuController menuController, PipSnapAlgorithm pipSnapAlgorithm, FloatingContentCoordinator floatingContentCoordinator, Optional<PipPerfHintController> pipPerfHintControllerOptional) { PipScheduler pipScheduler, Optional<PipPerfHintController> pipPerfHintControllerOptional, PipBoundsAlgorithm pipBoundsAlgorithm, PipTransitionState pipTransitionState) { return new PipMotionHelper(context, pipBoundsState, menuController, pipSnapAlgorithm, floatingContentCoordinator, pipPerfHintControllerOptional); floatingContentCoordinator, pipScheduler, pipPerfHintControllerOptional, pipBoundsAlgorithm, pipTransitionState); } @WMSingleton Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java +1 −1 Original line number Diff line number Diff line Loading @@ -53,7 +53,7 @@ public class PipInputConsumer { * Listener interface for callers to learn when this class is registered or unregistered with * window manager */ private interface RegistrationListener { interface RegistrationListener { void onRegistrationChanged(boolean isRegistered); } Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java +89 −33 Original line number Diff line number Diff line Loading @@ -31,7 +31,9 @@ import android.annotation.Nullable; import android.content.Context; import android.graphics.PointF; import android.graphics.Rect; import android.os.Bundle; import android.os.Debug; import android.view.SurfaceControl; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.R; Loading @@ -39,6 +41,7 @@ import com.android.wm.shell.animation.FloatProperties; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.magnetictarget.MagnetizedObject; import com.android.wm.shell.common.pip.PipAppOpsListener; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.common.pip.PipPerfHintController; import com.android.wm.shell.common.pip.PipSnapAlgorithm; Loading @@ -57,6 +60,7 @@ import java.util.function.Consumer; public class PipMotionHelper implements PipAppOpsListener.Callback, FloatingContentCoordinator.FloatingContent { private static final String TAG = "PipMotionHelper"; private static final String FLING_BOUNDS_CHANGE = "fling_bounds_change"; private static final boolean DEBUG = false; private static final int SHRINK_STACK_FROM_MENU_DURATION = 250; Loading @@ -72,7 +76,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private final Context mContext; private @NonNull PipBoundsState mPipBoundsState; private @NonNull PipBoundsAlgorithm mPipBoundsAlgorithm; private @NonNull PipScheduler mPipScheduler; private @NonNull PipTransitionState mPipTransitionState; private PhonePipMenuController mMenuController; private PipSnapAlgorithm mSnapAlgorithm; Loading Loading @@ -144,6 +150,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, */ private boolean mDismissalPending = false; /** * Set to true if bounds change transition has been scheduled from PipMotionHelper. */ private boolean mWaitingForBoundsChangeTransition = false; /** * Gets set in {@link #animateToExpandedState(Rect, Rect, Rect, Runnable)}, this callback is * used to show menu activity when the expand animation is completed. Loading @@ -152,22 +163,25 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, public PipMotionHelper(Context context, @NonNull PipBoundsState pipBoundsState, PhonePipMenuController menuController, PipSnapAlgorithm snapAlgorithm, FloatingContentCoordinator floatingContentCoordinator, Optional<PipPerfHintController> pipPerfHintControllerOptional) { FloatingContentCoordinator floatingContentCoordinator, PipScheduler pipScheduler, Optional<PipPerfHintController> pipPerfHintControllerOptional, PipBoundsAlgorithm pipBoundsAlgorithm, PipTransitionState pipTransitionState) { mContext = context; mPipBoundsState = pipBoundsState; mPipBoundsAlgorithm = pipBoundsAlgorithm; mPipScheduler = pipScheduler; mMenuController = menuController; mSnapAlgorithm = snapAlgorithm; mFloatingContentCoordinator = floatingContentCoordinator; mPipPerfHintController = pipPerfHintControllerOptional.orElse(null); mResizePipUpdateListener = (target, values) -> { if (mPipBoundsState.getMotionBoundsState().isInMotion()) { /* mPipTaskOrganizer.scheduleUserResizePip(getBounds(), mPipBoundsState.getMotionBoundsState().getBoundsInMotion(), null); */ mPipScheduler.scheduleUserResizePip( mPipBoundsState.getMotionBoundsState().getBoundsInMotion()); } }; mPipTransitionState = pipTransitionState; mPipTransitionState.addPipTransitionStateChangedListener(this::onPipTransitionStateChanged); } void init() { Loading Loading @@ -236,12 +250,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, mPipBoundsState.setBounds(toBounds); } else { mPipBoundsState.getMotionBoundsState().setBoundsInMotion(toBounds); /* mPipTaskOrganizer.scheduleUserResizePip(getBounds(), toBounds, (Rect newBounds) -> { mMenuController.updateMenuLayout(newBounds); }); */ mPipScheduler.scheduleUserResizePip(toBounds); } } else { // If PIP is 'catching up' after being stuck in the dismiss target, update the animation Loading Loading @@ -552,11 +561,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, /** Set new fling configs whose min/max values respect the given movement bounds. */ private void rebuildFlingConfigs() { mFlingConfigX = new PhysicsAnimator.FlingConfig(DEFAULT_FRICTION, mPipBoundsState.getMovementBounds().left, mPipBoundsState.getMovementBounds().right); mPipBoundsAlgorithm.getMovementBounds(getBounds()).left, mPipBoundsAlgorithm.getMovementBounds(getBounds()).right); mFlingConfigY = new PhysicsAnimator.FlingConfig(DEFAULT_FRICTION, mPipBoundsState.getMovementBounds().top, mPipBoundsState.getMovementBounds().bottom); mPipBoundsAlgorithm.getMovementBounds(getBounds()).top, mPipBoundsAlgorithm.getMovementBounds(getBounds()).bottom); final Rect insetBounds = mPipBoundsState.getDisplayLayout().stableInsets(); mStashConfigX = new PhysicsAnimator.FlingConfig( DEFAULT_FRICTION, Loading Loading @@ -623,22 +632,15 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private void onBoundsPhysicsAnimationEnd() { // The physics animation ended, though we may not necessarily be done animating, such as // when we're still dragging after moving out of the magnetic target. if (!mDismissalPending && !mSpringingToTouch && !mMagnetizedPip.getObjectStuckToTarget()) { // All motion operations have actually finished. mPipBoundsState.setBounds( mPipBoundsState.getMotionBoundsState().getBoundsInMotion()); mPipBoundsState.getMotionBoundsState().onAllAnimationsEnded(); if (!mDismissalPending) { if (!mDismissalPending && !mSpringingToTouch && !mMagnetizedPip.getObjectStuckToTarget()) { // do not schedule resize if PiP is dismissing, which may cause app re-open to // mBounds instead of its normal bounds. // mPipTaskOrganizer.scheduleFinishResizePip(getBounds()); } Bundle extra = new Bundle(); extra.putBoolean(FLING_BOUNDS_CHANGE, true); mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra); return; } mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded(); mSpringingToTouch = false; mDismissalPending = false; settlePipBoundsAfterPhysicsAnimation(true /* animatingAfter */); cleanUpHighPerfSessionMaybe(); } Loading @@ -662,7 +664,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, + " callers=\n%s", TAG, toBounds, Debug.getCallers(5, " ")); } if (!toBounds.equals(getBounds())) { // mPipTaskOrganizer.scheduleResizePip(toBounds, mUpdateBoundsCallback); mPipScheduler.scheduleAnimateResizePip(toBounds); } } Loading @@ -685,6 +687,60 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, // setAnimatingToBounds(toBounds); } private void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState, @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) { switch (newState) { case PipTransitionState.SCHEDULED_BOUNDS_CHANGE: if (!extra.getBoolean(FLING_BOUNDS_CHANGE)) break; // If touch is turned off and we are in a fling animation, schedule a transition. mWaitingForBoundsChangeTransition = true; mPipScheduler.scheduleAnimateResizePip( mPipBoundsState.getMotionBoundsState().getBoundsInMotion()); break; case PipTransitionState.CHANGING_PIP_BOUNDS: if (!mWaitingForBoundsChangeTransition) break; // If bounds change transition was scheduled from this class, handle leash updates. mWaitingForBoundsChangeTransition = false; SurfaceControl.Transaction startTx = extra.getParcelable( PipTransition.PIP_START_TX, SurfaceControl.Transaction.class); Rect destinationBounds = extra.getParcelable( PipTransition.PIP_DESTINATION_BOUNDS, Rect.class); startTx.setPosition(mPipTransitionState.mPinnedTaskLeash, destinationBounds.left, destinationBounds.top); startTx.apply(); // All motion operations have actually finished, so make bounds cache updates. settlePipBoundsAfterPhysicsAnimation(false /* animatingAfter */); cleanUpHighPerfSessionMaybe(); // Setting state to CHANGED_PIP_BOUNDS applies finishTx and notifies Core. mPipTransitionState.setState(PipTransitionState.CHANGED_PIP_BOUNDS); break; case PipTransitionState.EXITING_PIP: // We need to force finish any local animators if about to leave PiP, to avoid // breaking the state (e.g. leashes are cleaned up upon exit). if (!mPipBoundsState.getMotionBoundsState().isInMotion()) break; cancelPhysicsAnimation(); settlePipBoundsAfterPhysicsAnimation(false /* animatingAfter */); } } private void settlePipBoundsAfterPhysicsAnimation(boolean animatingAfter) { if (!animatingAfter) { // The physics animation ended, though we may not necessarily be done animating, such as // when we're still dragging after moving out of the magnetic target. Only set the final // bounds state and clear motion bounds completely if the whole animation is over. mPipBoundsState.setBounds(mPipBoundsState.getMotionBoundsState().getBoundsInMotion()); mPipBoundsState.getMotionBoundsState().onAllAnimationsEnded(); } mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded(); mSpringingToTouch = false; mDismissalPending = false; } /** * Returns a MagnetizedObject wrapper for PIP's animated bounds. This is provided to the * magnetic dismiss target so it can calculate PIP's size and position. Loading libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java +19 −0 Original line number Diff line number Diff line Loading @@ -25,16 +25,19 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Rect; import android.view.SurfaceControl; import android.window.WindowContainerTransaction; import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.protolog.ShellProtoLogGroup; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; Loading Loading @@ -156,4 +159,20 @@ public class PipScheduler { wct.setBounds(mPipTransitionState.mPipTaskToken, toBounds); mPipTransitionController.startResizeTransition(wct); } /** * Directly perform a scaled matrix transformation on the leash. This will not perform any * {@link WindowContainerTransaction}. */ public void scheduleUserResizePip(Rect toBounds) { if (toBounds.isEmpty()) { ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: Attempted to user resize PIP to empty bounds, aborting.", TAG); return; } SurfaceControl leash = mPipTransitionState.mPinnedTaskLeash; final SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); tx.setPosition(leash, toBounds.left, toBounds.top); tx.apply(); } } libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java +49 −12 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.wm.shell.pip2.phone; import static android.view.WindowManager.INPUT_CONSUMER_PIP; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASHING; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASH_MINIMUM_VELOCITY_THRESHOLD; import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_LEFT; Loading @@ -30,18 +32,19 @@ import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_PICTURE_ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.os.Bundle; import android.provider.DeviceConfig; import android.util.Size; import android.view.DisplayCutout; import android.view.InputEvent; import android.view.MotionEvent; import android.view.ViewConfiguration; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; Loading Loading @@ -80,6 +83,7 @@ public class PipTouchHandler { private final Context mContext; private final PipBoundsAlgorithm mPipBoundsAlgorithm; @NonNull private final PipBoundsState mPipBoundsState; @NonNull private final PipTransitionState mPipTransitionState; @NonNull private final SizeSpecSource mSizeSpecSource; private final PipUiEventLogger mPipUiEventLogger; private final PipDismissTargetHandler mPipDismissTargetHandler; Loading Loading @@ -125,6 +129,7 @@ public class PipTouchHandler { private final FloatingContentCoordinator mFloatingContentCoordinator; private PipMotionHelper mMotionHelper; private PipTouchGesture mGesture; private PipInputConsumer mPipInputConsumer; // Temp vars private final Rect mTmpBounds = new Rect(); Loading Loading @@ -167,6 +172,7 @@ public class PipTouchHandler { PhonePipMenuController menuController, PipBoundsAlgorithm pipBoundsAlgorithm, @NonNull PipBoundsState pipBoundsState, @NonNull PipTransitionState pipTransitionState, @NonNull SizeSpecSource sizeSpecSource, PipMotionHelper pipMotionHelper, FloatingContentCoordinator floatingContentCoordinator, Loading @@ -179,6 +185,9 @@ public class PipTouchHandler { mAccessibilityManager = context.getSystemService(AccessibilityManager.class); mPipBoundsAlgorithm = pipBoundsAlgorithm; mPipBoundsState = pipBoundsState; mPipTransitionState = pipTransitionState; mPipTransitionState.addPipTransitionStateChangedListener(this::onPipTransitionStateChanged); mSizeSpecSource = sizeSpecSource; mMenuController = menuController; mPipUiEventLogger = pipUiEventLogger; Loading Loading @@ -227,6 +236,11 @@ public class PipTouchHandler { mPipResizeGestureHandler.init(); mPipDismissTargetHandler.init(); mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(), INPUT_CONSUMER_PIP, mMainExecutor); mPipInputConsumer.setInputListener(this::handleTouchEvent); mPipInputConsumer.setRegistrationListener(this::onRegistrationChanged); mEnableStash = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_SYSTEMUI, PIP_STASHING, Loading Loading @@ -294,19 +308,17 @@ public class PipTouchHandler { void onActivityPinned() { mPipDismissTargetHandler.createOrUpdateDismissTarget(); mPipResizeGestureHandler.onActivityPinned(); mFloatingContentCoordinator.onContentAdded(mMotionHelper); mPipInputConsumer.registerInputConsumer(); } void onActivityUnpinned(ComponentName topPipActivity) { if (topPipActivity == null) { void onActivityUnpinned() { // Clean up state after the last PiP activity is removed mPipDismissTargetHandler.cleanUpDismissTarget(); mFloatingContentCoordinator.onContentRemoved(mMotionHelper); } mPipResizeGestureHandler.onActivityUnpinned(); mPipInputConsumer.unregisterInputConsumer(); } void onPinnedStackAnimationEnded( Loading Loading @@ -512,6 +524,7 @@ public class PipTouchHandler { return true; } /* if ((ev.getAction() == MotionEvent.ACTION_DOWN || mTouchState.isUserInteracting()) && mPipDismissTargetHandler.maybeConsumeMotionEvent(ev)) { // If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event Loading @@ -528,11 +541,13 @@ public class PipTouchHandler { return true; } if (!mTouchState.isUserInteracting()) { // Ignore the motion event When the entry animation is waiting to be started if (!mTouchState.isUserInteracting() && mPipTaskOrganizer.isEntryScheduled()) { ProtoLog.wtf(WM_SHELL_PICTURE_IN_PICTURE, "%s: Waiting to start the entry animation, skip the motion event.", TAG); return true; } */ // Update the touch state mTouchState.onTouchEvent(ev); Loading Loading @@ -808,7 +823,7 @@ public class PipTouchHandler { mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mPipBoundsState.getMovementBounds().bottom; mMotionHelper.setSpringingToTouch(false); // mPipDismissTargetHandler.setTaskLeash(mPipTaskOrganizer.getSurfaceControl()); mPipDismissTargetHandler.setTaskLeash(mPipTransitionState.mPinnedTaskLeash); // If the menu is still visible then just poke the menu // so that it will timeout after the user stops touching it Loading Loading @@ -880,7 +895,8 @@ public class PipTouchHandler { // Reset the touch state on up before the fling settles mTouchState.reset(); if (mEnableStash && shouldStash(vel, getPossiblyMotionBounds())) { mMotionHelper.stashToEdge(vel.x, vel.y, this::stashEndAction /* endAction */); // mMotionHelper.stashToEdge(vel.x, vel.y, // this::stashEndAction /* endAction */); } else { if (mPipBoundsState.isStashed()) { // Reset stashed state if previously stashed Loading Loading @@ -1059,6 +1075,27 @@ public class PipTouchHandler { mPipResizeGestureHandler.setOhmOffset(offset); } private void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState, @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) { switch (newState) { case PipTransitionState.ENTERED_PIP: onActivityPinned(); mTouchState.setAllowInputEvents(true); break; case PipTransitionState.EXITED_PIP: mTouchState.setAllowInputEvents(false); onActivityUnpinned(); break; case PipTransitionState.SCHEDULED_BOUNDS_CHANGE: mTouchState.setAllowInputEvents(false); break; case PipTransitionState.CHANGED_PIP_BOUNDS: mTouchState.setAllowInputEvents(true); break; } } /** * Dumps the {@link PipTouchHandler} state. */ Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/dagger/pip/Pip2Module.java +10 −4 Original line number Diff line number Diff line Loading @@ -132,6 +132,7 @@ public abstract class Pip2Module { PhonePipMenuController menuPhoneController, PipBoundsAlgorithm pipBoundsAlgorithm, @NonNull PipBoundsState pipBoundsState, @NonNull PipTransitionState pipTransitionState, @NonNull SizeSpecSource sizeSpecSource, PipMotionHelper pipMotionHelper, FloatingContentCoordinator floatingContentCoordinator, Loading @@ -139,8 +140,9 @@ public abstract class Pip2Module { @ShellMainThread ShellExecutor mainExecutor, Optional<PipPerfHintController> pipPerfHintControllerOptional) { return new PipTouchHandler(context, shellInit, menuPhoneController, pipBoundsAlgorithm, pipBoundsState, sizeSpecSource, pipMotionHelper, floatingContentCoordinator, pipUiEventLogger, mainExecutor, pipPerfHintControllerOptional); pipBoundsState, pipTransitionState, sizeSpecSource, pipMotionHelper, floatingContentCoordinator, pipUiEventLogger, mainExecutor, pipPerfHintControllerOptional); } @WMSingleton Loading @@ -149,9 +151,13 @@ public abstract class Pip2Module { PipBoundsState pipBoundsState, PhonePipMenuController menuController, PipSnapAlgorithm pipSnapAlgorithm, FloatingContentCoordinator floatingContentCoordinator, Optional<PipPerfHintController> pipPerfHintControllerOptional) { PipScheduler pipScheduler, Optional<PipPerfHintController> pipPerfHintControllerOptional, PipBoundsAlgorithm pipBoundsAlgorithm, PipTransitionState pipTransitionState) { return new PipMotionHelper(context, pipBoundsState, menuController, pipSnapAlgorithm, floatingContentCoordinator, pipPerfHintControllerOptional); floatingContentCoordinator, pipScheduler, pipPerfHintControllerOptional, pipBoundsAlgorithm, pipTransitionState); } @WMSingleton Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipInputConsumer.java +1 −1 Original line number Diff line number Diff line Loading @@ -53,7 +53,7 @@ public class PipInputConsumer { * Listener interface for callers to learn when this class is registered or unregistered with * window manager */ private interface RegistrationListener { interface RegistrationListener { void onRegistrationChanged(boolean isRegistered); } Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipMotionHelper.java +89 −33 Original line number Diff line number Diff line Loading @@ -31,7 +31,9 @@ import android.annotation.Nullable; import android.content.Context; import android.graphics.PointF; import android.graphics.Rect; import android.os.Bundle; import android.os.Debug; import android.view.SurfaceControl; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.R; Loading @@ -39,6 +41,7 @@ import com.android.wm.shell.animation.FloatProperties; import com.android.wm.shell.common.FloatingContentCoordinator; import com.android.wm.shell.common.magnetictarget.MagnetizedObject; import com.android.wm.shell.common.pip.PipAppOpsListener; import com.android.wm.shell.common.pip.PipBoundsAlgorithm; import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.common.pip.PipPerfHintController; import com.android.wm.shell.common.pip.PipSnapAlgorithm; Loading @@ -57,6 +60,7 @@ import java.util.function.Consumer; public class PipMotionHelper implements PipAppOpsListener.Callback, FloatingContentCoordinator.FloatingContent { private static final String TAG = "PipMotionHelper"; private static final String FLING_BOUNDS_CHANGE = "fling_bounds_change"; private static final boolean DEBUG = false; private static final int SHRINK_STACK_FROM_MENU_DURATION = 250; Loading @@ -72,7 +76,9 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private final Context mContext; private @NonNull PipBoundsState mPipBoundsState; private @NonNull PipBoundsAlgorithm mPipBoundsAlgorithm; private @NonNull PipScheduler mPipScheduler; private @NonNull PipTransitionState mPipTransitionState; private PhonePipMenuController mMenuController; private PipSnapAlgorithm mSnapAlgorithm; Loading Loading @@ -144,6 +150,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, */ private boolean mDismissalPending = false; /** * Set to true if bounds change transition has been scheduled from PipMotionHelper. */ private boolean mWaitingForBoundsChangeTransition = false; /** * Gets set in {@link #animateToExpandedState(Rect, Rect, Rect, Runnable)}, this callback is * used to show menu activity when the expand animation is completed. Loading @@ -152,22 +163,25 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, public PipMotionHelper(Context context, @NonNull PipBoundsState pipBoundsState, PhonePipMenuController menuController, PipSnapAlgorithm snapAlgorithm, FloatingContentCoordinator floatingContentCoordinator, Optional<PipPerfHintController> pipPerfHintControllerOptional) { FloatingContentCoordinator floatingContentCoordinator, PipScheduler pipScheduler, Optional<PipPerfHintController> pipPerfHintControllerOptional, PipBoundsAlgorithm pipBoundsAlgorithm, PipTransitionState pipTransitionState) { mContext = context; mPipBoundsState = pipBoundsState; mPipBoundsAlgorithm = pipBoundsAlgorithm; mPipScheduler = pipScheduler; mMenuController = menuController; mSnapAlgorithm = snapAlgorithm; mFloatingContentCoordinator = floatingContentCoordinator; mPipPerfHintController = pipPerfHintControllerOptional.orElse(null); mResizePipUpdateListener = (target, values) -> { if (mPipBoundsState.getMotionBoundsState().isInMotion()) { /* mPipTaskOrganizer.scheduleUserResizePip(getBounds(), mPipBoundsState.getMotionBoundsState().getBoundsInMotion(), null); */ mPipScheduler.scheduleUserResizePip( mPipBoundsState.getMotionBoundsState().getBoundsInMotion()); } }; mPipTransitionState = pipTransitionState; mPipTransitionState.addPipTransitionStateChangedListener(this::onPipTransitionStateChanged); } void init() { Loading Loading @@ -236,12 +250,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, mPipBoundsState.setBounds(toBounds); } else { mPipBoundsState.getMotionBoundsState().setBoundsInMotion(toBounds); /* mPipTaskOrganizer.scheduleUserResizePip(getBounds(), toBounds, (Rect newBounds) -> { mMenuController.updateMenuLayout(newBounds); }); */ mPipScheduler.scheduleUserResizePip(toBounds); } } else { // If PIP is 'catching up' after being stuck in the dismiss target, update the animation Loading Loading @@ -552,11 +561,11 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, /** Set new fling configs whose min/max values respect the given movement bounds. */ private void rebuildFlingConfigs() { mFlingConfigX = new PhysicsAnimator.FlingConfig(DEFAULT_FRICTION, mPipBoundsState.getMovementBounds().left, mPipBoundsState.getMovementBounds().right); mPipBoundsAlgorithm.getMovementBounds(getBounds()).left, mPipBoundsAlgorithm.getMovementBounds(getBounds()).right); mFlingConfigY = new PhysicsAnimator.FlingConfig(DEFAULT_FRICTION, mPipBoundsState.getMovementBounds().top, mPipBoundsState.getMovementBounds().bottom); mPipBoundsAlgorithm.getMovementBounds(getBounds()).top, mPipBoundsAlgorithm.getMovementBounds(getBounds()).bottom); final Rect insetBounds = mPipBoundsState.getDisplayLayout().stableInsets(); mStashConfigX = new PhysicsAnimator.FlingConfig( DEFAULT_FRICTION, Loading Loading @@ -623,22 +632,15 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private void onBoundsPhysicsAnimationEnd() { // The physics animation ended, though we may not necessarily be done animating, such as // when we're still dragging after moving out of the magnetic target. if (!mDismissalPending && !mSpringingToTouch && !mMagnetizedPip.getObjectStuckToTarget()) { // All motion operations have actually finished. mPipBoundsState.setBounds( mPipBoundsState.getMotionBoundsState().getBoundsInMotion()); mPipBoundsState.getMotionBoundsState().onAllAnimationsEnded(); if (!mDismissalPending) { if (!mDismissalPending && !mSpringingToTouch && !mMagnetizedPip.getObjectStuckToTarget()) { // do not schedule resize if PiP is dismissing, which may cause app re-open to // mBounds instead of its normal bounds. // mPipTaskOrganizer.scheduleFinishResizePip(getBounds()); } Bundle extra = new Bundle(); extra.putBoolean(FLING_BOUNDS_CHANGE, true); mPipTransitionState.setState(PipTransitionState.SCHEDULED_BOUNDS_CHANGE, extra); return; } mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded(); mSpringingToTouch = false; mDismissalPending = false; settlePipBoundsAfterPhysicsAnimation(true /* animatingAfter */); cleanUpHighPerfSessionMaybe(); } Loading @@ -662,7 +664,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, + " callers=\n%s", TAG, toBounds, Debug.getCallers(5, " ")); } if (!toBounds.equals(getBounds())) { // mPipTaskOrganizer.scheduleResizePip(toBounds, mUpdateBoundsCallback); mPipScheduler.scheduleAnimateResizePip(toBounds); } } Loading @@ -685,6 +687,60 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, // setAnimatingToBounds(toBounds); } private void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState, @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) { switch (newState) { case PipTransitionState.SCHEDULED_BOUNDS_CHANGE: if (!extra.getBoolean(FLING_BOUNDS_CHANGE)) break; // If touch is turned off and we are in a fling animation, schedule a transition. mWaitingForBoundsChangeTransition = true; mPipScheduler.scheduleAnimateResizePip( mPipBoundsState.getMotionBoundsState().getBoundsInMotion()); break; case PipTransitionState.CHANGING_PIP_BOUNDS: if (!mWaitingForBoundsChangeTransition) break; // If bounds change transition was scheduled from this class, handle leash updates. mWaitingForBoundsChangeTransition = false; SurfaceControl.Transaction startTx = extra.getParcelable( PipTransition.PIP_START_TX, SurfaceControl.Transaction.class); Rect destinationBounds = extra.getParcelable( PipTransition.PIP_DESTINATION_BOUNDS, Rect.class); startTx.setPosition(mPipTransitionState.mPinnedTaskLeash, destinationBounds.left, destinationBounds.top); startTx.apply(); // All motion operations have actually finished, so make bounds cache updates. settlePipBoundsAfterPhysicsAnimation(false /* animatingAfter */); cleanUpHighPerfSessionMaybe(); // Setting state to CHANGED_PIP_BOUNDS applies finishTx and notifies Core. mPipTransitionState.setState(PipTransitionState.CHANGED_PIP_BOUNDS); break; case PipTransitionState.EXITING_PIP: // We need to force finish any local animators if about to leave PiP, to avoid // breaking the state (e.g. leashes are cleaned up upon exit). if (!mPipBoundsState.getMotionBoundsState().isInMotion()) break; cancelPhysicsAnimation(); settlePipBoundsAfterPhysicsAnimation(false /* animatingAfter */); } } private void settlePipBoundsAfterPhysicsAnimation(boolean animatingAfter) { if (!animatingAfter) { // The physics animation ended, though we may not necessarily be done animating, such as // when we're still dragging after moving out of the magnetic target. Only set the final // bounds state and clear motion bounds completely if the whole animation is over. mPipBoundsState.setBounds(mPipBoundsState.getMotionBoundsState().getBoundsInMotion()); mPipBoundsState.getMotionBoundsState().onAllAnimationsEnded(); } mPipBoundsState.getMotionBoundsState().onPhysicsAnimationEnded(); mSpringingToTouch = false; mDismissalPending = false; } /** * Returns a MagnetizedObject wrapper for PIP's animated bounds. This is provided to the * magnetic dismiss target so it can calculate PIP's size and position. Loading
libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipScheduler.java +19 −0 Original line number Diff line number Diff line Loading @@ -25,16 +25,19 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.graphics.Rect; import android.view.SurfaceControl; import android.window.WindowContainerTransaction; import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.pip.PipBoundsState; import com.android.wm.shell.common.pip.PipUtils; import com.android.wm.shell.pip.PipTransitionController; import com.android.wm.shell.protolog.ShellProtoLogGroup; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; Loading Loading @@ -156,4 +159,20 @@ public class PipScheduler { wct.setBounds(mPipTransitionState.mPipTaskToken, toBounds); mPipTransitionController.startResizeTransition(wct); } /** * Directly perform a scaled matrix transformation on the leash. This will not perform any * {@link WindowContainerTransaction}. */ public void scheduleUserResizePip(Rect toBounds) { if (toBounds.isEmpty()) { ProtoLog.w(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "%s: Attempted to user resize PIP to empty bounds, aborting.", TAG); return; } SurfaceControl leash = mPipTransitionState.mPinnedTaskLeash; final SurfaceControl.Transaction tx = new SurfaceControl.Transaction(); tx.setPosition(leash, toBounds.left, toBounds.top); tx.apply(); } }
libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipTouchHandler.java +49 −12 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.wm.shell.pip2.phone; import static android.view.WindowManager.INPUT_CONSUMER_PIP; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASHING; import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.PIP_STASH_MINIMUM_VELOCITY_THRESHOLD; import static com.android.wm.shell.common.pip.PipBoundsState.STASH_TYPE_LEFT; Loading @@ -30,18 +32,19 @@ import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_PICTURE_ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.content.ComponentName; import android.content.Context; import android.content.res.Resources; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; import android.os.Bundle; import android.provider.DeviceConfig; import android.util.Size; import android.view.DisplayCutout; import android.view.InputEvent; import android.view.MotionEvent; import android.view.ViewConfiguration; import android.view.WindowManagerGlobal; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; Loading Loading @@ -80,6 +83,7 @@ public class PipTouchHandler { private final Context mContext; private final PipBoundsAlgorithm mPipBoundsAlgorithm; @NonNull private final PipBoundsState mPipBoundsState; @NonNull private final PipTransitionState mPipTransitionState; @NonNull private final SizeSpecSource mSizeSpecSource; private final PipUiEventLogger mPipUiEventLogger; private final PipDismissTargetHandler mPipDismissTargetHandler; Loading Loading @@ -125,6 +129,7 @@ public class PipTouchHandler { private final FloatingContentCoordinator mFloatingContentCoordinator; private PipMotionHelper mMotionHelper; private PipTouchGesture mGesture; private PipInputConsumer mPipInputConsumer; // Temp vars private final Rect mTmpBounds = new Rect(); Loading Loading @@ -167,6 +172,7 @@ public class PipTouchHandler { PhonePipMenuController menuController, PipBoundsAlgorithm pipBoundsAlgorithm, @NonNull PipBoundsState pipBoundsState, @NonNull PipTransitionState pipTransitionState, @NonNull SizeSpecSource sizeSpecSource, PipMotionHelper pipMotionHelper, FloatingContentCoordinator floatingContentCoordinator, Loading @@ -179,6 +185,9 @@ public class PipTouchHandler { mAccessibilityManager = context.getSystemService(AccessibilityManager.class); mPipBoundsAlgorithm = pipBoundsAlgorithm; mPipBoundsState = pipBoundsState; mPipTransitionState = pipTransitionState; mPipTransitionState.addPipTransitionStateChangedListener(this::onPipTransitionStateChanged); mSizeSpecSource = sizeSpecSource; mMenuController = menuController; mPipUiEventLogger = pipUiEventLogger; Loading Loading @@ -227,6 +236,11 @@ public class PipTouchHandler { mPipResizeGestureHandler.init(); mPipDismissTargetHandler.init(); mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(), INPUT_CONSUMER_PIP, mMainExecutor); mPipInputConsumer.setInputListener(this::handleTouchEvent); mPipInputConsumer.setRegistrationListener(this::onRegistrationChanged); mEnableStash = DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_SYSTEMUI, PIP_STASHING, Loading Loading @@ -294,19 +308,17 @@ public class PipTouchHandler { void onActivityPinned() { mPipDismissTargetHandler.createOrUpdateDismissTarget(); mPipResizeGestureHandler.onActivityPinned(); mFloatingContentCoordinator.onContentAdded(mMotionHelper); mPipInputConsumer.registerInputConsumer(); } void onActivityUnpinned(ComponentName topPipActivity) { if (topPipActivity == null) { void onActivityUnpinned() { // Clean up state after the last PiP activity is removed mPipDismissTargetHandler.cleanUpDismissTarget(); mFloatingContentCoordinator.onContentRemoved(mMotionHelper); } mPipResizeGestureHandler.onActivityUnpinned(); mPipInputConsumer.unregisterInputConsumer(); } void onPinnedStackAnimationEnded( Loading Loading @@ -512,6 +524,7 @@ public class PipTouchHandler { return true; } /* if ((ev.getAction() == MotionEvent.ACTION_DOWN || mTouchState.isUserInteracting()) && mPipDismissTargetHandler.maybeConsumeMotionEvent(ev)) { // If the first touch event occurs within the magnetic field, pass the ACTION_DOWN event Loading @@ -528,11 +541,13 @@ public class PipTouchHandler { return true; } if (!mTouchState.isUserInteracting()) { // Ignore the motion event When the entry animation is waiting to be started if (!mTouchState.isUserInteracting() && mPipTaskOrganizer.isEntryScheduled()) { ProtoLog.wtf(WM_SHELL_PICTURE_IN_PICTURE, "%s: Waiting to start the entry animation, skip the motion event.", TAG); return true; } */ // Update the touch state mTouchState.onTouchEvent(ev); Loading Loading @@ -808,7 +823,7 @@ public class PipTouchHandler { mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mPipBoundsState.getMovementBounds().bottom; mMotionHelper.setSpringingToTouch(false); // mPipDismissTargetHandler.setTaskLeash(mPipTaskOrganizer.getSurfaceControl()); mPipDismissTargetHandler.setTaskLeash(mPipTransitionState.mPinnedTaskLeash); // If the menu is still visible then just poke the menu // so that it will timeout after the user stops touching it Loading Loading @@ -880,7 +895,8 @@ public class PipTouchHandler { // Reset the touch state on up before the fling settles mTouchState.reset(); if (mEnableStash && shouldStash(vel, getPossiblyMotionBounds())) { mMotionHelper.stashToEdge(vel.x, vel.y, this::stashEndAction /* endAction */); // mMotionHelper.stashToEdge(vel.x, vel.y, // this::stashEndAction /* endAction */); } else { if (mPipBoundsState.isStashed()) { // Reset stashed state if previously stashed Loading Loading @@ -1059,6 +1075,27 @@ public class PipTouchHandler { mPipResizeGestureHandler.setOhmOffset(offset); } private void onPipTransitionStateChanged(@PipTransitionState.TransitionState int oldState, @PipTransitionState.TransitionState int newState, @Nullable Bundle extra) { switch (newState) { case PipTransitionState.ENTERED_PIP: onActivityPinned(); mTouchState.setAllowInputEvents(true); break; case PipTransitionState.EXITED_PIP: mTouchState.setAllowInputEvents(false); onActivityUnpinned(); break; case PipTransitionState.SCHEDULED_BOUNDS_CHANGE: mTouchState.setAllowInputEvents(false); break; case PipTransitionState.CHANGED_PIP_BOUNDS: mTouchState.setAllowInputEvents(true); break; } } /** * Dumps the {@link PipTouchHandler} state. */ Loading