summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/animation/PhysicsAnimator.kt5
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java11
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/Pip.java73
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMediaController.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java327
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipAppOpsListener.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java266
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipDismissTargetHandler.java125
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java45
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMenuView.java29
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipMotionHelper.java33
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java35
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java14
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchState.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipUpdateThread.java60
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipController.java63
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipNotificationController.java9
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java6
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/PipTaskOrganizerTest.java7
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipTouchStateTest.java32
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java29
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java11
-rw-r--r--packages/SystemUI/src/com/android/systemui/wmshell/WMShellModule.java14
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/wmshell/WMShellTest.java3
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