6/ Update pip to use shell main thread
- Remove some currently unused pip interface methods - Remove some usages of handler - Remove update thread (will eventually run on shell main thread) Bug: 161979899 Test: atest WMShellUnitTests Change-Id: If97e5182e396029120f0d0fb9e730ece93cddcc8 Signed-off-by: Winson Chung <winsonc@google.com>
This commit is contained in:
@@ -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.")
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -34,54 +34,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.
|
||||
*/
|
||||
@@ -100,12 +63,6 @@ public interface Pip {
|
||||
default void onOverlayChanged() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the session listener for the current user.
|
||||
*/
|
||||
default void registerSessionListenerForCurrentUser() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when SysUI state changed.
|
||||
*
|
||||
@@ -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.
|
||||
* Registers the session listener for the current user.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
default void resumePipResizing(int reason) {
|
||||
default void registerSessionListenerForCurrentUser() {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,14 +108,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
|
||||
@@ -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) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
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 = mMotionHelper.getMagnetizedPip();
|
||||
mMagneticTarget = mMagnetizedPip.addTarget(mTargetView, 0);
|
||||
updateMagneticTargetSize();
|
||||
|
||||
mMagnetizedPip.setAnimateStuckToTarget(
|
||||
(target, velX, velY, flung, after) -> {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user