Merge "Ensure that task org updates all happen off the main thread" into rvc-dev
This commit is contained in:
@@ -64,7 +64,7 @@ public class InputConsumerController {
|
||||
private final class InputEventReceiver extends BatchedInputEventReceiver {
|
||||
|
||||
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
|
||||
super(inputChannel, looper, Choreographer.getSfInstance());
|
||||
super(inputChannel, looper, Choreographer.getInstance());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -19,11 +19,8 @@ package com.android.systemui.pip;
|
||||
import android.animation.Animator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.MainThread;
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.os.RemoteException;
|
||||
import android.view.IWindowContainer;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.view.animation.Interpolator;
|
||||
@@ -61,31 +58,30 @@ public class PipAnimationController {
|
||||
com.android.internal.R.interpolator.fast_out_slow_in);
|
||||
}
|
||||
|
||||
@MainThread
|
||||
PipTransitionAnimator getAnimator(IWindowContainer wc, boolean scheduleFinishPip,
|
||||
PipTransitionAnimator getAnimator(SurfaceControl leash, boolean scheduleFinishPip,
|
||||
Rect destinationBounds, float alphaStart, float alphaEnd) {
|
||||
if (mCurrentAnimator == null) {
|
||||
mCurrentAnimator = setupPipTransitionAnimator(
|
||||
PipTransitionAnimator.ofAlpha(wc, scheduleFinishPip,
|
||||
destinationBounds, alphaStart, alphaEnd));
|
||||
PipTransitionAnimator.ofAlpha(leash, scheduleFinishPip, destinationBounds,
|
||||
alphaStart, alphaEnd));
|
||||
} else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_ALPHA
|
||||
&& mCurrentAnimator.isRunning()) {
|
||||
mCurrentAnimator.updateEndValue(alphaEnd);
|
||||
} else {
|
||||
mCurrentAnimator.cancel();
|
||||
mCurrentAnimator = setupPipTransitionAnimator(
|
||||
PipTransitionAnimator.ofAlpha(wc, scheduleFinishPip,
|
||||
destinationBounds, alphaStart, alphaEnd));
|
||||
PipTransitionAnimator.ofAlpha(leash, scheduleFinishPip, destinationBounds,
|
||||
alphaStart, alphaEnd));
|
||||
}
|
||||
return mCurrentAnimator;
|
||||
}
|
||||
|
||||
@MainThread
|
||||
PipTransitionAnimator getAnimator(IWindowContainer wc, boolean scheduleFinishPip,
|
||||
PipTransitionAnimator getAnimator(SurfaceControl leash, boolean scheduleFinishPip,
|
||||
Rect startBounds, Rect endBounds) {
|
||||
if (mCurrentAnimator == null) {
|
||||
mCurrentAnimator = setupPipTransitionAnimator(
|
||||
PipTransitionAnimator.ofBounds(wc, scheduleFinishPip, startBounds, endBounds));
|
||||
PipTransitionAnimator.ofBounds(leash, scheduleFinishPip,
|
||||
startBounds, endBounds));
|
||||
} else if (mCurrentAnimator.getAnimationType() == ANIM_TYPE_BOUNDS
|
||||
&& mCurrentAnimator.isRunning()) {
|
||||
mCurrentAnimator.setDestinationBounds(endBounds);
|
||||
@@ -94,7 +90,8 @@ public class PipAnimationController {
|
||||
} else {
|
||||
mCurrentAnimator.cancel();
|
||||
mCurrentAnimator = setupPipTransitionAnimator(
|
||||
PipTransitionAnimator.ofBounds(wc, scheduleFinishPip, startBounds, endBounds));
|
||||
PipTransitionAnimator.ofBounds(leash, scheduleFinishPip,
|
||||
startBounds, endBounds));
|
||||
}
|
||||
return mCurrentAnimator;
|
||||
}
|
||||
@@ -116,18 +113,18 @@ public class PipAnimationController {
|
||||
/**
|
||||
* Called when PiP animation is started.
|
||||
*/
|
||||
public void onPipAnimationStart(IWindowContainer wc, PipTransitionAnimator animator) {}
|
||||
public void onPipAnimationStart(PipTransitionAnimator animator) {}
|
||||
|
||||
/**
|
||||
* Called when PiP animation is ended.
|
||||
*/
|
||||
public void onPipAnimationEnd(IWindowContainer wc, SurfaceControl.Transaction tx,
|
||||
public void onPipAnimationEnd(SurfaceControl.Transaction tx,
|
||||
PipTransitionAnimator animator) {}
|
||||
|
||||
/**
|
||||
* Called when PiP animation is cancelled.
|
||||
*/
|
||||
public void onPipAnimationCancel(IWindowContainer wc, PipTransitionAnimator animator) {}
|
||||
public void onPipAnimationCancel(PipTransitionAnimator animator) {}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -137,7 +134,6 @@ public class PipAnimationController {
|
||||
public abstract static class PipTransitionAnimator<T> extends ValueAnimator implements
|
||||
ValueAnimator.AnimatorUpdateListener,
|
||||
ValueAnimator.AnimatorListener {
|
||||
private final IWindowContainer mWindowContainer;
|
||||
private final boolean mScheduleFinishPip;
|
||||
private final SurfaceControl mLeash;
|
||||
private final @AnimationType int mAnimationType;
|
||||
@@ -149,23 +145,18 @@ public class PipAnimationController {
|
||||
private PipAnimationCallback mPipAnimationCallback;
|
||||
private SurfaceControlTransactionFactory mSurfaceControlTransactionFactory;
|
||||
|
||||
private PipTransitionAnimator(IWindowContainer wc, boolean scheduleFinishPip,
|
||||
private PipTransitionAnimator(SurfaceControl leash, boolean scheduleFinishPip,
|
||||
@AnimationType int animationType, Rect destinationBounds,
|
||||
T startValue, T endValue) {
|
||||
mWindowContainer = wc;
|
||||
mScheduleFinishPip = scheduleFinishPip;
|
||||
try {
|
||||
mLeash = wc.getLeash();
|
||||
mAnimationType = animationType;
|
||||
mDestinationBounds.set(destinationBounds);
|
||||
mStartValue = startValue;
|
||||
mEndValue = endValue;
|
||||
addListener(this);
|
||||
addUpdateListener(this);
|
||||
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
mLeash = leash;
|
||||
mAnimationType = animationType;
|
||||
mDestinationBounds.set(destinationBounds);
|
||||
mStartValue = startValue;
|
||||
mEndValue = endValue;
|
||||
addListener(this);
|
||||
addUpdateListener(this);
|
||||
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -173,7 +164,7 @@ public class PipAnimationController {
|
||||
mCurrentValue = mStartValue;
|
||||
applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(), FRACTION_START);
|
||||
if (mPipAnimationCallback != null) {
|
||||
mPipAnimationCallback.onPipAnimationStart(mWindowContainer, this);
|
||||
mPipAnimationCallback.onPipAnimationStart(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,14 +180,14 @@ public class PipAnimationController {
|
||||
final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
|
||||
applySurfaceControlTransaction(mLeash, tx, FRACTION_END);
|
||||
if (mPipAnimationCallback != null) {
|
||||
mPipAnimationCallback.onPipAnimationEnd(mWindowContainer, tx, this);
|
||||
mPipAnimationCallback.onPipAnimationEnd(tx, this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
if (mPipAnimationCallback != null) {
|
||||
mPipAnimationCallback.onPipAnimationCancel(mWindowContainer, this);
|
||||
mPipAnimationCallback.onPipAnimationCancel(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -260,9 +251,9 @@ public class PipAnimationController {
|
||||
abstract void applySurfaceControlTransaction(SurfaceControl leash,
|
||||
SurfaceControl.Transaction tx, float fraction);
|
||||
|
||||
static PipTransitionAnimator<Float> ofAlpha(IWindowContainer wc, boolean scheduleFinishPip,
|
||||
static PipTransitionAnimator<Float> ofAlpha(SurfaceControl leash, boolean scheduleFinishPip,
|
||||
Rect destinationBounds, float startValue, float endValue) {
|
||||
return new PipTransitionAnimator<Float>(wc, scheduleFinishPip, ANIM_TYPE_ALPHA,
|
||||
return new PipTransitionAnimator<Float>(leash, scheduleFinishPip, ANIM_TYPE_ALPHA,
|
||||
destinationBounds, startValue, endValue) {
|
||||
@Override
|
||||
void applySurfaceControlTransaction(SurfaceControl leash,
|
||||
@@ -281,10 +272,10 @@ public class PipAnimationController {
|
||||
};
|
||||
}
|
||||
|
||||
static PipTransitionAnimator<Rect> ofBounds(IWindowContainer wc, boolean scheduleFinishPip,
|
||||
static PipTransitionAnimator<Rect> ofBounds(SurfaceControl leash, boolean scheduleFinishPip,
|
||||
Rect startValue, Rect endValue) {
|
||||
// construct new Rect instances in case they are recycled
|
||||
return new PipTransitionAnimator<Rect>(wc, scheduleFinishPip, ANIM_TYPE_BOUNDS,
|
||||
return new PipTransitionAnimator<Rect>(leash, scheduleFinishPip, ANIM_TYPE_BOUNDS,
|
||||
endValue, new Rect(startValue), new Rect(endValue)) {
|
||||
private final Rect mTmpRect = new Rect();
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.DisplayInfo;
|
||||
@@ -38,9 +39,13 @@ import android.view.IWindowContainer;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowContainerTransaction;
|
||||
|
||||
import com.android.internal.os.SomeArgs;
|
||||
import com.android.systemui.pip.phone.PipUpdateThread;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Manages PiP tasks such as resize and offset.
|
||||
@@ -56,7 +61,13 @@ import java.util.Objects;
|
||||
public class PipTaskOrganizer extends ITaskOrganizer.Stub {
|
||||
private static final String TAG = PipTaskOrganizer.class.getSimpleName();
|
||||
|
||||
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 final Handler mMainHandler;
|
||||
private final Handler mUpdateHandler;
|
||||
private final ITaskOrganizerController mTaskOrganizerController;
|
||||
private final PipBoundsHandler mPipBoundsHandler;
|
||||
private final PipAnimationController mPipAnimationController;
|
||||
@@ -64,11 +75,11 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
|
||||
private final Rect mDisplayBounds = new Rect();
|
||||
private final Rect mLastReportedBounds = new Rect();
|
||||
|
||||
// These callbacks are called on the update thread
|
||||
private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
|
||||
new PipAnimationController.PipAnimationCallback() {
|
||||
@Override
|
||||
public void onPipAnimationStart(IWindowContainer wc,
|
||||
PipAnimationController.PipTransitionAnimator animator) {
|
||||
public void onPipAnimationStart(PipAnimationController.PipTransitionAnimator animator) {
|
||||
mMainHandler.post(() -> {
|
||||
for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
|
||||
final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
|
||||
@@ -78,7 +89,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPipAnimationEnd(IWindowContainer wc, SurfaceControl.Transaction tx,
|
||||
public void onPipAnimationEnd(SurfaceControl.Transaction tx,
|
||||
PipAnimationController.PipTransitionAnimator animator) {
|
||||
mMainHandler.post(() -> {
|
||||
for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
|
||||
@@ -86,13 +97,11 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
|
||||
callback.onPipTransitionFinished();
|
||||
}
|
||||
});
|
||||
final Rect destinationBounds = animator.getDestinationBounds();
|
||||
finishResizeInternal(destinationBounds, wc, tx, animator.shouldScheduleFinishPip());
|
||||
finishResize(animator.getDestinationBounds(), tx, animator.shouldScheduleFinishPip());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPipAnimationCancel(IWindowContainer wc,
|
||||
PipAnimationController.PipTransitionAnimator animator) {
|
||||
public void onPipAnimationCancel(PipAnimationController.PipTransitionAnimator animator) {
|
||||
mMainHandler.post(() -> {
|
||||
for (int i = mPipTransitionCallbacks.size() - 1; i >= 0; i--) {
|
||||
final PipTransitionCallback callback = mPipTransitionCallbacks.get(i);
|
||||
@@ -102,28 +111,75 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
|
||||
}
|
||||
};
|
||||
|
||||
private Handler.Callback mUpdateCallbacks = new Handler.Callback() {
|
||||
@Override
|
||||
public boolean handleMessage(Message 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;
|
||||
boolean scheduleFinishPip = args.argi1 != 0;
|
||||
int duration = args.argi2;
|
||||
animateResizePip(scheduleFinishPip, currentBounds, toBounds, duration);
|
||||
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;
|
||||
boolean scheduleFinishPip = args.argi1 != 0;
|
||||
finishResize(toBounds, tx, scheduleFinishPip);
|
||||
if (updateBoundsCallback != null) {
|
||||
updateBoundsCallback.accept(toBounds);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
args.recycle();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
private ActivityManager.RunningTaskInfo mTaskInfo;
|
||||
private IWindowContainer mToken;
|
||||
private SurfaceControl mLeash;
|
||||
private boolean mInPip;
|
||||
private @PipAnimationController.AnimationType int mOneShotAnimationType = ANIM_TYPE_BOUNDS;
|
||||
|
||||
public PipTaskOrganizer(Context context, @NonNull PipBoundsHandler boundsHandler) {
|
||||
mMainHandler = new Handler(Looper.getMainLooper());
|
||||
mUpdateHandler = new Handler(PipUpdateThread.get().getLooper(), mUpdateCallbacks);
|
||||
mTaskOrganizerController = ActivityTaskManager.getTaskOrganizerController();
|
||||
mPipBoundsHandler = boundsHandler;
|
||||
mPipAnimationController = new PipAnimationController(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset the PiP window, animate if the given duration is not {@link #DURATION_NONE}
|
||||
*/
|
||||
public void offsetPinnedStack(Rect originalBounds, int xOffset, int yOffset, int durationMs) {
|
||||
if (mTaskInfo == null) {
|
||||
Log.w(TAG, "mTaskInfo is not set");
|
||||
return;
|
||||
}
|
||||
final Rect destinationBounds = new Rect(originalBounds);
|
||||
destinationBounds.offset(xOffset, yOffset);
|
||||
animateResizePipInternal(mTaskInfo.token, false /* scheduleFinishPip*/,
|
||||
originalBounds, destinationBounds, durationMs);
|
||||
public Handler getUpdateHandler() {
|
||||
return mUpdateHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -171,7 +227,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
|
||||
try {
|
||||
mLastReportedBounds.set(destinationBounds);
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
wct.setBounds(mTaskInfo.token, destinationBounds);
|
||||
wct.setBounds(mToken, destinationBounds);
|
||||
mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed to apply window container transaction", e);
|
||||
@@ -185,13 +241,20 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
|
||||
getAspectRatioOrDefault(info.pictureInPictureParams), null /* bounds */);
|
||||
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
|
||||
mTaskInfo = info;
|
||||
mToken = mTaskInfo.token;
|
||||
mInPip = true;
|
||||
try {
|
||||
mLeash = mToken.getLeash();
|
||||
} catch (RemoteException e) {
|
||||
throw new RuntimeException("Unable to get leash", e);
|
||||
}
|
||||
if (mOneShotAnimationType == ANIM_TYPE_BOUNDS) {
|
||||
final Rect currentBounds = mTaskInfo.configuration.windowConfiguration.getBounds();
|
||||
animateResizePipInternal(mTaskInfo.token, true /* scheduleFinishPip */,
|
||||
currentBounds, destinationBounds, DURATION_DEFAULT_MS);
|
||||
scheduleAnimateResizePip(true /* scheduleFinishPip */,
|
||||
currentBounds, destinationBounds, DURATION_DEFAULT_MS, null);
|
||||
} else if (mOneShotAnimationType == ANIM_TYPE_ALPHA) {
|
||||
mMainHandler.post(() -> mPipAnimationController
|
||||
.getAnimator(mTaskInfo.token, true /* scheduleFinishPip */,
|
||||
mUpdateHandler.post(() -> mPipAnimationController
|
||||
.getAnimator(mLeash, true /* scheduleFinishPip */,
|
||||
destinationBounds, 0f, 1f)
|
||||
.setPipAnimationCallback(mPipAnimationCallback)
|
||||
.setDuration(DURATION_DEFAULT_MS)
|
||||
@@ -205,12 +268,12 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
|
||||
@Override
|
||||
public void taskVanished(IWindowContainer token) {
|
||||
Objects.requireNonNull(token, "Requires valid IWindowContainer");
|
||||
if (token.asBinder() != mTaskInfo.token.asBinder()) {
|
||||
if (token.asBinder() != mToken.asBinder()) {
|
||||
Log.wtf(TAG, "Unrecognized token: " + token);
|
||||
return;
|
||||
}
|
||||
animateResizePipInternal(token, false /* scheduleFinishPip */,
|
||||
mLastReportedBounds, mDisplayBounds, DURATION_DEFAULT_MS);
|
||||
scheduleAnimateResizePip(mDisplayBounds, DURATION_DEFAULT_MS, null);
|
||||
mInPip = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -227,7 +290,7 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
|
||||
final Rect destinationBounds = mPipBoundsHandler.getDestinationBounds(
|
||||
getAspectRatioOrDefault(newParams), null /* bounds */);
|
||||
Objects.requireNonNull(destinationBounds, "Missing destination bounds");
|
||||
animateResizePip(destinationBounds, DURATION_DEFAULT_MS);
|
||||
scheduleAnimateResizePip(destinationBounds, DURATION_DEFAULT_MS, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -243,102 +306,158 @@ public class PipTaskOrganizer extends ITaskOrganizer.Stub {
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly perform manipulation/resize on the leash. This will not perform any
|
||||
* {@link WindowContainerTransaction} until {@link #finishResize} is called.
|
||||
* Animates resizing of the pinned stack given the duration.
|
||||
*/
|
||||
public void resizePip(Rect destinationBounds) {
|
||||
Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer");
|
||||
resizePipInternal(mTaskInfo.token, destinationBounds);
|
||||
public void scheduleAnimateResizePip(Rect toBounds, int duration,
|
||||
Consumer<Rect> updateBoundsCallback) {
|
||||
scheduleAnimateResizePip(false /* scheduleFinishPip */,
|
||||
mLastReportedBounds, toBounds, duration, updateBoundsCallback);
|
||||
}
|
||||
|
||||
private void resizePipInternal(IWindowContainer wc,
|
||||
Rect destinationBounds) {
|
||||
Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer");
|
||||
try {
|
||||
// Could happen when dismissPip
|
||||
if (wc == null || wc.getLeash() == null) {
|
||||
Log.w(TAG, "Abort animation, invalid leash");
|
||||
return;
|
||||
}
|
||||
final SurfaceControl leash = wc.getLeash();
|
||||
new SurfaceControl.Transaction()
|
||||
.setPosition(leash, destinationBounds.left, destinationBounds.top)
|
||||
.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height())
|
||||
.apply();
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Abort animation, invalid window container", e);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Should not reach here, terrible thing happened", e);
|
||||
private void scheduleAnimateResizePip(boolean scheduleFinishPip,
|
||||
Rect currentBounds, Rect destinationBounds, int durationMs,
|
||||
Consumer<Rect> updateBoundsCallback) {
|
||||
Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
|
||||
if (!mInPip) {
|
||||
// Ignore animation when we are no longer in PIP
|
||||
return;
|
||||
}
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = updateBoundsCallback;
|
||||
args.arg2 = currentBounds;
|
||||
args.arg3 = destinationBounds;
|
||||
args.argi1 = scheduleFinishPip ? 1 : 0;
|
||||
args.argi2 = durationMs;
|
||||
mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_ANIMATE, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Directly perform manipulation/resize on the leash. This will not perform any
|
||||
* {@link WindowContainerTransaction} until {@link #scheduleFinishResizePip} is called.
|
||||
*/
|
||||
public void scheduleResizePip(Rect toBounds, Consumer<Rect> updateBoundsCallback) {
|
||||
Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = updateBoundsCallback;
|
||||
args.arg2 = toBounds;
|
||||
mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_RESIZE_IMMEDIATE, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finish a intermediate resize operation. This is expected to be called after
|
||||
* {@link #resizePip}.
|
||||
* {@link #scheduleResizePip}.
|
||||
*/
|
||||
public void finishResize(Rect destinationBounds) {
|
||||
try {
|
||||
final IWindowContainer wc = mTaskInfo.token;
|
||||
SurfaceControl.Transaction tx = new SurfaceControl.Transaction()
|
||||
.setPosition(wc.getLeash(), destinationBounds.left,
|
||||
destinationBounds.top)
|
||||
.setWindowCrop(wc.getLeash(), destinationBounds.width(),
|
||||
destinationBounds.height());
|
||||
finishResizeInternal(destinationBounds, wc, tx, false);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to obtain leash");
|
||||
}
|
||||
public void scheduleFinishResizePip(Rect destinationBounds) {
|
||||
Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
|
||||
SurfaceControl.Transaction tx = new SurfaceControl.Transaction()
|
||||
.setPosition(mLeash, destinationBounds.left, destinationBounds.top)
|
||||
.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height());
|
||||
scheduleFinishResizePip(tx, destinationBounds, false /* scheduleFinishPip */,
|
||||
null);
|
||||
}
|
||||
|
||||
private void finishResizeInternal(Rect destinationBounds, IWindowContainer wc,
|
||||
SurfaceControl.Transaction tx, boolean shouldScheduleFinishPip) {
|
||||
private void scheduleFinishResizePip(SurfaceControl.Transaction tx,
|
||||
Rect destinationBounds, boolean scheduleFinishPip,
|
||||
Consumer<Rect> updateBoundsCallback) {
|
||||
Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = updateBoundsCallback;
|
||||
args.arg2 = tx;
|
||||
args.arg3 = destinationBounds;
|
||||
args.argi1 = scheduleFinishPip ? 1 : 0;
|
||||
mUpdateHandler.sendMessage(mUpdateHandler.obtainMessage(MSG_FINISH_RESIZE, args));
|
||||
}
|
||||
|
||||
/**
|
||||
* Offset the PiP window, animate if the given duration is not {@link #DURATION_NONE}
|
||||
*/
|
||||
public void scheduleOffsetPip(Rect originalBounds, int offset, int duration,
|
||||
Consumer<Rect> updateBoundsCallback) {
|
||||
if (!mInPip) {
|
||||
// Ignore offsets when we are no longer in PIP
|
||||
return;
|
||||
}
|
||||
Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
|
||||
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));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
final Rect destinationBounds = new Rect(originalBounds);
|
||||
destinationBounds.offset(xOffset, yOffset);
|
||||
animateResizePip(false /* scheduleFinishPip*/, originalBounds, destinationBounds,
|
||||
durationMs);
|
||||
}
|
||||
|
||||
private void resizePip(Rect destinationBounds) {
|
||||
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
|
||||
throw new RuntimeException("Callers should call scheduleResizePip() instead of this "
|
||||
+ "directly");
|
||||
}
|
||||
Objects.requireNonNull(mToken, "Requires valid IWindowContainer");
|
||||
// Could happen when dismissPip
|
||||
if (mToken == null || mLeash == null) {
|
||||
Log.w(TAG, "Abort animation, invalid leash");
|
||||
return;
|
||||
}
|
||||
new SurfaceControl.Transaction()
|
||||
.setPosition(mLeash, destinationBounds.left, destinationBounds.top)
|
||||
.setWindowCrop(mLeash, destinationBounds.width(), destinationBounds.height())
|
||||
.apply();
|
||||
}
|
||||
|
||||
private void finishResize(Rect destinationBounds, SurfaceControl.Transaction tx,
|
||||
boolean shouldScheduleFinishPip) {
|
||||
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
|
||||
throw new RuntimeException("Callers should call scheduleResizePip() instead of this "
|
||||
+ "directly");
|
||||
}
|
||||
mLastReportedBounds.set(destinationBounds);
|
||||
try {
|
||||
final WindowContainerTransaction wct = new WindowContainerTransaction();
|
||||
if (shouldScheduleFinishPip) {
|
||||
wct.scheduleFinishEnterPip(wc, destinationBounds);
|
||||
wct.scheduleFinishEnterPip(mToken, destinationBounds);
|
||||
} else {
|
||||
wct.setBounds(wc, destinationBounds);
|
||||
wct.setBounds(mToken, destinationBounds);
|
||||
}
|
||||
wct.setBoundsChangeTransaction(mTaskInfo.token, tx);
|
||||
wct.setBoundsChangeTransaction(mToken, tx);
|
||||
mTaskOrganizerController.applyContainerTransaction(wct, null /* ITaskOrganizer */);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to apply container transaction", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Animates resizing of the pinned stack given the duration.
|
||||
*/
|
||||
public void animateResizePip(Rect destinationBounds, int durationMs) {
|
||||
Objects.requireNonNull(mTaskInfo, "Requires valid IWindowContainer");
|
||||
animateResizePipInternal(mTaskInfo.token, false, mLastReportedBounds,
|
||||
destinationBounds, durationMs);
|
||||
}
|
||||
|
||||
private void animateResizePipInternal(IWindowContainer wc, boolean scheduleFinishPip,
|
||||
Rect currentBounds, Rect destinationBounds, int durationMs) {
|
||||
try {
|
||||
// Could happen when dismissPip
|
||||
if (wc == null || wc.getLeash() == null) {
|
||||
Log.w(TAG, "Abort animation, invalid leash");
|
||||
return;
|
||||
}
|
||||
final SurfaceControl leash = wc.getLeash();
|
||||
|
||||
mMainHandler.post(() -> mPipAnimationController
|
||||
.getAnimator(wc, scheduleFinishPip, currentBounds, destinationBounds)
|
||||
.setPipAnimationCallback(mPipAnimationCallback)
|
||||
.setDuration(durationMs)
|
||||
.start());
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Abort animation, invalid window container", e);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Should not reach here, terrible thing happened", e);
|
||||
private void animateResizePip(boolean scheduleFinishPip, Rect currentBounds,
|
||||
Rect destinationBounds, int durationMs) {
|
||||
if (Looper.myLooper() != mUpdateHandler.getLooper()) {
|
||||
throw new RuntimeException("Callers should call scheduleAnimateResizePip() instead of "
|
||||
+ "this directly");
|
||||
}
|
||||
// Could happen when dismissPip
|
||||
if (mToken == null || mLeash == null) {
|
||||
Log.w(TAG, "Abort animation, invalid leash");
|
||||
return;
|
||||
}
|
||||
mUpdateHandler.post(() -> mPipAnimationController
|
||||
.getAnimator(mLeash, scheduleFinishPip, currentBounds, destinationBounds)
|
||||
.setPipAnimationCallback(mPipAnimationCallback)
|
||||
.setDuration(durationMs)
|
||||
.start());
|
||||
}
|
||||
|
||||
|
||||
private float getAspectRatioOrDefault(@Nullable PictureInPictureParams params) {
|
||||
return params == null
|
||||
? mPipBoundsHandler.getDefaultAspectRatio()
|
||||
|
||||
@@ -28,16 +28,11 @@ import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Debug;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Log;
|
||||
import android.view.Choreographer;
|
||||
|
||||
import androidx.dynamicanimation.animation.SpringForce;
|
||||
|
||||
import com.android.internal.graphics.SfVsyncFrameCallbackProvider;
|
||||
import com.android.internal.os.SomeArgs;
|
||||
import com.android.systemui.pip.PipSnapAlgorithm;
|
||||
import com.android.systemui.pip.PipTaskOrganizer;
|
||||
import com.android.systemui.shared.system.WindowManagerWrapper;
|
||||
@@ -47,11 +42,12 @@ import com.android.systemui.util.animation.FloatProperties;
|
||||
import com.android.systemui.util.animation.PhysicsAnimator;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A helper to animate and manipulate the PiP.
|
||||
*/
|
||||
public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Callback,
|
||||
public class PipMotionHelper implements PipAppOpsListener.Callback,
|
||||
FloatingContentCoordinator.FloatingContent {
|
||||
|
||||
private static final String TAG = "PipMotionHelper";
|
||||
@@ -68,14 +64,9 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
// The fraction of the stack height that the user has to drag offscreen to dismiss the PiP
|
||||
private static final float DISMISS_OFFSCREEN_FRACTION = 0.3f;
|
||||
|
||||
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 final Context mContext;
|
||||
private final IActivityTaskManager mActivityTaskManager;
|
||||
private final PipTaskOrganizer mPipTaskOrganizer;
|
||||
private final Handler mHandler;
|
||||
|
||||
private PipMenuActivityController mMenuController;
|
||||
private PipSnapAlgorithm mSnapAlgorithm;
|
||||
@@ -92,9 +83,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
/** The region that all of PIP must stay within. */
|
||||
private Rect mFloatingAllowedArea = new Rect();
|
||||
|
||||
private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider =
|
||||
new SfVsyncFrameCallbackProvider();
|
||||
|
||||
/**
|
||||
* Bounds that are animated using the physics animator.
|
||||
*/
|
||||
@@ -112,16 +100,11 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
private PhysicsAnimator<Rect> mAnimatedBoundsPhysicsAnimator = PhysicsAnimator.getInstance(
|
||||
mAnimatedBounds);
|
||||
|
||||
/** Callback that re-sizes PIP to the animated bounds. */
|
||||
private final Choreographer.FrameCallback mResizePipVsyncCallback =
|
||||
l -> resizePipUnchecked(mAnimatedBounds);
|
||||
|
||||
/**
|
||||
* Update listener that posts a vsync frame callback to resize PIP to {@link #mAnimatedBounds}.
|
||||
* Update listener that resizes the PIP to {@link #mAnimatedBounds}.
|
||||
*/
|
||||
private final PhysicsAnimator.UpdateListener<Rect> mResizePipVsyncUpdateListener =
|
||||
(target, values) ->
|
||||
mSfVsyncFrameProvider.postFrameCallback(mResizePipVsyncCallback);
|
||||
private final PhysicsAnimator.UpdateListener<Rect> mResizePipUpdateListener =
|
||||
(target, values) -> resizePipUnchecked(mAnimatedBounds);
|
||||
|
||||
/** FlingConfig instances provided to PhysicsAnimator for fling gestures. */
|
||||
private PhysicsAnimator.FlingConfig mFlingConfigX;
|
||||
@@ -137,12 +120,13 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
new PhysicsAnimator.SpringConfig(
|
||||
SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY);
|
||||
|
||||
private final Consumer<Rect> mUpdateBoundsCallback = (toBounds) -> mBounds.set(toBounds);
|
||||
|
||||
public PipMotionHelper(Context context, IActivityTaskManager activityTaskManager,
|
||||
PipTaskOrganizer pipTaskOrganizer, PipMenuActivityController menuController,
|
||||
PipSnapAlgorithm snapAlgorithm, FlingAnimationUtils flingAnimationUtils,
|
||||
FloatingContentCoordinator floatingContentCoordinator) {
|
||||
mContext = context;
|
||||
mHandler = new Handler(ForegroundThread.get().getLooper(), this);
|
||||
mActivityTaskManager = activityTaskManager;
|
||||
mPipTaskOrganizer = pipTaskOrganizer;
|
||||
mMenuController = menuController;
|
||||
@@ -234,7 +218,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
}
|
||||
cancelAnimations();
|
||||
mMenuController.hideMenuWithoutResize();
|
||||
mHandler.post(() -> {
|
||||
mPipTaskOrganizer.getUpdateHandler().post(() -> {
|
||||
try {
|
||||
mActivityTaskManager.dismissPip(!skipAnimation, EXPAND_STACK_TO_FULLSCREEN_DURATION);
|
||||
} catch (RemoteException e) {
|
||||
@@ -253,7 +237,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
}
|
||||
cancelAnimations();
|
||||
mMenuController.hideMenuWithoutResize();
|
||||
mHandler.post(() -> {
|
||||
mPipTaskOrganizer.getUpdateHandler().post(() -> {
|
||||
try {
|
||||
mActivityTaskManager.removeStacksInWindowingModes(
|
||||
new int[]{ WINDOWING_MODE_PINNED });
|
||||
@@ -406,17 +390,13 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
* Animates the PiP to offset it from the IME or shelf.
|
||||
*/
|
||||
void animateToOffset(Rect originalBounds, int offset) {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "animateToOffset: originalBounds=" + originalBounds + " offset=" + offset
|
||||
+ " callers=\n" + Debug.getCallers(5, " "));
|
||||
}
|
||||
cancelAnimations();
|
||||
adjustAndAnimatePipOffset(originalBounds, offset, SHIFT_DURATION);
|
||||
}
|
||||
|
||||
private void adjustAndAnimatePipOffset(Rect originalBounds, int offset, int duration) {
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = originalBounds;
|
||||
// offset would be zero if triggered from screen rotation.
|
||||
args.argi1 = offset;
|
||||
args.argi2 = duration;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_OFFSET_ANIMATE, args));
|
||||
mPipTaskOrganizer.scheduleOffsetPip(originalBounds, offset, SHIFT_DURATION,
|
||||
mUpdateBoundsCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -437,8 +417,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
|
||||
/**
|
||||
* Starts the physics animator which will update the animated PIP bounds using physics
|
||||
* animations, as well as the TimeAnimator which will apply those bounds to PIP at intervals
|
||||
* synchronized with the SurfaceFlinger vsync frame provider.
|
||||
* animations, as well as the TimeAnimator which will apply those bounds to PIP.
|
||||
*
|
||||
* This will also add end actions to the bounds animator that cancel the TimeAnimator and update
|
||||
* the 'real' bounds to equal the final animated bounds.
|
||||
@@ -448,7 +427,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
|
||||
mAnimatedBoundsPhysicsAnimator
|
||||
.withEndActions(() -> mPipTaskOrganizer.onMotionMovementEnd(mAnimatedBounds))
|
||||
.addUpdateListener(mResizePipVsyncUpdateListener)
|
||||
.addUpdateListener(mResizePipUpdateListener)
|
||||
.start();
|
||||
}
|
||||
|
||||
@@ -471,9 +450,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
+ " callers=\n" + Debug.getCallers(5, " "));
|
||||
}
|
||||
if (!toBounds.equals(mBounds)) {
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = toBounds;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_RESIZE_IMMEDIATE, args));
|
||||
mPipTaskOrganizer.scheduleResizePip(toBounds, mUpdateBoundsCallback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,10 +463,7 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
+ " duration=" + duration + " callers=\n" + Debug.getCallers(5, " "));
|
||||
}
|
||||
if (!toBounds.equals(mBounds)) {
|
||||
SomeArgs args = SomeArgs.obtain();
|
||||
args.arg1 = toBounds;
|
||||
args.argi1 = duration;
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_RESIZE_ANIMATE, args));
|
||||
mPipTaskOrganizer.scheduleAnimateResizePip(toBounds, duration, mUpdateBoundsCallback);
|
||||
setAnimatingToBounds(toBounds);
|
||||
}
|
||||
}
|
||||
@@ -538,70 +512,6 @@ public class PipMotionHelper implements Handler.Callback, PipAppOpsListener.Call
|
||||
return dismissArea.contains(endpoint.x, endpoint.y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles messages to be processed on the background thread.
|
||||
*/
|
||||
public boolean handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_RESIZE_IMMEDIATE: {
|
||||
SomeArgs args = (SomeArgs) msg.obj;
|
||||
Rect toBounds = (Rect) args.arg1;
|
||||
mPipTaskOrganizer.resizePip(toBounds);
|
||||
mBounds.set(toBounds);
|
||||
return true;
|
||||
}
|
||||
|
||||
case MSG_RESIZE_ANIMATE: {
|
||||
SomeArgs args = (SomeArgs) msg.obj;
|
||||
Rect toBounds = (Rect) args.arg1;
|
||||
int duration = args.argi1;
|
||||
try {
|
||||
StackInfo stackInfo = mActivityTaskManager.getStackInfo(
|
||||
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
|
||||
if (stackInfo == null) {
|
||||
// In the case where we've already re-expanded or dismissed the PiP, then
|
||||
// just skip the resize
|
||||
return true;
|
||||
}
|
||||
|
||||
mPipTaskOrganizer.animateResizePip(toBounds, duration);
|
||||
mBounds.set(toBounds);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Could not animate resize pinned stack to bounds: " + toBounds, e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
case MSG_OFFSET_ANIMATE: {
|
||||
SomeArgs args = (SomeArgs) msg.obj;
|
||||
Rect originalBounds = (Rect) args.arg1;
|
||||
final int offset = args.argi1;
|
||||
final int duration = args.argi2;
|
||||
try {
|
||||
StackInfo stackInfo = mActivityTaskManager.getStackInfo(
|
||||
WINDOWING_MODE_PINNED, ACTIVITY_TYPE_UNDEFINED);
|
||||
if (stackInfo == null) {
|
||||
// In the case where we've already re-expanded or dismissed the PiP, then
|
||||
// just skip the resize
|
||||
return true;
|
||||
}
|
||||
|
||||
mPipTaskOrganizer.offsetPinnedStack(originalBounds,
|
||||
0 /* xOffset */, offset, duration);
|
||||
Rect toBounds = new Rect(originalBounds);
|
||||
toBounds.offset(0, offset);
|
||||
mBounds.set(toBounds);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Could not animate offset pinned stack with offset: " + offset, e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void dump(PrintWriter pw, String prefix) {
|
||||
final String innerPrefix = prefix + " ";
|
||||
pw.println(prefix + TAG);
|
||||
|
||||
@@ -21,33 +21,38 @@ import android.os.HandlerThread;
|
||||
|
||||
/**
|
||||
* Similar to {@link com.android.internal.os.BackgroundThread}, this is a shared singleton
|
||||
* foreground thread for each process.
|
||||
* foreground thread for each process for updating PIP.
|
||||
*/
|
||||
public final class ForegroundThread extends HandlerThread {
|
||||
private static ForegroundThread sInstance;
|
||||
public final class PipUpdateThread extends HandlerThread {
|
||||
private static PipUpdateThread sInstance;
|
||||
private static Handler sHandler;
|
||||
|
||||
private ForegroundThread() {
|
||||
super("recents.fg");
|
||||
private PipUpdateThread() {
|
||||
super("pip");
|
||||
}
|
||||
|
||||
private static void ensureThreadLocked() {
|
||||
if (sInstance == null) {
|
||||
sInstance = new ForegroundThread();
|
||||
sInstance = new PipUpdateThread();
|
||||
sInstance.start();
|
||||
sHandler = new Handler(sInstance.getLooper());
|
||||
}
|
||||
}
|
||||
|
||||
public static ForegroundThread get() {
|
||||
synchronized (ForegroundThread.class) {
|
||||
/**
|
||||
* @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 (ForegroundThread.class) {
|
||||
synchronized (PipUpdateThread.class) {
|
||||
ensureThreadLocked();
|
||||
return sHandler;
|
||||
}
|
||||
@@ -20,6 +20,8 @@ import static android.app.ActivityTaskManager.INVALID_STACK_ID;
|
||||
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
|
||||
|
||||
import static com.android.systemui.pip.PipAnimationController.DURATION_DEFAULT_MS;
|
||||
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.app.ActivityManager.StackInfo;
|
||||
import android.app.ActivityTaskManager;
|
||||
@@ -50,7 +52,6 @@ import com.android.systemui.R;
|
||||
import com.android.systemui.UiOffloadThread;
|
||||
import com.android.systemui.broadcast.BroadcastDispatcher;
|
||||
import com.android.systemui.pip.BasePipManager;
|
||||
import com.android.systemui.pip.PipAnimationController;
|
||||
import com.android.systemui.pip.PipBoundsHandler;
|
||||
import com.android.systemui.pip.PipTaskOrganizer;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
@@ -233,6 +234,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
|
||||
if (mInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
mInitialized = true;
|
||||
mContext = context;
|
||||
mPipBoundsHandler = pipBoundsHandler;
|
||||
@@ -434,8 +436,7 @@ public class PipManager implements BasePipManager, PipTaskOrganizer.PipTransitio
|
||||
mCurrentPipBounds = mPipBounds;
|
||||
break;
|
||||
}
|
||||
mPipTaskOrganizer.animateResizePip(mCurrentPipBounds,
|
||||
PipAnimationController.DURATION_DEFAULT_MS);
|
||||
mPipTaskOrganizer.scheduleAnimateResizePip(mCurrentPipBounds, DURATION_DEFAULT_MS, null);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,7 +26,6 @@ import static org.mockito.Mockito.verify;
|
||||
import android.graphics.Rect;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.IWindowContainer;
|
||||
import android.view.SurfaceControl;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
@@ -51,7 +50,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
|
||||
private PipAnimationController mPipAnimationController;
|
||||
|
||||
@Mock
|
||||
private IWindowContainer mWindowContainer;
|
||||
private SurfaceControl mLeash;
|
||||
|
||||
@Mock
|
||||
private PipAnimationController.PipAnimationCallback mPipAnimationCallback;
|
||||
@@ -65,8 +64,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
|
||||
@Test
|
||||
public void getAnimator_withAlpha_returnFloatAnimator() {
|
||||
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
|
||||
.getAnimator(mWindowContainer, true /* scheduleFinishPip */,
|
||||
new Rect(), 0f, 1f);
|
||||
.getAnimator(mLeash, true /* scheduleFinishPip */, new Rect(), 0f, 1f);
|
||||
|
||||
assertEquals("Expect ANIM_TYPE_ALPHA animation",
|
||||
animator.getAnimationType(), PipAnimationController.ANIM_TYPE_ALPHA);
|
||||
@@ -75,8 +73,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
|
||||
@Test
|
||||
public void getAnimator_withBounds_returnBoundsAnimator() {
|
||||
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
|
||||
.getAnimator(mWindowContainer, true /* scheduleFinishPip */,
|
||||
new Rect(), new Rect());
|
||||
.getAnimator(mLeash, true /* scheduleFinishPip */, new Rect(), new Rect());
|
||||
|
||||
assertEquals("Expect ANIM_TYPE_BOUNDS animation",
|
||||
animator.getAnimationType(), PipAnimationController.ANIM_TYPE_BOUNDS);
|
||||
@@ -88,14 +85,12 @@ public class PipAnimationControllerTest extends SysuiTestCase {
|
||||
final Rect endValue1 = new Rect(100, 100, 200, 200);
|
||||
final Rect endValue2 = new Rect(200, 200, 300, 300);
|
||||
final PipAnimationController.PipTransitionAnimator oldAnimator = mPipAnimationController
|
||||
.getAnimator(mWindowContainer, true /* scheduleFinishPip */,
|
||||
startValue, endValue1);
|
||||
.getAnimator(mLeash, true /* scheduleFinishPip */, startValue, endValue1);
|
||||
oldAnimator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
|
||||
oldAnimator.start();
|
||||
|
||||
final PipAnimationController.PipTransitionAnimator newAnimator = mPipAnimationController
|
||||
.getAnimator(mWindowContainer, true /* scheduleFinishPip */,
|
||||
startValue, endValue2);
|
||||
.getAnimator(mLeash, true /* scheduleFinishPip */, startValue, endValue2);
|
||||
|
||||
assertEquals("getAnimator with same type returns same animator",
|
||||
oldAnimator, newAnimator);
|
||||
@@ -106,13 +101,11 @@ public class PipAnimationControllerTest extends SysuiTestCase {
|
||||
@Test
|
||||
public void getAnimator_scheduleFinishPip() {
|
||||
PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
|
||||
.getAnimator(mWindowContainer, true /* scheduleFinishPip */,
|
||||
new Rect(), 0f, 1f);
|
||||
.getAnimator(mLeash, true /* scheduleFinishPip */, new Rect(), 0f, 1f);
|
||||
assertTrue("scheduleFinishPip is true", animator.shouldScheduleFinishPip());
|
||||
|
||||
animator = mPipAnimationController
|
||||
.getAnimator(mWindowContainer, false /* scheduleFinishPip */,
|
||||
new Rect(), 0f, 1f);
|
||||
.getAnimator(mLeash, false /* scheduleFinishPip */, new Rect(), 0f, 1f);
|
||||
assertFalse("scheduleFinishPip is false", animator.shouldScheduleFinishPip());
|
||||
}
|
||||
|
||||
@@ -122,8 +115,7 @@ public class PipAnimationControllerTest extends SysuiTestCase {
|
||||
final Rect endValue1 = new Rect(100, 100, 200, 200);
|
||||
final Rect endValue2 = new Rect(200, 200, 300, 300);
|
||||
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
|
||||
.getAnimator(mWindowContainer, true /* scheduleFinishPip */,
|
||||
startValue, endValue1);
|
||||
.getAnimator(mLeash, true /* scheduleFinishPip */, startValue, endValue1);
|
||||
|
||||
animator.updateEndValue(endValue2);
|
||||
|
||||
@@ -135,24 +127,23 @@ public class PipAnimationControllerTest extends SysuiTestCase {
|
||||
final Rect startValue = new Rect(0, 0, 100, 100);
|
||||
final Rect endValue = new Rect(100, 100, 200, 200);
|
||||
final PipAnimationController.PipTransitionAnimator animator = mPipAnimationController
|
||||
.getAnimator(mWindowContainer, true /* scheduleFinishPip */,
|
||||
startValue, endValue);
|
||||
.getAnimator(mLeash, true /* scheduleFinishPip */, startValue, endValue);
|
||||
animator.setSurfaceControlTransactionFactory(DummySurfaceControlTx::new);
|
||||
|
||||
animator.setPipAnimationCallback(mPipAnimationCallback);
|
||||
|
||||
// onAnimationStart triggers onPipAnimationStart
|
||||
animator.onAnimationStart(animator);
|
||||
verify(mPipAnimationCallback).onPipAnimationStart(mWindowContainer, animator);
|
||||
verify(mPipAnimationCallback).onPipAnimationStart(animator);
|
||||
|
||||
// onAnimationCancel triggers onPipAnimationCancel
|
||||
animator.onAnimationCancel(animator);
|
||||
verify(mPipAnimationCallback).onPipAnimationCancel(mWindowContainer, animator);
|
||||
verify(mPipAnimationCallback).onPipAnimationCancel(animator);
|
||||
|
||||
// onAnimationEnd triggers onPipAnimationEnd
|
||||
animator.onAnimationEnd(animator);
|
||||
verify(mPipAnimationCallback).onPipAnimationEnd(eq(mWindowContainer),
|
||||
any(SurfaceControl.Transaction.class), eq(animator));
|
||||
verify(mPipAnimationCallback).onPipAnimationEnd(any(SurfaceControl.Transaction.class),
|
||||
eq(animator));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user