diff options
27 files changed, 605 insertions, 678 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt index 5cd660a2caa5..7ea4689be7e2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt @@ -472,11 +472,6 @@ class PhysicsAnimator<T> private constructor (target: T) { * animator is under test. */ internal fun startInternal() { - if (!Looper.getMainLooper().isCurrentThread) { - Log.e(TAG, "Animations can only be started on the main thread. If you are seeing " + - "this message in a test, call PhysicsAnimatorTestUtils#prepareForTest in " + - "your test setup.") - } val target = weakTarget.get() if (target == null) { Log.w(TAG, "Trying to animate a GC-ed object.") diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java index d37e628b9a51..1149cceb1068 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java @@ -17,10 +17,15 @@ package com.android.wm.shell.common; import android.os.Looper; +import android.os.SystemClock; +import android.os.Trace; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; +import java.util.function.BooleanSupplier; +import java.util.function.Predicate; +import java.util.function.Supplier; /** * Super basic Executor interface that adds support for delayed execution and removing callbacks. @@ -65,17 +70,17 @@ public interface ShellExecutor extends Executor { /** * See {@link android.os.Handler#postDelayed(Runnable, long)}. */ - void executeDelayed(Runnable r, long delayMillis); + void executeDelayed(Runnable runnable, long delayMillis); /** * See {@link android.os.Handler#removeCallbacks}. */ - void removeCallbacks(Runnable r); + void removeCallbacks(Runnable runnable); /** * See {@link android.os.Handler#hasCallbacks(Runnable)}. */ - boolean hasCallback(Runnable r); + boolean hasCallback(Runnable runnable); /** * Returns the looper that this executor is running on. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java index 1f07542c9a27..d14c3e3c0dd4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java @@ -35,54 +35,17 @@ import java.util.function.Consumer; @ExternalThread public interface Pip { /** - * Closes PIP (PIPed activity and PIP system UI). - */ - default void closePip() { - } - - /** - * Dump the current state and information if need. - * - * @param pw The stream to dump information to. - */ - default void dump(PrintWriter pw) { - } - - /** * Expand PIP, it's possible that specific request to activate the window via Alt-tab. */ default void expandPip() { } /** - * Get the touch handler which manages all the touch handling for PIP on the Phone, - * including moving, dismissing and expanding the PIP. (Do not use in TV) - * - * @return - */ - default @Nullable PipTouchHandler getPipTouchHandler() { - return null; - } - - /** * Hides the PIP menu. */ default void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) {} /** - * Returns {@code true} if PIP is shown. - */ - default boolean isPipShown() { - return false; - } - - /** - * Moves the PIPed activity to the fullscreen and closes PIP system UI. - */ - default void movePipToFullscreen() { - } - - /** * Called when configuration is changed. */ default void onConfigurationChanged(Configuration newConfig) { @@ -101,12 +64,6 @@ public interface Pip { } /** - * Registers the session listener for the current user. - */ - default void registerSessionListenerForCurrentUser() { - } - - /** * Called when SysUI state changed. * * @param isSysUiStateValid Is SysUI state valid or not. @@ -116,19 +73,9 @@ public interface Pip { } /** - * Resize the Pip to the appropriate size for the input state. - * - * @param state In Pip state also used to determine the new size for the Pip. - */ - default void resizePinnedStack(int state) { - } - - /** - * Resumes resizing operation on the Pip that was previously suspended. - * - * @param reason The reason resizing operations on the Pip was suspended. + * Registers the session listener for the current user. */ - default void resumePipResizing(int reason) { + default void registerSessionListenerForCurrentUser() { } /** @@ -162,14 +109,6 @@ public interface Pip { default void showPictureInPictureMenu() {} /** - * Suspends resizing operation on the Pip until {@link #resumePipResizing} is called. - * - * @param reason The reason for suspending resizing operations on the Pip. - */ - default void suspendPipResizing(int reason) { - } - - /** * Called by Launcher when swiping an auto-pip enabled Activity to home starts * @param componentName {@link ComponentName} represents the Activity entering PiP * @param activityInfo {@link ActivityInfo} tied to the Activity @@ -199,4 +138,12 @@ public interface Pip { * PiP and the Back-from-Edge gesture. */ default void setPipExclusionBoundsChangeListener(Consumer<Rect> listener) { } + + /** + * Dump the current state and information if need. + * + * @param pw The stream to dump information to. + */ + default void dump(PrintWriter pw) { + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java index d96d4d0a6a3c..1a4616c5f591 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java @@ -31,6 +31,7 @@ import android.media.MediaMetadata; import android.media.session.MediaController; import android.media.session.MediaSessionManager; import android.media.session.PlaybackState; +import android.os.Handler; import android.os.UserHandle; import androidx.annotation.Nullable; @@ -74,6 +75,7 @@ public class PipMediaController { } private final Context mContext; + private final Handler mMainHandler; private final MediaSessionManager mMediaSessionManager; private MediaController mMediaController; @@ -118,15 +120,16 @@ public class PipMediaController { private final ArrayList<ActionListener> mActionListeners = new ArrayList<>(); private final ArrayList<MetadataListener> mMetadataListeners = new ArrayList<>(); - public PipMediaController(Context context) { + public PipMediaController(Context context, Handler mainHandler) { mContext = context; + mMainHandler = mainHandler; IntentFilter mediaControlFilter = new IntentFilter(); mediaControlFilter.addAction(ACTION_PLAY); mediaControlFilter.addAction(ACTION_PAUSE); mediaControlFilter.addAction(ACTION_NEXT); mediaControlFilter.addAction(ACTION_PREV); - mContext.registerReceiver(mPlayPauseActionReceiver, mediaControlFilter, - UserHandle.USER_ALL); + mContext.registerReceiverForAllUsers(mPlayPauseActionReceiver, mediaControlFilter, + null /* permission */, mainHandler); createMediaActions(); mMediaSessionManager = context.getSystemService(MediaSessionManager.class); @@ -245,7 +248,7 @@ public class PipMediaController { public void registerSessionListenerForCurrentUser() { mMediaSessionManager.removeOnActiveSessionsChangedListener(mSessionsChangedListener); mMediaSessionManager.addOnActiveSessionsChangedListener(mSessionsChangedListener, null, - UserHandle.CURRENT, null); + UserHandle.CURRENT, mMainHandler); } /** @@ -277,7 +280,7 @@ public class PipMediaController { } mMediaController = controller; if (controller != null) { - controller.registerCallback(mPlaybackChangedListener); + controller.registerCallback(mPlaybackChangedListener, mMainHandler); } notifyActionsChanged(); notifyMetadataChanged(getMediaMetadata()); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java index 1279cd36724b..b80f285c3eb4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java @@ -50,9 +50,7 @@ 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.Rational; @@ -64,14 +62,14 @@ import android.window.WindowContainerTransaction; import android.window.WindowContainerTransactionCallback; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.os.SomeArgs; +import com.android.internal.jank.InteractionJankMonitor; import com.android.wm.shell.R; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.ShellExecutor; +import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.pip.phone.PipMotionHelper; -import com.android.wm.shell.pip.phone.PipUpdateThread; - import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; @@ -98,12 +96,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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_ANIMATE = 2; - private static final int MSG_OFFSET_ANIMATE = 3; - private static final int MSG_FINISH_RESIZE = 4; - private static final int MSG_RESIZE_USER = 5; - // Not a complete set of states but serves what we want right now. private enum State { UNDEFINED(0), @@ -135,8 +127,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } } - private final Handler mMainHandler; - private final Handler mUpdateHandler; private final PipBoundsState mPipBoundsState; private final PipBoundsAlgorithm mPipBoundsAlgorithm; private final @NonNull PipMenuController mPipMenuController; @@ -148,6 +138,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private final Map<IBinder, Configuration> mInitialState = new HashMap<>(); private final Optional<LegacySplitScreen> mSplitScreenOptional; protected final ShellTaskOrganizer mTaskOrganizer; + protected final ShellExecutor mMainExecutor; // These callbacks are called on the update thread private final PipAnimationController.PipAnimationCallback mPipAnimationCallback = @@ -183,68 +174,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } }; - @SuppressWarnings("unchecked") - private final Handler.Callback mUpdateCallbacks = (msg) -> { - SomeArgs args = (SomeArgs) msg.obj; - Consumer<Rect> updateBoundsCallback = (Consumer<Rect>) args.arg1; - switch (msg.what) { - case MSG_RESIZE_IMMEDIATE: { - Rect toBounds = (Rect) args.arg2; - resizePip(toBounds); - if (updateBoundsCallback != null) { - updateBoundsCallback.accept(toBounds); - } - break; - } - case MSG_RESIZE_ANIMATE: { - Rect currentBounds = (Rect) args.arg2; - Rect toBounds = (Rect) args.arg3; - Rect sourceHintRect = (Rect) args.arg4; - float startingAngle = (float) args.arg5; - int duration = args.argi2; - animateResizePip(currentBounds, toBounds, sourceHintRect, - args.argi1 /* direction */, duration, startingAngle); - if (updateBoundsCallback != null) { - updateBoundsCallback.accept(toBounds); - } - break; - } - case MSG_OFFSET_ANIMATE: { - Rect originalBounds = (Rect) args.arg2; - final int offset = args.argi1; - final int duration = args.argi2; - offsetPip(originalBounds, 0 /* xOffset */, offset, duration); - Rect toBounds = new Rect(originalBounds); - toBounds.offset(0, offset); - if (updateBoundsCallback != null) { - updateBoundsCallback.accept(toBounds); - } - break; - } - case MSG_FINISH_RESIZE: { - SurfaceControl.Transaction tx = (SurfaceControl.Transaction) args.arg2; - Rect toBounds = (Rect) args.arg3; - finishResize(tx, toBounds, args.argi1 /* direction */, -1); - if (updateBoundsCallback != null) { - updateBoundsCallback.accept(toBounds); - } - break; - } - case MSG_RESIZE_USER: { - Rect startBounds = (Rect) args.arg2; - Rect toBounds = (Rect) args.arg3; - float degrees = (float) args.arg4; - userResizePip(startBounds, toBounds, degrees); - if (updateBoundsCallback != null) { - updateBoundsCallback.accept(toBounds); - } - break; - } - } - args.recycle(); - return true; - }; - private ActivityManager.RunningTaskInfo mTaskInfo; private WindowContainerToken mToken; private SurfaceControl mLeash; @@ -276,9 +205,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, Optional<LegacySplitScreen> splitScreenOptional, @NonNull DisplayController displayController, @NonNull PipUiEventLogger pipUiEventLogger, - @NonNull ShellTaskOrganizer shellTaskOrganizer) { - mMainHandler = new Handler(Looper.getMainLooper()); - mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks); + @NonNull ShellTaskOrganizer shellTaskOrganizer, + @ShellMainThread ShellExecutor mainExecutor) { mPipBoundsState = pipBoundsState; mPipBoundsAlgorithm = boundsHandler; mPipMenuController = pipMenuController; @@ -290,12 +218,13 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new; mSplitScreenOptional = splitScreenOptional; mTaskOrganizer = shellTaskOrganizer; - mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_PIP); - displayController.addDisplayWindowListener(this); - } + mMainExecutor = mainExecutor; - public Handler getUpdateHandler() { - return mUpdateHandler; + // TODO: Can be removed once wm components are created on the shell-main thread + mMainExecutor.execute(() -> { + mTaskOrganizer.addListenerForType(this, TASK_LISTENER_TYPE_PIP); + }); + displayController.addDisplayWindowListener(this); } public Rect getCurrentOrAnimatingBounds() { @@ -428,15 +357,17 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() { @Override public void onTransactionReady(int id, SurfaceControl.Transaction t) { - t.apply(); - // Make sure to grab the latest source hint rect as it could have been updated - // right after applying the windowing mode change. - final Rect sourceHintRect = getValidSourceHintRect(mPictureInPictureParams, - destinationBounds); - scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds, - 0 /* startingAngle */, sourceHintRect, direction, animationDurationMs, - null /* updateBoundsCallback */); - mState = State.EXITING_PIP; + mMainExecutor.execute(() -> { + t.apply(); + // Make sure to grab the latest source hint rect as it could have been + // updated right after applying the windowing mode change. + final Rect sourceHintRect = getValidSourceHintRect(mPictureInPictureParams, + destinationBounds); + scheduleAnimateResizePip(mPipBoundsState.getBounds(), destinationBounds, + 0 /* startingAngle */, sourceHintRect, direction, + animationDurationMs, null /* updateBoundsCallback */); + mState = State.EXITING_PIP; + }); } }); } @@ -465,12 +396,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } // removePipImmediately is expected when the following animation finishes. - mUpdateHandler.post(() -> mPipAnimationController + mPipAnimationController .getAnimator(mLeash, mPipBoundsState.getBounds(), 1f, 0f) .setTransitionDirection(TRANSITION_DIRECTION_REMOVE_STACK) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(mEnterExitAnimationDuration) - .start()); + .start(); mInitialState.remove(mToken.asBinder()); mState = State.EXITING_PIP; } @@ -579,12 +510,12 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, tx.setAlpha(mLeash, 0f); tx.apply(); applyEnterPipSyncTransaction(destinationBounds, () -> { - mUpdateHandler.post(() -> mPipAnimationController + mPipAnimationController .getAnimator(mLeash, destinationBounds, 0f, 1f) .setTransitionDirection(TRANSITION_DIRECTION_TO_PIP) .setPipAnimationCallback(mPipAnimationCallback) .setDuration(durationMs) - .start()); + .start(); // mState is set right after the animation is kicked off to block any resize // requests such as offsetPip that may have been called prior to the transition. mState = State.ENTERING_PIP; @@ -599,13 +530,16 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, wct.setActivityWindowingMode(mToken, WINDOWING_MODE_UNDEFINED); wct.setBounds(mToken, destinationBounds); wct.scheduleFinishEnterPip(mToken, destinationBounds); + // TODO: Migrate to SyncTransactionQueue mTaskOrganizer.applySyncTransaction(wct, new WindowContainerTransactionCallback() { @Override public void onTransactionReady(int id, SurfaceControl.Transaction t) { - t.apply(); - if (runnable != null) { - runnable.run(); - } + mMainExecutor.execute(() -> { + t.apply(); + if (runnable != null) { + runnable.run(); + } + }); } }); } @@ -621,12 +555,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mState = State.ENTERING_PIP; } final Rect pipBounds = mPipBoundsState.getBounds(); - runOnMainHandler(() -> { - for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { - final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); - callback.onPipTransitionStarted(componentName, direction, pipBounds); - } - }); + for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { + final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); + callback.onPipTransitionStarted(componentName, direction, pipBounds); + } } private void sendOnPipTransitionFinished( @@ -634,29 +566,17 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, if (direction == TRANSITION_DIRECTION_TO_PIP) { mState = State.ENTERED_PIP; } - runOnMainHandler(() -> { - for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { - final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); - callback.onPipTransitionFinished(mTaskInfo.baseActivity, direction); - } - }); + for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { + final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); + callback.onPipTransitionFinished(mTaskInfo.baseActivity, direction); + } } private void sendOnPipTransitionCancelled( @PipAnimationController.TransitionDirection int direction) { - runOnMainHandler(() -> { - for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { - final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); - callback.onPipTransitionCanceled(mTaskInfo.baseActivity, direction); - } - }); - } - - private void runOnMainHandler(Runnable r) { - if (Looper.getMainLooper() == Looper.myLooper()) { - r.run(); - } else { - mMainHandler.post(r); + for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) { + final PipTransitionCallback callback = mPipTransitionCallbacks.get(i); + callback.onPipTransitionCanceled(mTaskInfo.baseActivity, direction); } } @@ -872,15 +792,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return; } - SomeArgs args = SomeArgs.obtain(); - args.arg1 = updateBoundsCallback; - args.arg2 = currentBounds; - args.arg3 = destinationBounds; - args.arg4 = sourceHintRect; - args.arg5 = startingAngle; - args.argi1 = direction; - args.argi2 = durationMs; - mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_ANIMATE, args)); + animateResizePip(currentBounds, destinationBounds, sourceHintRect, direction, durationMs, + startingAngle); + if (updateBoundsCallback != null) { + updateBoundsCallback.accept(destinationBounds); + } } /** @@ -888,10 +804,24 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, * {@link WindowContainerTransaction} until {@link #scheduleFinishResizePip} is called. */ public void scheduleResizePip(Rect toBounds, Consumer<Rect> updateBoundsCallback) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = updateBoundsCallback; - args.arg2 = toBounds; - mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_IMMEDIATE, args)); + // Could happen when exitPip + if (mToken == null || mLeash == null) { + Log.w(TAG, "Abort animation, invalid leash"); + return; + } + mPipBoundsState.setBounds(toBounds); + final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); + mSurfaceTransactionHelper + .crop(tx, mLeash, toBounds) + .round(tx, mLeash, mState.isInPip()); + if (mPipMenuController.isMenuVisible()) { + mPipMenuController.resizePipMenu(mLeash, tx, toBounds); + } else { + tx.apply(); + } + if (updateBoundsCallback != null) { + updateBoundsCallback.accept(toBounds); + } } /** @@ -909,12 +839,27 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, */ public void scheduleUserResizePip(Rect startBounds, Rect toBounds, float degrees, Consumer<Rect> updateBoundsCallback) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = updateBoundsCallback; - args.arg2 = startBounds; - args.arg3 = toBounds; - args.arg4 = degrees; - mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_USER, args)); + // Could happen when exitPip + if (mToken == null || mLeash == null) { + Log.w(TAG, "Abort animation, invalid leash"); + return; + } + + if (startBounds.isEmpty() || toBounds.isEmpty()) { + Log.w(TAG, "Attempted to user resize PIP to or from empty bounds, aborting."); + return; + } + + final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); + mSurfaceTransactionHelper.scale(tx, mLeash, startBounds, toBounds, degrees); + if (mPipMenuController.isMenuVisible()) { + mPipMenuController.movePipMenu(mLeash, tx, toBounds); + } else { + tx.apply(); + } + if (updateBoundsCallback != null) { + updateBoundsCallback.accept(toBounds); + } } /** @@ -948,13 +893,11 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return; } - SomeArgs args = SomeArgs.obtain(); - args.arg1 = updateBoundsCallback; - args.arg2 = createFinishResizeSurfaceTransaction( - destinationBounds); - args.arg3 = destinationBounds; - args.argi1 = direction; - mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_FINISH_RESIZE, args)); + finishResize(createFinishResizeSurfaceTransaction(destinationBounds), destinationBounds, + direction, -1); + if (updateBoundsCallback != null) { + updateBoundsCallback.accept(destinationBounds); + } } private SurfaceControl.Transaction createFinishResizeSurfaceTransaction( @@ -979,20 +922,15 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, Log.d(TAG, "skip scheduleOffsetPip, entering pip deferred"); return; } - SomeArgs args = SomeArgs.obtain(); - args.arg1 = updateBoundsCallback; - args.arg2 = originalBounds; - // offset would be zero if triggered from screen rotation. - args.argi1 = offset; - args.argi2 = duration; - mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_OFFSET_ANIMATE, args)); + offsetPip(originalBounds, 0 /* xOffset */, offset, duration); + Rect toBounds = new Rect(originalBounds); + toBounds.offset(0, offset); + if (updateBoundsCallback != null) { + updateBoundsCallback.accept(toBounds); + } } private void offsetPip(Rect originalBounds, int xOffset, int yOffset, int durationMs) { - if (Looper.myLooper() != mUpdateHandler.getLooper()) { - throw new RuntimeException("Callers should call scheduleOffsetPip() instead of this " - + "directly"); - } if (mTaskInfo == null) { Log.w(TAG, "mTaskInfo is not set"); return; @@ -1003,62 +941,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, TRANSITION_DIRECTION_SAME, durationMs, 0); } - private void resizePip(Rect destinationBounds) { - if (Looper.myLooper() != mUpdateHandler.getLooper()) { - throw new RuntimeException("Callers should call scheduleResizePip() instead of this " - + "directly"); - } - // Could happen when exitPip - if (mToken == null || mLeash == null) { - Log.w(TAG, "Abort animation, invalid leash"); - return; - } - mPipBoundsState.setBounds(destinationBounds); - final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); - mSurfaceTransactionHelper - .crop(tx, mLeash, destinationBounds) - .round(tx, mLeash, mState.isInPip()); - if (mPipMenuController.isMenuVisible()) { - runOnMainHandler(() -> - mPipMenuController.resizePipMenu(mLeash, tx, destinationBounds)); - } else { - tx.apply(); - } - } - - private void userResizePip(Rect startBounds, Rect destinationBounds, float degrees) { - if (Looper.myLooper() != mUpdateHandler.getLooper()) { - throw new RuntimeException("Callers should call scheduleUserResizePip() instead of " - + "this directly"); - } - // Could happen when exitPip - if (mToken == null || mLeash == null) { - Log.w(TAG, "Abort animation, invalid leash"); - return; - } - - if (startBounds.isEmpty() || destinationBounds.isEmpty()) { - Log.w(TAG, "Attempted to user resize PIP to or from empty bounds, aborting."); - return; - } - - final SurfaceControl.Transaction tx = mSurfaceControlTransactionFactory.getTransaction(); - mSurfaceTransactionHelper.scale(tx, mLeash, startBounds, destinationBounds, degrees); - if (mPipMenuController.isMenuVisible()) { - runOnMainHandler(() -> - mPipMenuController.movePipMenu(mLeash, tx, destinationBounds)); - } else { - tx.apply(); - } - } - private void finishResize(SurfaceControl.Transaction tx, Rect destinationBounds, @PipAnimationController.TransitionDirection int direction, @PipAnimationController.AnimationType int type) { - if (Looper.myLooper() != mUpdateHandler.getLooper()) { - throw new RuntimeException("Callers should call scheduleResizePip() instead of this " - + "directly"); - } mPipBoundsState.setBounds(destinationBounds); if (direction == TRANSITION_DIRECTION_REMOVE_STACK) { removePipImmediately(); @@ -1097,7 +982,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, mSurfaceTransactionHelper.scale(t, snapshotSurface, snapshotSrc, snapshotDest); t.apply(); - mUpdateHandler.post(() -> { + mMainExecutor.execute(() -> { // Start animation to fade out the snapshot. final ValueAnimator animator = ValueAnimator.ofFloat(1.0f, 0.0f); animator.setDuration(mEnterExitAnimationDuration); @@ -1129,10 +1014,8 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, } private void finishResizeForMenu(Rect destinationBounds) { - runOnMainHandler(() -> { - mPipMenuController.movePipMenu(null, null, destinationBounds); - mPipMenuController.updateMenuBounds(destinationBounds); - }); + mPipMenuController.movePipMenu(null, null, destinationBounds); + mPipMenuController.updateMenuBounds(destinationBounds); } private void prepareFinishResizeTransaction(Rect destinationBounds, @@ -1185,10 +1068,6 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, private void animateResizePip(Rect currentBounds, Rect destinationBounds, Rect sourceHintRect, @PipAnimationController.TransitionDirection int direction, int durationMs, float startingAngle) { - if (Looper.myLooper() != mUpdateHandler.getLooper()) { - throw new RuntimeException("Callers should call scheduleAnimateResizePip() instead of " - + "this directly"); - } // Could happen when exitPip if (mToken == null || mLeash == null) { Log.w(TAG, "Abort animation, invalid leash"); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java index 5db8f3d7ef40..8bf1b468cb7d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java @@ -30,6 +30,7 @@ import android.graphics.Matrix; import android.graphics.Rect; import android.graphics.RectF; import android.os.Debug; +import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; @@ -39,6 +40,7 @@ import android.view.SyncRtSurfaceTransactionApplier; import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams; import android.view.WindowManagerGlobal; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.pip.PipMediaController; import com.android.wm.shell.pip.PipMediaController.ActionListener; @@ -97,6 +99,8 @@ public class PhonePipMenuController implements PipMenuController { private final RectF mTmpDestinationRectF = new RectF(); private final Context mContext; private final PipMediaController mMediaController; + private final ShellExecutor mMainExecutor; + private final Handler mMainHandler; private final ArrayList<Listener> mListeners = new ArrayList<>(); private final SystemWindows mSystemWindows; @@ -116,11 +120,14 @@ public class PhonePipMenuController implements PipMenuController { } }; - public PhonePipMenuController(Context context, - PipMediaController mediaController, SystemWindows systemWindows) { + public PhonePipMenuController(Context context, PipMediaController mediaController, + SystemWindows systemWindows, ShellExecutor mainExecutor, + Handler mainHandler) { mContext = context; mMediaController = mediaController; mSystemWindows = systemWindows; + mMainExecutor = mainExecutor; + mMainHandler = mainHandler; } public boolean isMenuVisible() { @@ -156,7 +163,7 @@ public class PhonePipMenuController implements PipMenuController { if (mPipMenuView != null) { detachPipMenuView(); } - mPipMenuView = new PipMenuView(mContext, this); + mPipMenuView = new PipMenuView(mContext, this, mMainExecutor, mMainHandler); mSystemWindows.addView(mPipMenuView, getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */), 0, SHELL_ROOT_LAYER_PIP); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAppOpsListener.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAppOpsListener.java index 2cd010796799..d97d2d6ebb4f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAppOpsListener.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAppOpsListener.java @@ -21,22 +21,20 @@ import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE; import android.app.AppOpsManager; import android.app.AppOpsManager.OnOpChangedListener; -import android.app.IActivityManager; import android.content.ComponentName; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager.NameNotFoundException; -import android.os.Handler; import android.util.Pair; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.PipUtils; public class PipAppOpsListener { private static final String TAG = PipAppOpsListener.class.getSimpleName(); private Context mContext; - private Handler mHandler; - private IActivityManager mActivityManager; + private ShellExecutor mMainExecutor; private AppOpsManager mAppOpsManager; private Callback mCallback; @@ -53,7 +51,7 @@ public class PipAppOpsListener { if (appInfo.packageName.equals(topPipActivityInfo.first.getPackageName()) && mAppOpsManager.checkOpNoThrow(OP_PICTURE_IN_PICTURE, appInfo.uid, packageName) != MODE_ALLOWED) { - mHandler.post(() -> mCallback.dismissPip()); + mMainExecutor.execute(() -> mCallback.dismissPip()); } } } catch (NameNotFoundException e) { @@ -63,11 +61,9 @@ public class PipAppOpsListener { } }; - public PipAppOpsListener(Context context, IActivityManager activityManager, - Callback callback) { + public PipAppOpsListener(Context context, Callback callback, ShellExecutor mainExecutor) { mContext = context; - mHandler = new Handler(mContext.getMainLooper()); - mActivityManager = activityManager; + mMainExecutor = mainExecutor; mAppOpsManager = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE); mCallback = callback; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java index fa880848a9c2..cefeb93998f9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java @@ -71,11 +71,11 @@ import java.util.function.Consumer; /** * Manages the picture-in-picture (PIP) UI and states for Phones. */ -public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallback { +public class PipController implements PipTaskOrganizer.PipTransitionCallback { private static final String TAG = "PipController"; private Context mContext; - private ShellExecutor mMainExecutor; + protected ShellExecutor mMainExecutor; private DisplayController mDisplayController; private PipInputConsumer mPipInputConsumer; private WindowManagerShellWrapper mWindowManagerShellWrapper; @@ -84,6 +84,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac private PipBoundsAlgorithm mPipBoundsAlgorithm; private PipBoundsState mPipBoundsState; private PipTouchHandler mTouchHandler; + protected final PipImpl mImpl = new PipImpl(); private final DisplayInfo mTmpDisplayInfo = new DisplayInfo(); private final Rect mTmpInsetBounds = new Rect(); @@ -204,6 +205,28 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } } + + /** + * Instantiates {@link PipController}, returns {@code null} if the feature not supported. + */ + @Nullable + public static Pip create(Context context, DisplayController displayController, + PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, + PipBoundsState pipBoundsState, PipMediaController pipMediaController, + PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, + PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper, + TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor) { + if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { + Slog.w(TAG, "Device doesn't support Pip feature"); + return null; + } + + return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm, + pipBoundsState, pipMediaController, phonePipMenuController, pipTaskOrganizer, + pipTouchHandler, windowManagerShellWrapper, taskStackListener, mainExecutor) + .mImpl; + } + protected PipController(Context context, DisplayController displayController, PipAppOpsListener pipAppOpsListener, @@ -235,7 +258,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mTouchHandler = pipTouchHandler; mAppOpsListener = pipAppOpsListener; mPipInputConsumer = new PipInputConsumer(WindowManagerGlobal.getWindowManagerService(), - INPUT_CONSUMER_PIP); + INPUT_CONSUMER_PIP, mainExecutor); mPipTaskOrganizer.registerPipTransitionCallback(this); mPipTaskOrganizer.registerOnDisplayIdChangeCallback((int displayId) -> { final DisplayInfo newDisplayInfo = new DisplayInfo(); @@ -288,7 +311,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac if (taskInfo != null) { // If SystemUI restart, and it already existed a pinned stack, // register the pip input consumer to ensure touch can send to it. - mPipInputConsumer.registerInputConsumer(true /* withSfVsync */); + mPipInputConsumer.registerInputConsumer(); } } catch (RemoteException | UnsupportedOperationException e) { Log.e(TAG, "Failed to register pinned stack listener", e); @@ -301,12 +324,10 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac @Override public void onActivityPinned(String packageName, int userId, int taskId, int stackId) { - mMainExecutor.execute(() -> { - mTouchHandler.onActivityPinned(); - mMediaController.onActivityPinned(); - mAppOpsListener.onActivityPinned(packageName); - }); - mPipInputConsumer.registerInputConsumer(true /* withSfVsync */); + mTouchHandler.onActivityPinned(); + mMediaController.onActivityPinned(); + mAppOpsListener.onActivityPinned(packageName); + mPipInputConsumer.registerInputConsumer(); } @Override @@ -314,10 +335,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac final Pair<ComponentName, Integer> topPipActivityInfo = PipUtils.getTopPipActivity(mContext); final ComponentName topActivity = topPipActivityInfo.first; - mMainExecutor.execute(() -> { - mTouchHandler.onActivityUnpinned(topActivity); - mAppOpsListener.onActivityUnpinned(); - }); + mTouchHandler.onActivityUnpinned(topActivity); + mAppOpsListener.onActivityUnpinned(); mPipInputConsumer.unregisterInputConsumer(); } @@ -333,60 +352,46 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac }); } - @Override - public void onConfigurationChanged(Configuration newConfig) { - mMainExecutor.execute(() -> { - mPipBoundsAlgorithm.onConfigurationChanged(mContext); - mTouchHandler.onConfigurationChanged(); - mPipBoundsState.onConfigurationChanged(); - }); + private void onConfigurationChanged(Configuration newConfig) { + mPipBoundsAlgorithm.onConfigurationChanged(mContext); + mTouchHandler.onConfigurationChanged(); + mPipBoundsState.onConfigurationChanged(); } - @Override - public void onDensityOrFontScaleChanged() { - mMainExecutor.execute(() -> { - mPipTaskOrganizer.onDensityOrFontScaleChanged(mContext); - }); + private void onDensityOrFontScaleChanged() { + mPipTaskOrganizer.onDensityOrFontScaleChanged(mContext); } - @Override - public void onOverlayChanged() { - mMainExecutor.execute(() -> { - mPipBoundsState.setDisplayLayout(new DisplayLayout(mContext, mContext.getDisplay())); - updateMovementBounds(null /* toBounds */, - false /* fromRotation */, false /* fromImeAdjustment */, - false /* fromShelfAdjustment */, - null /* windowContainerTransaction */); - }); + private void onOverlayChanged() { + mPipBoundsState.setDisplayLayout(new DisplayLayout(mContext, mContext.getDisplay())); + updateMovementBounds(null /* toBounds */, + false /* fromRotation */, false /* fromImeAdjustment */, + false /* fromShelfAdjustment */, + null /* windowContainerTransaction */); } - @Override - public void registerSessionListenerForCurrentUser() { + private void registerSessionListenerForCurrentUser() { mMediaController.registerSessionListenerForCurrentUser(); } - @Override - public void onSystemUiStateChanged(boolean isValidState, int flag) { + private void onSystemUiStateChanged(boolean isValidState, int flag) { mTouchHandler.onSystemUiStateChanged(isValidState); } /** * Expands the PIP. */ - @Override public void expandPip() { mTouchHandler.getMotionHelper().expandLeavePip(false /* skipAnimation */); } - @Override - public PipTouchHandler getPipTouchHandler() { + private PipTouchHandler getPipTouchHandler() { return mTouchHandler; } /** * Hides the PIP menu. */ - @Override public void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) { mMenuController.hideMenu(onStartCallback, onEndCallback); } @@ -408,9 +413,8 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac /** * Sets both shelf visibility and its height. */ - @Override - public void setShelfHeight(boolean visible, int height) { - mMainExecutor.execute(() -> setShelfHeightLocked(visible, height)); + private void setShelfHeight(boolean visible, int height) { + setShelfHeightLocked(visible, height); } private void setShelfHeightLocked(boolean visible, int height) { @@ -418,18 +422,15 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mPipBoundsState.setShelfVisibility(visible, shelfHeight); } - @Override - public void setPinnedStackAnimationType(int animationType) { - mMainExecutor.execute(() -> mPipTaskOrganizer.setOneShotAnimationType(animationType)); + private void setPinnedStackAnimationType(int animationType) { + mPipTaskOrganizer.setOneShotAnimationType(animationType); } - @Override - public void setPinnedStackAnimationListener(Consumer<Boolean> callback) { - mMainExecutor.execute(() -> mPinnedStackAnimationRecentsCallback = callback); + private void setPinnedStackAnimationListener(Consumer<Boolean> callback) { + mPinnedStackAnimationRecentsCallback = callback; } - @Override - public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, + private Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, PictureInPictureParams pictureInPictureParams, int launcherRotation, int shelfHeight) { setShelfHeightLocked(shelfHeight > 0 /* visible */, shelfHeight); @@ -438,11 +439,19 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac pictureInPictureParams); } - @Override - public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { + private void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { mPipTaskOrganizer.stopSwipePipToHome(componentName, destinationBounds); } + /** + * Set a listener to watch out for PiP bounds. This is mostly used by SystemUI's + * Back-gesture handler, to avoid conflicting with PiP when it's stashed. + */ + private void setPipExclusionBoundsChangeListener( + Consumer<Rect> pipExclusionBoundsChangeListener) { + mTouchHandler.setPipExclusionBoundsChangeListener(pipExclusionBoundsChangeListener); + } + @Override public void onPipTransitionStarted(ComponentName activity, int direction, Rect pipBounds) { if (isOutPipDirection(direction)) { @@ -468,16 +477,6 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } } - /** - * Set a listener to watch out for PiP bounds. This is mostly used by SystemUI's - * Back-gesture handler, to avoid conflicting with PiP when it's stashed. - */ - @Override - public void setPipExclusionBoundsChangeListener( - Consumer<Rect> pipExclusionBoundsChangeListener) { - mTouchHandler.setPipExclusionBoundsChangeListener(pipExclusionBoundsChangeListener); - } - @Override public void onPipTransitionFinished(ComponentName activity, int direction) { onPipTransitionFinishedOrCanceled(direction); @@ -607,8 +606,7 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac } } - @Override - public void dump(PrintWriter pw) { + private void dump(PrintWriter pw) { final String innerPrefix = " "; pw.println(TAG); mMenuController.dump(pw, innerPrefix); @@ -619,23 +617,123 @@ public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallbac mPipInputConsumer.dump(pw, innerPrefix); } - /** - * Instantiates {@link PipController}, returns {@code null} if the feature not supported. - */ - @Nullable - public static PipController create(Context context, DisplayController displayController, - PipAppOpsListener pipAppOpsListener, PipBoundsAlgorithm pipBoundsAlgorithm, - PipBoundsState pipBoundsState, PipMediaController pipMediaController, - PhonePipMenuController phonePipMenuController, PipTaskOrganizer pipTaskOrganizer, - PipTouchHandler pipTouchHandler, WindowManagerShellWrapper windowManagerShellWrapper, - TaskStackListenerImpl taskStackListener, ShellExecutor mainExecutor) { - if (!context.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE)) { - Slog.w(TAG, "Device doesn't support Pip feature"); - return null; + private class PipImpl implements Pip { + @Override + public void hidePipMenu(Runnable onStartCallback, Runnable onEndCallback) { + mMainExecutor.execute(() -> { + PipController.this.hidePipMenu(onStartCallback, onEndCallback); + }); } - return new PipController(context, displayController, pipAppOpsListener, pipBoundsAlgorithm, - pipBoundsState, pipMediaController, phonePipMenuController, pipTaskOrganizer, - pipTouchHandler, windowManagerShellWrapper, taskStackListener, mainExecutor); + @Override + public void expandPip() { + mMainExecutor.execute(() -> { + PipController.this.expandPip(); + }); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + mMainExecutor.execute(() -> { + PipController.this.onConfigurationChanged(newConfig); + }); + } + + @Override + public void onDensityOrFontScaleChanged() { + mMainExecutor.execute(() -> { + PipController.this.onDensityOrFontScaleChanged(); + }); + } + + @Override + public void onOverlayChanged() { + mMainExecutor.execute(() -> { + PipController.this.onOverlayChanged(); + }); + } + + @Override + public void onSystemUiStateChanged(boolean isSysUiStateValid, int flag) { + mMainExecutor.execute(() -> { + PipController.this.onSystemUiStateChanged(isSysUiStateValid, flag); + }); + } + + @Override + public void registerSessionListenerForCurrentUser() { + mMainExecutor.execute(() -> { + PipController.this.registerSessionListenerForCurrentUser(); + }); + } + + @Override + public void setShelfHeight(boolean visible, int height) { + mMainExecutor.execute(() -> { + PipController.this.setShelfHeight(visible, height); + }); + } + + @Override + public void setPinnedStackAnimationListener(Consumer<Boolean> callback) { + mMainExecutor.execute(() -> { + PipController.this.setPinnedStackAnimationListener(callback); + }); + } + + @Override + public void setPinnedStackAnimationType(int animationType) { + mMainExecutor.execute(() -> { + PipController.this.setPinnedStackAnimationType(animationType); + }); + } + + @Override + public void setPipExclusionBoundsChangeListener(Consumer<Rect> listener) { + mMainExecutor.execute(() -> { + PipController.this.setPipExclusionBoundsChangeListener(listener); + }); + } + + @Override + public void showPictureInPictureMenu() { + mMainExecutor.execute(() -> { + PipController.this.showPictureInPictureMenu(); + }); + } + + @Override + public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo, + PictureInPictureParams pictureInPictureParams, int launcherRotation, + int shelfHeight) { + Rect[] result = new Rect[1]; + try { + mMainExecutor.executeBlocking(() -> { + result[0] = PipController.this.startSwipePipToHome(componentName, activityInfo, + pictureInPictureParams, launcherRotation, shelfHeight); + }); + } catch (InterruptedException e) { + Slog.e(TAG, "Failed to start swipe pip to home"); + } + return result[0]; + } + + @Override + public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) { + mMainExecutor.execute(() -> { + PipController.this.stopSwipePipToHome(componentName, destinationBounds); + }); + } + + @Override + public void dump(PrintWriter pw) { + try { + mMainExecutor.executeBlocking(() -> { + PipController.this.dump(pw); + }); + } catch (InterruptedException e) { + Slog.e(TAG, "Failed to dump PipController in 2s"); + } + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java index bebe5f965251..d9a7bdb2eca6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java @@ -37,9 +37,12 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.wm.shell.R; import com.android.wm.shell.animation.PhysicsAnimator; import com.android.wm.shell.common.DismissCircleView; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.magnetictarget.MagnetizedObject; import com.android.wm.shell.pip.PipUiEventLogger; +import java.util.concurrent.TimeUnit; + import kotlin.Unit; /** @@ -57,36 +60,32 @@ public class PipDismissTargetHandler { * MagnetizedObject wrapper for PIP. This allows the magnetic target library to locate and move * PIP. */ - private final MagnetizedObject<Rect> mMagnetizedPip; + private MagnetizedObject<Rect> mMagnetizedPip; /** * Container for the dismiss circle, so that it can be animated within the container via * translation rather than within the WindowManager via slow layout animations. */ - private final ViewGroup mTargetViewContainer; + private ViewGroup mTargetViewContainer; /** Circle view used to render the dismiss target. */ - private final DismissCircleView mTargetView; + private DismissCircleView mTargetView; /** * MagneticTarget instance wrapping the target view and allowing us to set its magnetic radius. */ - private final MagnetizedObject.MagneticTarget mMagneticTarget; + private MagnetizedObject.MagneticTarget mMagneticTarget; - /** PhysicsAnimator instance for animating the dismiss target in/out. */ - private final PhysicsAnimator<View> mMagneticTargetAnimator; + /** + * PhysicsAnimator instance for animating the dismiss target in/out. + */ + private PhysicsAnimator<View> mMagneticTargetAnimator; /** Default configuration to use for springing the dismiss target in/out. */ private final PhysicsAnimator.SpringConfig mTargetSpringConfig = new PhysicsAnimator.SpringConfig( SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY); - /** - * Runnable that can be posted delayed to show the target. This needs to be saved as a member - * variable so we can pass it to removeCallbacks. - */ - private Runnable mShowTargetAction = this::showDismissTargetMaybe; - // Allow dragging the PIP to a location to close it private final boolean mEnableDismissDragToEdge; @@ -96,74 +95,76 @@ public class PipDismissTargetHandler { private final PipMotionHelper mMotionHelper; private final PipUiEventLogger mPipUiEventLogger; private final WindowManager mWindowManager; - private final Handler mHandler; + private final ShellExecutor mMainExecutor; public PipDismissTargetHandler(Context context, PipUiEventLogger pipUiEventLogger, - PipMotionHelper motionHelper, Handler handler) { + PipMotionHelper motionHelper, ShellExecutor mainExecutor) { mContext = context; mPipUiEventLogger = pipUiEventLogger; mMotionHelper = motionHelper; - mHandler = handler; + mMainExecutor = mainExecutor; mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); Resources res = context.getResources(); mEnableDismissDragToEdge = res.getBoolean(R.bool.config_pipEnableDismissDragToEdge); mDismissAreaHeight = res.getDimensionPixelSize(R.dimen.floating_dismiss_gradient_height); - mTargetView = new DismissCircleView(context); - mTargetViewContainer = new FrameLayout(context); - mTargetViewContainer.setBackgroundDrawable( - context.getDrawable(R.drawable.floating_dismiss_gradient_transition)); - mTargetViewContainer.setClipChildren(false); - mTargetViewContainer.addView(mTargetView); - - mMagnetizedPip = mMotionHelper.getMagnetizedPip(); - mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0); - updateMagneticTargetSize(); - - mMagnetizedPip.setAnimateStuckToTarget( - (target, velX, velY, flung, after) -> { + mMainExecutor.execute(() -> { + mTargetView = new DismissCircleView(context); + mTargetViewContainer = new FrameLayout(context); + mTargetViewContainer.setBackgroundDrawable( + context.getDrawable(R.drawable.floating_dismiss_gradient_transition)); + mTargetViewContainer.setClipChildren(false); + mTargetViewContainer.addView(mTargetView); + + mMagnetizedPip = mMotionHelper.getMagnetizedPip(); + mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0); + updateMagneticTargetSize(); + + mMagnetizedPip.setAnimateStuckToTarget( + (target, velX, velY, flung, after) -> { + if (mEnableDismissDragToEdge) { + mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, + after); + } + return Unit.INSTANCE; + }); + mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() { + @Override + public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) { + // Show the dismiss target, in case the initial touch event occurred within + // the magnetic field radius. if (mEnableDismissDragToEdge) { - mMotionHelper.animateIntoDismissTarget(target, velX, velY, flung, after); + showDismissTargetMaybe(); } - return Unit.INSTANCE; - }); - mMagnetizedPip.setMagnetListener(new MagnetizedObject.MagnetListener() { - @Override - public void onStuckToTarget(@NonNull MagnetizedObject.MagneticTarget target) { - // Show the dismiss target, in case the initial touch event occurred within the - // magnetic field radius. - if (mEnableDismissDragToEdge) { - showDismissTargetMaybe(); } - } - @Override - public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target, - float velX, float velY, boolean wasFlungOut) { - if (wasFlungOut) { - mMotionHelper.flingToSnapTarget(velX, velY, null /* endAction */); - hideDismissTargetMaybe(); - } else { - mMotionHelper.setSpringingToTouch(true); + @Override + public void onUnstuckFromTarget(@NonNull MagnetizedObject.MagneticTarget target, + float velX, float velY, boolean wasFlungOut) { + if (wasFlungOut) { + mMotionHelper.flingToSnapTarget(velX, velY, null /* endAction */); + hideDismissTargetMaybe(); + } else { + mMotionHelper.setSpringingToTouch(true); + } } - } - @Override - public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) { - mMotionHelper.notifyDismissalPending(); + @Override + public void onReleasedInTarget(@NonNull MagnetizedObject.MagneticTarget target) { + mMainExecutor.executeDelayed(() -> { + mMotionHelper.notifyDismissalPending(); + mMotionHelper.animateDismiss(); + hideDismissTargetMaybe(); - handler.post(() -> { - mMotionHelper.animateDismiss(); - hideDismissTargetMaybe(); - }); + mPipUiEventLogger.log( + PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE); + }, 0); + } + }); - mPipUiEventLogger.log( - PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_DRAG_TO_REMOVE); - } + mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView); }); - - mMagneticTargetAnimator = PhysicsAnimator.getInstance(mTargetView); } /** @@ -200,7 +201,6 @@ public class PipDismissTargetHandler { /** Adds the magnetic target view to the WindowManager so it's ready to be animated in. */ public void createOrUpdateDismissTarget() { if (!mTargetViewContainer.isAttachedToWindow()) { - mHandler.removeCallbacks(mShowTargetAction); mMagneticTargetAnimator.cancel(); mTargetViewContainer.setVisibility(View.INVISIBLE); @@ -270,7 +270,6 @@ public class PipDismissTargetHandler { return; } - mHandler.removeCallbacks(mShowTargetAction); mMagneticTargetAnimator .spring(DynamicAnimation.TRANSLATION_Y, mTargetViewContainer.getHeight(), @@ -286,8 +285,6 @@ public class PipDismissTargetHandler { * Removes the dismiss target and cancels any pending callbacks to show it. */ public void cleanUpDismissTarget() { - mHandler.removeCallbacks(mShowTargetAction); - if (mTargetViewContainer.isAttachedToWindow()) { mWindowManager.removeViewImmediate(mTargetViewContainer); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java index 0c64c8ca9b64..7a634c3eef78 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java @@ -29,6 +29,8 @@ import android.view.IWindowManager; import android.view.InputChannel; import android.view.InputEvent; +import com.android.wm.shell.common.ShellExecutor; + import java.io.PrintWriter; /** @@ -81,6 +83,7 @@ public class PipInputConsumer { private final IWindowManager mWindowManager; private final IBinder mToken; private final String mName; + private final ShellExecutor mMainExecutor; private InputEventReceiver mInputEventReceiver; private InputListener mListener; @@ -89,10 +92,12 @@ public class PipInputConsumer { /** * @param name the name corresponding to the input consumer that is defined in the system. */ - public PipInputConsumer(IWindowManager windowManager, String name) { + public PipInputConsumer(IWindowManager windowManager, String name, + ShellExecutor mainExecutor) { mWindowManager = windowManager; mToken = new Binder(); mName = name; + mMainExecutor = mainExecutor; } /** @@ -107,9 +112,11 @@ public class PipInputConsumer { */ public void setRegistrationListener(RegistrationListener listener) { mRegistrationListener = listener; - if (mRegistrationListener != null) { - mRegistrationListener.onRegistrationChanged(mInputEventReceiver != null); - } + mMainExecutor.execute(() -> { + if (mRegistrationListener != null) { + mRegistrationListener.onRegistrationChanged(mInputEventReceiver != null); + } + }); } /** @@ -125,14 +132,6 @@ public class PipInputConsumer { * Registers the input consumer. */ public void registerInputConsumer() { - registerInputConsumer(false); - } - - /** - * Registers the input consumer. - * @param withSfVsync the flag set using sf vsync signal or no - */ - public void registerInputConsumer(boolean withSfVsync) { if (mInputEventReceiver != null) { return; } @@ -144,11 +143,15 @@ public class PipInputConsumer { } catch (RemoteException e) { Log.e(TAG, "Failed to create input consumer", e); } - mInputEventReceiver = new InputEventReceiver(inputChannel, Looper.myLooper(), - withSfVsync ? Choreographer.getSfInstance() : Choreographer.getInstance()); - if (mRegistrationListener != null) { - mRegistrationListener.onRegistrationChanged(true /* isRegistered */); - } + mMainExecutor.execute(() -> { + // Choreographer.getSfInstance() must be called on the thread that the input event + // receiver should be receiving events + mInputEventReceiver = new InputEventReceiver(inputChannel, + mMainExecutor.getLooper(), Choreographer.getSfInstance()); + if (mRegistrationListener != null) { + mRegistrationListener.onRegistrationChanged(true /* isRegistered */); + } + }); } /** @@ -166,9 +169,11 @@ public class PipInputConsumer { } mInputEventReceiver.dispose(); mInputEventReceiver = null; - if (mRegistrationListener != null) { - mRegistrationListener.onRegistrationChanged(false /* isRegistered */); - } + mMainExecutor.execute(() -> { + if (mRegistrationListener != null) { + mRegistrationListener.onRegistrationChanged(false /* isRegistered */); + } + }); } public void dump(PrintWriter pw, String prefix) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java index 2e10fc93cafb..2e515ee91d3a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java @@ -63,6 +63,7 @@ import android.widget.LinearLayout; import com.android.wm.shell.R; import com.android.wm.shell.animation.Interpolators; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.PipUtils; import java.util.ArrayList; @@ -116,7 +117,8 @@ public class PipMenuView extends FrameLayout { } }; - private Handler mHandler = new Handler(); + private ShellExecutor mMainExecutor; + private Handler mMainHandler; private final Runnable mHideMenuRunnable = this::hideMenu; @@ -127,10 +129,13 @@ public class PipMenuView extends FrameLayout { protected View mTopEndContainer; protected PipMenuIconsAlgorithm mPipMenuIconsAlgorithm; - public PipMenuView(Context context, PhonePipMenuController controller) { + public PipMenuView(Context context, PhonePipMenuController controller, + ShellExecutor mainExecutor, Handler mainHandler) { super(context, null, 0); mContext = context; mController = controller; + mMainExecutor = mainExecutor; + mMainHandler = mainHandler; mAccessibilityManager = context.getSystemService(AccessibilityManager.class); inflate(context, R.layout.pip_menu, this); @@ -412,17 +417,15 @@ public class PipMenuView extends FrameLayout { d.setTint(Color.WHITE); actionView.setImageDrawable(d); } - }, mHandler); + }, mMainHandler); actionView.setContentDescription(action.getContentDescription()); if (action.isEnabled()) { actionView.setOnClickListener(v -> { - mHandler.post(() -> { - try { - action.getActionIntent().send(); - } catch (CanceledException e) { - Log.w(TAG, "Failed to send action", e); - } - }); + try { + action.getActionIntent().send(); + } catch (CanceledException e) { + Log.w(TAG, "Failed to send action", e); + } }); } actionView.setEnabled(action.isEnabled()); @@ -480,13 +483,13 @@ public class PipMenuView extends FrameLayout { } private void cancelDelayedHide() { - mHandler.removeCallbacks(mHideMenuRunnable); + mMainExecutor.removeCallbacks(mHideMenuRunnable); } private void repostDelayedHide(int delay) { int recommendedTimeout = mAccessibilityManager.getRecommendedTimeoutMillis(delay, FLAG_CONTENT_ICONS | FLAG_CONTENT_CONTROLS); - mHandler.removeCallbacks(mHideMenuRunnable); - mHandler.postDelayed(mHideMenuRunnable, recommendedTimeout); + mMainExecutor.removeCallbacks(mHideMenuRunnable); + mMainExecutor.executeDelayed(mHideMenuRunnable, recommendedTimeout); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java index 8c8f5c6d4940..4df7cef12f63 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java @@ -40,6 +40,7 @@ import androidx.dynamicanimation.animation.SpringForce; import com.android.wm.shell.animation.FloatProperties; import com.android.wm.shell.animation.PhysicsAnimator; import com.android.wm.shell.common.FloatingContentCoordinator; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.magnetictarget.MagnetizedObject; import com.android.wm.shell.pip.PipBoundsState; import com.android.wm.shell.pip.PipSnapAlgorithm; @@ -74,8 +75,6 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, private PhonePipMenuController mMenuController; private PipSnapAlgorithm mSnapAlgorithm; - private final Handler mMainHandler = new Handler(Looper.getMainLooper()); - /** The region that all of PIP must stay within. */ private final Rect mFloatingAllowedArea = new Rect(); @@ -130,10 +129,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY); private final Consumer<Rect> mUpdateBoundsCallback = (Rect newBounds) -> { - mMainHandler.post(() -> { - mMenuController.updateMenuLayout(newBounds); - mPipBoundsState.setBounds(newBounds); - }); + mMenuController.updateMenuLayout(newBounds); + mPipBoundsState.setBounds(newBounds); }; /** @@ -174,7 +171,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, public PipMotionHelper(Context context, @NonNull PipBoundsState pipBoundsState, PipTaskOrganizer pipTaskOrganizer, PhonePipMenuController menuController, - PipSnapAlgorithm snapAlgorithm, FloatingContentCoordinator floatingContentCoordinator) { + PipSnapAlgorithm snapAlgorithm, FloatingContentCoordinator floatingContentCoordinator, + ShellExecutor mainExecutor) { mContext = context; mPipTaskOrganizer = pipTaskOrganizer; mPipBoundsState = pipBoundsState; @@ -184,8 +182,12 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, mPipTaskOrganizer.registerPipTransitionCallback(mPipTransitionCallback); mTemporaryBoundsPhysicsAnimator = PhysicsAnimator.getInstance( mPipBoundsState.getMotionBoundsState().getBoundsInMotion()); - mTemporaryBoundsPhysicsAnimator.setCustomAnimationHandler( - mSfAnimationHandlerThreadLocal.get()); + + // Need to get the shell main thread sf vsync animation handler + mainExecutor.execute(() -> { + mTemporaryBoundsPhysicsAnimator.setCustomAnimationHandler( + mSfAnimationHandlerThreadLocal.get()); + }); mResizePipUpdateListener = (target, values) -> { if (mPipBoundsState.getMotionBoundsState().isInMotion()) { @@ -256,10 +258,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, mPipBoundsState.getMotionBoundsState().setBoundsInMotion(toBounds); mPipTaskOrganizer.scheduleUserResizePip(getBounds(), toBounds, (Rect newBounds) -> { - mMainHandler.post(() -> { mMenuController.updateMenuLayout(newBounds); - }); - }); + }); } } else { // If PIP is 'catching up' after being stuck in the dismiss target, update the animation @@ -326,11 +326,7 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, } cancelPhysicsAnimation(); mMenuController.hideMenuWithoutResize(); - mPipTaskOrganizer.getUpdateHandler().post(() -> { - mPipTaskOrganizer.exitPip(skipAnimation - ? 0 - : LEAVE_PIP_DURATION); - }); + mPipTaskOrganizer.exitPip(skipAnimation ? 0 : LEAVE_PIP_DURATION); } /** @@ -393,7 +389,8 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, .spring(FloatProperties.RECT_WIDTH, getBounds().width(), mSpringConfig) .spring(FloatProperties.RECT_HEIGHT, getBounds().height(), mSpringConfig) .flingThenSpring( - FloatProperties.RECT_X, velocityX, isStash ? mStashConfigX : mFlingConfigX, + FloatProperties.RECT_X, velocityX, + isStash ? mStashConfigX : mFlingConfigX, mSpringConfig, true /* flingMustReachMinOrMax */) .flingThenSpring( FloatProperties.RECT_Y, velocityY, mFlingConfigY, mSpringConfig); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java index 762b73800c23..41cc59d138fa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -29,7 +29,6 @@ import android.graphics.PointF; import android.graphics.Rect; import android.graphics.Region; import android.hardware.input.InputManager; -import android.os.Handler; import android.os.Looper; import android.provider.DeviceConfig; import android.view.BatchedInputEventReceiver; @@ -45,6 +44,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.policy.TaskResizingAlgorithm; import com.android.wm.shell.R; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.PipAnimationController; import com.android.wm.shell.pip.PipBoundsAlgorithm; import com.android.wm.shell.pip.PipBoundsState; @@ -52,7 +52,7 @@ import com.android.wm.shell.pip.PipTaskOrganizer; import com.android.wm.shell.pip.PipUiEventLogger; import java.io.PrintWriter; -import java.util.concurrent.Executor; +import java.util.function.Consumer; import java.util.function.Function; /** @@ -73,7 +73,7 @@ public class PipResizeGestureHandler { private final PhonePipMenuController mPhonePipMenuController; private final PipUiEventLogger mPipUiEventLogger; private final int mDisplayId; - private final Executor mMainExecutor; + private final ShellExecutor mMainExecutor; private final Region mTmpRegion = new Region(); private final PointF mDownPoint = new PointF(); @@ -91,7 +91,6 @@ public class PipResizeGestureHandler { private final Rect mDisplayBounds = new Rect(); private final Function<Rect, Rect> mMovementBoundsSupplier; private final Runnable mUpdateMovementBoundsRunnable; - private final Handler mHandler; private int mDelta; private float mTouchSlop; @@ -119,10 +118,10 @@ public class PipResizeGestureHandler { PipBoundsState pipBoundsState, PipMotionHelper motionHelper, PipTaskOrganizer pipTaskOrganizer, Function<Rect, Rect> movementBoundsSupplier, Runnable updateMovementBoundsRunnable, PipUiEventLogger pipUiEventLogger, - PhonePipMenuController menuActivityController) { + PhonePipMenuController menuActivityController, ShellExecutor mainExecutor) { mContext = context; mDisplayId = context.getDisplayId(); - mMainExecutor = context.getMainExecutor(); + mMainExecutor = mainExecutor; mPipBoundsAlgorithm = pipBoundsAlgorithm; mPipBoundsState = pipBoundsState; mMotionHelper = motionHelper; @@ -131,7 +130,6 @@ public class PipResizeGestureHandler { mUpdateMovementBoundsRunnable = updateMovementBoundsRunnable; mPhonePipMenuController = menuActivityController; mPipUiEventLogger = pipUiEventLogger; - mHandler = new Handler(Looper.getMainLooper()); context.getDisplay().getRealSize(mMaxSize); reloadResources(); @@ -140,7 +138,8 @@ public class PipResizeGestureHandler { DeviceConfig.NAMESPACE_SYSTEMUI, PIP_PINCH_RESIZE, /* defaultValue = */ false); - DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, mMainExecutor, + DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, + mMainExecutor, new DeviceConfig.OnPropertiesChangedListener() { @Override public void onPropertiesChanged(DeviceConfig.Properties properties) { @@ -213,8 +212,8 @@ public class PipResizeGestureHandler { // Register input event receiver mInputMonitor = InputManager.getInstance().monitorGestureInput( "pip-resize", mDisplayId); - mInputEventReceiver = new SysUiInputEventReceiver( - mInputMonitor.getInputChannel(), Looper.getMainLooper()); + mInputEventReceiver = new PipResizeInputEventReceiver( + mInputMonitor.getInputChannel(), mMainExecutor.getLooper()); } } @@ -523,7 +522,7 @@ public class PipResizeGestureHandler { private void finishResize() { if (!mLastResizeBounds.isEmpty()) { - final Runnable callback = () -> { + final Consumer<Rect> callback = (rect) -> { mUserResizeBounds.set(mLastResizeBounds); mMotionHelper.synchronizePinnedStackBounds(); mUpdateMovementBoundsRunnable.run(); @@ -537,16 +536,10 @@ public class PipResizeGestureHandler { mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, mPipBoundsAlgorithm.getSnapFraction(mPipBoundsState.getBounds())); mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds, - PINCH_RESIZE_SNAP_DURATION, mAngle, - (Rect rect) -> { - mHandler.post(callback); - }); + PINCH_RESIZE_SNAP_DURATION, -mAngle, callback); } else { mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds, - PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE, - (Rect bounds) -> { - mHandler.post(callback); - }); + PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE, callback); } mPipUiEventLogger.log( PipUiEventLogger.PipUiEventEnum.PICTURE_IN_PICTURE_RESIZE); @@ -593,8 +586,8 @@ public class PipResizeGestureHandler { pw.println(innerPrefix + "mThresholdCrossed=" + mThresholdCrossed); } - class SysUiInputEventReceiver extends BatchedInputEventReceiver { - SysUiInputEventReceiver(InputChannel channel, Looper looper) { + class PipResizeInputEventReceiver extends BatchedInputEventReceiver { + PipResizeInputEventReceiver(InputChannel channel, Looper looper) { super(channel, looper, Choreographer.getSfInstance()); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java index 75d674e4ceea..c9f5ae28175c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java @@ -167,18 +167,20 @@ public class PipTouchHandler { mGesture = new DefaultPipTouchGesture(); mMotionHelper = new PipMotionHelper(mContext, pipBoundsState, pipTaskOrganizer, mMenuController, mPipBoundsAlgorithm.getSnapAlgorithm(), - floatingContentCoordinator); + floatingContentCoordinator, mainExecutor); mPipResizeGestureHandler = new PipResizeGestureHandler(context, pipBoundsAlgorithm, pipBoundsState, mMotionHelper, pipTaskOrganizer, this::getMovementBounds, - this::updateMovementBounds, pipUiEventLogger, menuController); + this::updateMovementBounds, pipUiEventLogger, menuController, + mainExecutor); mPipDismissTargetHandler = new PipDismissTargetHandler(context, pipUiEventLogger, - mMotionHelper, mHandler); - mTouchState = new PipTouchState(ViewConfiguration.get(context), mHandler, + mMotionHelper, mainExecutor); + mTouchState = new PipTouchState(ViewConfiguration.get(context), () -> mMenuController.showMenuWithDelay(MENU_STATE_FULL, mPipBoundsState.getBounds(), true /* allowMenuTimeout */, willResizeMenu(), shouldShowResizeHandle()), - menuController::hideMenu); + menuController::hideMenu, + mainExecutor); Resources res = context.getResources(); mEnableResize = res.getBoolean(R.bool.config_pipEnableResizeForMenu); @@ -196,7 +198,7 @@ public class PipTouchHandler { PIP_STASHING, /* defaultValue = */ true); DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_SYSTEMUI, - context.getMainExecutor(), + mainExecutor, properties -> { if (properties.getKeyset().contains(PIP_STASHING)) { mEnableStash = properties.getBoolean( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java index 5f2327ce98d6..53303ff2b679 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java @@ -25,6 +25,7 @@ import android.view.VelocityTracker; import android.view.ViewConfiguration; import com.android.internal.annotations.VisibleForTesting; +import com.android.wm.shell.common.ShellExecutor; import java.io.PrintWriter; @@ -39,7 +40,7 @@ public class PipTouchState { public static final long DOUBLE_TAP_TIMEOUT = 200; static final long HOVER_EXIT_TIMEOUT = 50; - private final Handler mHandler; + private final ShellExecutor mMainExecutor; private final ViewConfiguration mViewConfig; private final Runnable mDoubleTapTimeoutCallback; private final Runnable mHoverExitTimeoutCallback; @@ -67,12 +68,12 @@ public class PipTouchState { private int mActivePointerId; private int mLastTouchDisplayId = Display.INVALID_DISPLAY; - public PipTouchState(ViewConfiguration viewConfig, Handler handler, - Runnable doubleTapTimeoutCallback, Runnable hoverExitTimeoutCallback) { + public PipTouchState(ViewConfiguration viewConfig, Runnable doubleTapTimeoutCallback, + Runnable hoverExitTimeoutCallback, ShellExecutor mainExecutor) { mViewConfig = viewConfig; - mHandler = handler; mDoubleTapTimeoutCallback = doubleTapTimeoutCallback; mHoverExitTimeoutCallback = hoverExitTimeoutCallback; + mMainExecutor = mainExecutor; } /** @@ -116,7 +117,7 @@ public class PipTouchState { mIsDragging = false; mLastDownTouchTime = mDownTouchTime; if (mDoubleTapTimeoutCallback != null) { - mHandler.removeCallbacks(mDoubleTapTimeoutCallback); + mMainExecutor.removeCallbacks(mDoubleTapTimeoutCallback); } break; } @@ -324,8 +325,8 @@ public class PipTouchState { public void scheduleDoubleTapTimeoutCallback() { if (mIsWaitingForDoubleTap) { long delay = getDoubleTapTimeoutCallbackDelay(); - mHandler.removeCallbacks(mDoubleTapTimeoutCallback); - mHandler.postDelayed(mDoubleTapTimeoutCallback, delay); + mMainExecutor.removeCallbacks(mDoubleTapTimeoutCallback); + mMainExecutor.executeDelayed(mDoubleTapTimeoutCallback, delay); } } @@ -342,17 +343,17 @@ public class PipTouchState { */ public void removeDoubleTapTimeoutCallback() { mIsWaitingForDoubleTap = false; - mHandler.removeCallbacks(mDoubleTapTimeoutCallback); + mMainExecutor.removeCallbacks(mDoubleTapTimeoutCallback); } @VisibleForTesting public void scheduleHoverExitTimeoutCallback() { - mHandler.removeCallbacks(mHoverExitTimeoutCallback); - mHandler.postDelayed(mHoverExitTimeoutCallback, HOVER_EXIT_TIMEOUT); + mMainExecutor.removeCallbacks(mHoverExitTimeoutCallback); + mMainExecutor.executeDelayed(mHoverExitTimeoutCallback, HOVER_EXIT_TIMEOUT); } void removeHoverExitTimeoutCallback() { - mHandler.removeCallbacks(mHoverExitTimeoutCallback); + mMainExecutor.removeCallbacks(mHoverExitTimeoutCallback); } void addMovementToVelocityTracker(MotionEvent event) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipUpdateThread.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipUpdateThread.java deleted file mode 100644 index d686cac3457b..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipUpdateThread.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.pip.phone; - -import android.os.Handler; -import android.os.HandlerThread; - -/** - * Similar to {@link com.android.internal.os.BackgroundThread}, this is a shared singleton - * foreground thread for each process for updating PIP. - */ -public final class PipUpdateThread extends HandlerThread { - private static PipUpdateThread sInstance; - private static Handler sHandler; - - private PipUpdateThread() { - super("pip"); - } - - private static void ensureThreadLocked() { - if (sInstance == null) { - sInstance = new PipUpdateThread(); - sInstance.start(); - sHandler = new Handler(sInstance.getLooper()); - } - } - - /** - * @return the static update thread instance - */ - public static PipUpdateThread get() { - synchronized (PipUpdateThread.class) { - ensureThreadLocked(); - return sInstance; - } - } - /** - * @return the static update thread handler instance - */ - public static Handler getHandler() { - synchronized (PipUpdateThread.class) { - ensureThreadLocked(); - return sHandler; - } - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java index 8bc60f9dbcd9..61cf22b7164b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java @@ -36,6 +36,7 @@ import android.view.DisplayInfo; import com.android.wm.shell.R; import com.android.wm.shell.WindowManagerShellWrapper; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TaskStackListenerCallback; import com.android.wm.shell.common.TaskStackListenerImpl; import com.android.wm.shell.pip.PinnedStackListenerForwarder; @@ -51,7 +52,7 @@ import java.lang.annotation.RetentionPolicy; /** * Manages the picture-in-picture (PIP) UI and states. */ -public class TvPipController implements Pip, PipTaskOrganizer.PipTransitionCallback, +public class TvPipController implements PipTaskOrganizer.PipTransitionCallback, TvPipMenuController.Delegate, TvPipNotificationController.Delegate { private static final String TAG = "TvPipController"; static final boolean DEBUG = true; @@ -90,13 +91,15 @@ public class TvPipController implements Pip, PipTaskOrganizer.PipTransitionCallb private final PipMediaController mPipMediaController; private final TvPipNotificationController mPipNotificationController; private final TvPipMenuController mTvPipMenuController; + private final ShellExecutor mMainExecutor; + private final TvPipImpl mImpl = new TvPipImpl(); private @State int mState = STATE_NO_PIP; private int mPinnedTaskId = NONEXISTENT_TASK_ID; private int mResizeAnimationDuration; - public TvPipController( + public static Pip create( Context context, PipBoundsState pipBoundsState, PipBoundsAlgorithm pipBoundsAlgorithm, @@ -105,8 +108,34 @@ public class TvPipController implements Pip, PipTaskOrganizer.PipTransitionCallb PipMediaController pipMediaController, TvPipNotificationController pipNotificationController, TaskStackListenerImpl taskStackListener, - WindowManagerShellWrapper wmShell) { + WindowManagerShellWrapper wmShell, + ShellExecutor mainExecutor) { + return new TvPipController( + context, + pipBoundsState, + pipBoundsAlgorithm, + pipTaskOrganizer, + tvPipMenuController, + pipMediaController, + pipNotificationController, + taskStackListener, + wmShell, + mainExecutor).mImpl; + } + + private TvPipController( + Context context, + PipBoundsState pipBoundsState, + PipBoundsAlgorithm pipBoundsAlgorithm, + PipTaskOrganizer pipTaskOrganizer, + TvPipMenuController tvPipMenuController, + PipMediaController pipMediaController, + TvPipNotificationController pipNotificationController, + TaskStackListenerImpl taskStackListener, + WindowManagerShellWrapper wmShell, + ShellExecutor mainExecutor) { mContext = context; + mMainExecutor = mainExecutor; mPipBoundsState = pipBoundsState; mPipBoundsState.setDisplayInfo(getDisplayInfo()); @@ -129,8 +158,7 @@ public class TvPipController implements Pip, PipTaskOrganizer.PipTransitionCallb registerWmShellPinnedStackListener(wmShell); } - @Override - public void onConfigurationChanged(Configuration newConfig) { + private void onConfigurationChanged(Configuration newConfig) { if (DEBUG) Log.d(TAG, "onConfigurationChanged(), state=" + stateToName(mState)); if (isPipShown()) { @@ -145,8 +173,7 @@ public class TvPipController implements Pip, PipTaskOrganizer.PipTransitionCallb /** * Returns {@code true} if Pip is shown. */ - @Override - public boolean isPipShown() { + private boolean isPipShown() { return mState != STATE_NO_PIP; } @@ -211,8 +238,7 @@ public class TvPipController implements Pip, PipTaskOrganizer.PipTransitionCallb * @param state the to determine the Pip bounds. IMPORTANT: should always match the current * state of the Controller. */ - @Override - public void resizePinnedStack(@State int state) { + private void resizePinnedStack(@State int state) { if (state != mState) { throw new IllegalArgumentException("The passed state should match the current state!"); } @@ -240,8 +266,7 @@ public class TvPipController implements Pip, PipTaskOrganizer.PipTransitionCallb mPipTaskOrganizer.scheduleAnimateResizePip(newBounds, mResizeAnimationDuration, null); } - @Override - public void registerSessionListenerForCurrentUser() { + private void registerSessionListenerForCurrentUser() { mPipMediaController.registerSessionListenerForCurrentUser(); } @@ -418,4 +443,20 @@ public class TvPipController implements Pip, PipTaskOrganizer.PipTransitionCallb throw new IllegalArgumentException("Unknown state " + state); } } + + private class TvPipImpl implements Pip { + @Override + public void onConfigurationChanged(Configuration newConfig) { + mMainExecutor.execute(() -> { + TvPipController.this.onConfigurationChanged(newConfig); + }); + } + + @Override + public void registerSessionListenerForCurrentUser() { + mMainExecutor.execute(() -> { + TvPipController.this.registerSessionListenerForCurrentUser(); + }); + } + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java index 470ab0c9c0e3..ee41b41a743d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java @@ -24,6 +24,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ParceledListSlice; +import android.os.Handler; import android.util.Log; import android.view.SurfaceControl; @@ -47,6 +48,7 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis private final Context mContext; private final SystemWindows mSystemWindows; private final PipBoundsState mPipBoundsState; + private final Handler mMainHandler; private Delegate mDelegate; private SurfaceControl mLeash; @@ -56,10 +58,12 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis private final List<RemoteAction> mAppActions = new ArrayList<>(); public TvPipMenuController(Context context, PipBoundsState pipBoundsState, - SystemWindows systemWindows, PipMediaController pipMediaController) { + SystemWindows systemWindows, PipMediaController pipMediaController, + Handler mainHandler) { mContext = context; mPipBoundsState = pipBoundsState; mSystemWindows = systemWindows; + mMainHandler = mainHandler; // We need to "close" the menu the platform call for all the system dialogs to close (for // example, on the Home button press). @@ -69,8 +73,9 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis hideMenu(); } }; - context.registerReceiver(closeSystemDialogsBroadcastReceiver, - new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); + context.registerReceiverForAllUsers(closeSystemDialogsBroadcastReceiver, + new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), null /* permission */, + mainHandler); pipMediaController.addActionListener(this::onMediaActionsChanged); } @@ -199,9 +204,9 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis return; } if (!mAppActions.isEmpty()) { - mMenuView.setAdditionalActions(mAppActions); + mMenuView.setAdditionalActions(mAppActions, mMainHandler); } else { - mMenuView.setAdditionalActions(mMediaActions); + mMenuView.setAdditionalActions(mMediaActions, mMainHandler); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java index e08ca52fd2ca..d6cd9ea13ca1 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java @@ -49,7 +49,7 @@ import java.util.List; /** * A View that represents Pip Menu on TV. It's responsible for displaying 2 ever-present Pip Menu * actions: Fullscreen and Close, but could also display "additional" actions, that may be set via - * a {@link #setAdditionalActions(List)} call. + * a {@link #setAdditionalActions(List, Handler)} call. */ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { private static final String TAG = "TvPipMenuView"; @@ -57,7 +57,6 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { private static final float DISABLED_ACTION_ALPHA = 0.54f; - private final Handler mUiThreadHandler; private final Animator mFadeInAnimation; private final Animator mFadeOutAnimation; @Nullable private Listener mListener; @@ -80,7 +79,6 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { public TvPipMenuView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - mUiThreadHandler = new Handler(Looper.getMainLooper()); inflate(context, R.layout.tv_pip_menu, this); @@ -132,7 +130,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { } } - void setAdditionalActions(List<RemoteAction> actions) { + void setAdditionalActions(List<RemoteAction> actions, Handler mainHandler) { if (DEBUG) Log.d(TAG, "setAdditionalActions()"); // Make sure we exactly as many additional buttons as we have actions to display. @@ -176,7 +174,7 @@ public class TvPipMenuView extends FrameLayout implements View.OnClickListener { action.getIcon().loadDrawableAsync(mContext, drawable -> { drawable.setTint(Color.WHITE); button.setImageDrawable(drawable); - }, mUiThreadHandler); + }, mainHandler); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java index ce4b60893367..a47483144fef 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java @@ -27,6 +27,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.media.MediaMetadata; +import android.os.Handler; import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; @@ -60,6 +61,7 @@ public class TvPipNotificationController { private final NotificationManager mNotificationManager; private final Notification.Builder mNotificationBuilder; private final ActionBroadcastReceiver mActionBroadcastReceiver; + private final Handler mMainHandler; private Delegate mDelegate; private String mDefaultTitle; @@ -70,10 +72,12 @@ public class TvPipNotificationController { private String mMediaTitle; private Bitmap mArt; - public TvPipNotificationController(Context context, PipMediaController pipMediaController) { + public TvPipNotificationController(Context context, PipMediaController pipMediaController, + Handler mainHandler) { mContext = context; mPackageManager = context.getPackageManager(); mNotificationManager = context.getSystemService(NotificationManager.class); + mMainHandler = mainHandler; mNotificationBuilder = new Notification.Builder(context, NOTIFICATION_CHANNEL) .setLocalOnly(true) @@ -219,7 +223,8 @@ public class TvPipNotificationController { void register() { if (mRegistered) return; - mContext.registerReceiver(this, mIntentFilter, UserHandle.USER_ALL); + mContext.registerReceiverForAllUsers(this, mIntentFilter, null /* permission */, + mMainHandler); mRegistered = true; } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java index 9eb13fb1c5e7..5f5c30bb6207 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java @@ -46,7 +46,7 @@ public class TestShellExecutor implements ShellExecutor { @Override public boolean hasCallback(Runnable r) { - return !mRunnables.isEmpty(); + return mRunnables.contains(r); } @Override @@ -55,8 +55,8 @@ public class TestShellExecutor implements ShellExecutor { } public void flushAll() { - for (int i = mRunnables.size() - 1; i >= 0; --i) { - mRunnables.get(i).run(); + for (Runnable r : mRunnables) { + r.run(); } mRunnables.clear(); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java index b5d10d71e693..245858d14b49 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java @@ -43,7 +43,9 @@ import android.window.WindowContainerToken; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.pip.phone.PhonePipMenuController; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; @@ -71,6 +73,7 @@ public class PipTaskOrganizerTest extends ShellTestCase { @Mock private PipUiEventLogger mMockPipUiEventLogger; @Mock private Optional<LegacySplitScreen> mMockOptionalSplitScreen; @Mock private ShellTaskOrganizer mMockShellTaskOrganizer; + private TestShellExecutor mMainExecutor; private PipBoundsState mPipBoundsState; private ComponentName mComponent1; @@ -82,10 +85,12 @@ public class PipTaskOrganizerTest extends ShellTestCase { mComponent1 = new ComponentName(mContext, "component1"); mComponent2 = new ComponentName(mContext, "component2"); mPipBoundsState = new PipBoundsState(mContext); + mMainExecutor = new TestShellExecutor(); mSpiedPipTaskOrganizer = spy(new PipTaskOrganizer(mContext, mPipBoundsState, mMockPipBoundsAlgorithm, mMockPhonePipMenuController, mMockPipSurfaceTransactionHelper, mMockOptionalSplitScreen, mMockdDisplayController, - mMockPipUiEventLogger, mMockShellTaskOrganizer)); + mMockPipUiEventLogger, mMockShellTaskOrganizer, mMainExecutor)); + mMainExecutor.flushAll(); preparePipTaskOrg(); } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java index 000f7e8b2e85..0d4d1269f767 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java @@ -24,18 +24,15 @@ import static android.view.MotionEvent.ACTION_UP; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import android.os.Handler; -import android.os.Looper; import android.os.SystemClock; import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; -import android.testing.TestableLooper.RunWithLooper; import android.view.MotionEvent; import android.view.ViewConfiguration; import androidx.test.filters.SmallTest; import com.android.wm.shell.ShellTestCase; +import com.android.wm.shell.TestShellExecutor; import org.junit.Before; import org.junit.Test; @@ -45,23 +42,22 @@ import java.util.concurrent.CountDownLatch; @RunWith(AndroidTestingRunner.class) @SmallTest -@RunWithLooper public class PipTouchStateTest extends ShellTestCase { private PipTouchState mTouchState; private CountDownLatch mDoubleTapCallbackTriggeredLatch; private CountDownLatch mHoverExitCallbackTriggeredLatch; + private TestShellExecutor mShellMainExecutor; @Before public void setUp() throws Exception { + mShellMainExecutor = new TestShellExecutor(); mDoubleTapCallbackTriggeredLatch = new CountDownLatch(1); mHoverExitCallbackTriggeredLatch = new CountDownLatch(1); mTouchState = new PipTouchState(ViewConfiguration.get(getContext()), - Handler.createAsync(Looper.myLooper()), () -> { - mDoubleTapCallbackTriggeredLatch.countDown(); - }, () -> { - mHoverExitCallbackTriggeredLatch.countDown(); - }); + mDoubleTapCallbackTriggeredLatch::countDown, + mHoverExitCallbackTriggeredLatch::countDown, + mShellMainExecutor); assertFalse(mTouchState.isDoubleTap()); assertFalse(mTouchState.isWaitingForDoubleTap()); } @@ -91,9 +87,7 @@ public class PipTouchStateTest extends ShellTestCase { assertTrue(mTouchState.getDoubleTapTimeoutCallbackDelay() == 10); mTouchState.scheduleDoubleTapTimeoutCallback(); - // TODO: Remove this sleep. Its only being added because it speeds up this test a bit. - Thread.sleep(15); - TestableLooper.get(this).processAllMessages(); + mShellMainExecutor.flushAll(); assertTrue(mDoubleTapCallbackTriggeredLatch.getCount() == 0); } @@ -128,17 +122,13 @@ public class PipTouchStateTest extends ShellTestCase { @Test public void testHoverExitTimeout_timeoutCallbackCalled() throws Exception { mTouchState.scheduleHoverExitTimeoutCallback(); - - // TODO: Remove this sleep. Its only being added because it speeds up this test a bit. - Thread.sleep(50); - TestableLooper.get(this).processAllMessages(); + mShellMainExecutor.flushAll(); assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 0); } @Test public void testHoverExitTimeout_timeoutCallbackNotCalled() throws Exception { mTouchState.scheduleHoverExitTimeoutCallback(); - TestableLooper.get(this).processAllMessages(); assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 1); } @@ -147,14 +137,12 @@ public class PipTouchStateTest extends ShellTestCase { mTouchState.scheduleHoverExitTimeoutCallback(); mTouchState.onTouchEvent(createMotionEvent(ACTION_BUTTON_PRESS, SystemClock.uptimeMillis(), 0, 0)); - - // TODO: Remove this sleep. Its only being added because it speeds up this test a bit. - Thread.sleep(50); - TestableLooper.get(this).processAllMessages(); + mShellMainExecutor.flushAll(); assertTrue(mHoverExitCallbackTriggeredLatch.getCount() == 1); } private MotionEvent createMotionEvent(int action, long eventTime, float x, float y) { return MotionEvent.obtain(0, eventTime, action, x, y, 0); } + } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java index 8a79acef756c..416de0465f1a 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java @@ -17,13 +17,16 @@ package com.android.systemui.wmshell; import android.content.Context; +import android.os.Handler; import com.android.systemui.dagger.WMSingleton; import com.android.wm.shell.ShellTaskOrganizer; import com.android.wm.shell.WindowManagerShellWrapper; import com.android.wm.shell.common.DisplayController; +import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.SystemWindows; import com.android.wm.shell.common.TaskStackListenerImpl; +import com.android.wm.shell.common.annotations.ShellMainThread; import com.android.wm.shell.legacysplitscreen.LegacySplitScreen; import com.android.wm.shell.pip.Pip; import com.android.wm.shell.pip.PipBoundsAlgorithm; @@ -57,9 +60,10 @@ public abstract class TvPipModule { PipMediaController pipMediaController, TvPipNotificationController tvPipNotificationController, TaskStackListenerImpl taskStackListener, - WindowManagerShellWrapper windowManagerShellWrapper) { + WindowManagerShellWrapper windowManagerShellWrapper, + @ShellMainThread ShellExecutor mainExecutor) { return Optional.of( - new TvPipController( + TvPipController.create( context, pipBoundsState, pipBoundsAlgorithm, @@ -68,7 +72,8 @@ public abstract class TvPipModule { pipMediaController, tvPipNotificationController, taskStackListener, - windowManagerShellWrapper)); + windowManagerShellWrapper, + mainExecutor)); } @WMSingleton @@ -84,21 +89,26 @@ public abstract class TvPipModule { return new PipBoundsState(context); } + // Handler needed for loadDrawableAsync() in PipControlsViewController @WMSingleton @Provides static TvPipMenuController providesTvPipMenuController( Context context, PipBoundsState pipBoundsState, SystemWindows systemWindows, - PipMediaController pipMediaController) { - return new TvPipMenuController(context, pipBoundsState, systemWindows, pipMediaController); + PipMediaController pipMediaController, + @ShellMainThread Handler mainHandler) { + return new TvPipMenuController(context, pipBoundsState, systemWindows, pipMediaController, + mainHandler); } + // Handler needed for registerReceiverForAllUsers() @WMSingleton @Provides static TvPipNotificationController provideTvPipNotificationController(Context context, - PipMediaController pipMediaController) { - return new TvPipNotificationController(context, pipMediaController); + PipMediaController pipMediaController, + @ShellMainThread Handler mainHandler) { + return new TvPipNotificationController(context, pipMediaController, mainHandler); } @WMSingleton @@ -109,9 +119,10 @@ public abstract class TvPipModule { PipBoundsAlgorithm pipBoundsAlgorithm, PipSurfaceTransactionHelper pipSurfaceTransactionHelper, Optional<LegacySplitScreen> splitScreenOptional, DisplayController displayController, - PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) { + PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, + @ShellMainThread ShellExecutor mainExecutor) { return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm, tvPipMenuController, pipSurfaceTransactionHelper, splitScreenOptional, - displayController, pipUiEventLogger, shellTaskOrganizer); + displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor); } } diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java index bbc238a35cbd..103e6bb08ed4 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java @@ -250,14 +250,17 @@ public abstract class WMShellBaseModule { @Provides static PipAppOpsListener providePipAppOpsListener(Context context, IActivityManager activityManager, - PipTouchHandler pipTouchHandler) { - return new PipAppOpsListener(context, activityManager, pipTouchHandler.getMotionHelper()); + PipTouchHandler pipTouchHandler, + @ShellMainThread ShellExecutor mainExecutor) { + return new PipAppOpsListener(context, pipTouchHandler.getMotionHelper(), mainExecutor); } + // Needs handler for registering broadcast receivers @WMSingleton @Provides - static PipMediaController providePipMediaController(Context context) { - return new PipMediaController(context); + static PipMediaController providePipMediaController(Context context, + @ShellMainThread Handler mainHandler) { + return new PipMediaController(context, mainHandler); } @WMSingleton diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java index 8105250f0ca0..12a3b5d66d55 100644 --- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java +++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java @@ -19,6 +19,7 @@ package com.android.systemui.wmshell; import android.animation.AnimationHandler; import android.app.ActivityTaskManager; import android.content.Context; +import android.os.Handler; import android.view.IWindowManager; import com.android.systemui.dagger.WMSingleton; @@ -125,11 +126,15 @@ public class WMShellModule { return new PipBoundsAlgorithm(context, pipBoundsState); } + // Handler is used by Icon.loadDrawableAsync @WMSingleton @Provides static PhonePipMenuController providesPipPhoneMenuController(Context context, - PipMediaController pipMediaController, SystemWindows systemWindows) { - return new PhonePipMenuController(context, pipMediaController, systemWindows); + PipMediaController pipMediaController, SystemWindows systemWindows, + @ShellMainThread ShellExecutor mainExecutor, + @ShellMainThread Handler mainHandler) { + return new PhonePipMenuController(context, pipMediaController, systemWindows, + mainExecutor, mainHandler); } @WMSingleton @@ -154,9 +159,10 @@ public class WMShellModule { PhonePipMenuController menuPhoneController, PipSurfaceTransactionHelper pipSurfaceTransactionHelper, Optional<LegacySplitScreen> splitScreenOptional, DisplayController displayController, - PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer) { + PipUiEventLogger pipUiEventLogger, ShellTaskOrganizer shellTaskOrganizer, + @ShellMainThread ShellExecutor mainExecutor) { return new PipTaskOrganizer(context, pipBoundsState, pipBoundsAlgorithm, menuPhoneController, pipSurfaceTransactionHelper, splitScreenOptional, - displayController, pipUiEventLogger, shellTaskOrganizer); + displayController, pipUiEventLogger, shellTaskOrganizer, mainExecutor); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java index 8b86403b554e..4078d4dad984 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java @@ -63,7 +63,6 @@ public class WMShellTest extends SysuiTestCase { @Mock ScreenLifecycle mScreenLifecycle; @Mock SysUiState mSysUiState; @Mock Pip mPip; - @Mock PipTouchHandler mPipTouchHandler; @Mock LegacySplitScreen mLegacySplitScreen; @Mock OneHanded mOneHanded; @Mock HideDisplayCutout mHideDisplayCutout; @@ -80,8 +79,6 @@ public class WMShellTest extends SysuiTestCase { Optional.of(mShellCommandHandler), mCommandQueue, mConfigurationController, mKeyguardUpdateMonitor, mNavigationModeController, mScreenLifecycle, mSysUiState, mProtoTracer, mSysUiMainExecutor); - - when(mPip.getPipTouchHandler()).thenReturn(mPipTouchHandler); } @Test |