fixes more error
This commit is contained in:
@@ -0,0 +1,685 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.splitscreen;
|
||||
|
||||
import static android.view.WindowManager.TRANSIT_CHANGE;
|
||||
import static android.view.WindowManager.TRANSIT_CLOSE;
|
||||
import static android.view.WindowManager.TRANSIT_OPEN;
|
||||
import static android.view.WindowManager.TRANSIT_TO_BACK;
|
||||
|
||||
import static com.android.wm.shell.animation.Interpolators.ALPHA_IN;
|
||||
import static com.android.wm.shell.animation.Interpolators.ALPHA_OUT;
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.FADE_DURATION;
|
||||
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
|
||||
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN;
|
||||
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_TRANSITIONS;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreen.stageTypeToString;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_DRAG_DIVIDER;
|
||||
import static com.android.wm.shell.splitscreen.SplitScreenController.exitReasonToString;
|
||||
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS;
|
||||
import static com.android.wm.shell.transition.Transitions.TRANSIT_SPLIT_DISMISS_SNAP;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.os.IBinder;
|
||||
import android.view.SurfaceControl;
|
||||
import android.view.WindowManager;
|
||||
import android.window.RemoteTransition;
|
||||
import android.window.TransitionInfo;
|
||||
import android.window.WindowContainerToken;
|
||||
import android.window.WindowContainerTransaction;
|
||||
|
||||
import com.android.internal.protolog.common.ProtoLog;
|
||||
import com.android.wm.shell.common.TransactionPool;
|
||||
import com.android.wm.shell.common.split.SplitDecorManager;
|
||||
import com.android.wm.shell.protolog.ShellProtoLogGroup;
|
||||
import com.android.wm.shell.shared.TransitionUtil;
|
||||
import com.android.wm.shell.transition.OneShotRemoteHandler;
|
||||
import com.android.wm.shell.transition.Transitions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/** Manages transition animations for split-screen. */
|
||||
class SplitScreenTransitions {
|
||||
private static final String TAG = "SplitScreenTransitions";
|
||||
|
||||
private final TransactionPool mTransactionPool;
|
||||
private final Transitions mTransitions;
|
||||
private final Runnable mOnFinish;
|
||||
|
||||
DismissSession mPendingDismiss = null;
|
||||
EnterSession mPendingEnter = null;
|
||||
TransitSession mPendingResize = null;
|
||||
TransitSession mPendingRemotePassthrough = null;
|
||||
|
||||
private IBinder mAnimatingTransition = null;
|
||||
private OneShotRemoteHandler mActiveRemoteHandler = null;
|
||||
|
||||
private final Transitions.TransitionFinishCallback mRemoteFinishCB = this::onFinish;
|
||||
|
||||
/** Keeps track of currently running animations */
|
||||
private final ArrayList<Animator> mAnimations = new ArrayList<>();
|
||||
private final StageCoordinator mStageCoordinator;
|
||||
|
||||
private Transitions.TransitionFinishCallback mFinishCallback = null;
|
||||
private SurfaceControl.Transaction mFinishTransaction;
|
||||
private SplitScreen.SplitInvocationListener mSplitInvocationListener;
|
||||
private Executor mSplitInvocationListenerExecutor;
|
||||
|
||||
SplitScreenTransitions(@NonNull TransactionPool pool, @NonNull Transitions transitions,
|
||||
@NonNull Runnable onFinishCallback, StageCoordinator stageCoordinator) {
|
||||
mTransactionPool = pool;
|
||||
mTransitions = transitions;
|
||||
mOnFinish = onFinishCallback;
|
||||
mStageCoordinator = stageCoordinator;
|
||||
}
|
||||
|
||||
private void initTransition(@NonNull IBinder transition,
|
||||
@NonNull SurfaceControl.Transaction finishTransaction,
|
||||
@NonNull Transitions.TransitionFinishCallback finishCallback) {
|
||||
mAnimatingTransition = transition;
|
||||
mFinishTransaction = finishTransaction;
|
||||
mFinishCallback = finishCallback;
|
||||
}
|
||||
|
||||
/** Play animation for enter transition or dismiss transition. */
|
||||
void playAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||
@NonNull SurfaceControl.Transaction startTransaction,
|
||||
@NonNull SurfaceControl.Transaction finishTransaction,
|
||||
@NonNull Transitions.TransitionFinishCallback finishCallback,
|
||||
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
|
||||
@NonNull WindowContainerToken topRoot) {
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playAnimation: transition=%d", info.getDebugId());
|
||||
initTransition(transition, finishTransaction, finishCallback);
|
||||
|
||||
final TransitSession pendingTransition = getPendingTransition(transition);
|
||||
if (pendingTransition != null) {
|
||||
if (pendingTransition.mCanceled) {
|
||||
// The pending transition was canceled, so skip playing animation.
|
||||
startTransaction.apply();
|
||||
onFinish(null /* wct */);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pendingTransition.mRemoteHandler != null) {
|
||||
pendingTransition.mRemoteHandler.startAnimation(transition, info, startTransaction,
|
||||
finishTransaction, mRemoteFinishCB);
|
||||
mActiveRemoteHandler = pendingTransition.mRemoteHandler;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
playInternalAnimation(transition, info, startTransaction, mainRoot, sideRoot, topRoot);
|
||||
}
|
||||
|
||||
/** Internal function of playAnimation. */
|
||||
private void playInternalAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||
@NonNull SurfaceControl.Transaction t, @NonNull WindowContainerToken mainRoot,
|
||||
@NonNull WindowContainerToken sideRoot, @NonNull WindowContainerToken topRoot) {
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playInternalAnimation: transition=%d",
|
||||
info.getDebugId());
|
||||
// Play some place-holder fade animations
|
||||
final boolean isEnter = isPendingEnter(transition);
|
||||
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||
final SurfaceControl leash = change.getLeash();
|
||||
final int mode = info.getChanges().get(i).getMode();
|
||||
|
||||
final int rootIdx = TransitionUtil.rootIndexFor(change, info);
|
||||
if (mode == TRANSIT_CHANGE) {
|
||||
if (change.getParent() != null) {
|
||||
// This is probably reparented, so we want the parent to be immediately visible
|
||||
final TransitionInfo.Change parentChange = info.getChange(change.getParent());
|
||||
t.show(parentChange.getLeash());
|
||||
t.setAlpha(parentChange.getLeash(), 1.f);
|
||||
// and then animate this layer outside the parent (since, for example, this is
|
||||
// the home task animating from fullscreen to part-screen).
|
||||
t.reparent(parentChange.getLeash(), info.getRoot(rootIdx).getLeash());
|
||||
t.setLayer(parentChange.getLeash(), info.getChanges().size() - i);
|
||||
// build the finish reparent/reposition
|
||||
mFinishTransaction.reparent(leash, parentChange.getLeash());
|
||||
mFinishTransaction.setPosition(leash,
|
||||
change.getEndRelOffset().x, change.getEndRelOffset().y);
|
||||
}
|
||||
}
|
||||
|
||||
final boolean isTopRoot = topRoot.equals(change.getContainer());
|
||||
final boolean isMainRoot = mainRoot.equals(change.getContainer());
|
||||
final boolean isSideRoot = sideRoot.equals(change.getContainer());
|
||||
final boolean isDivider = change.getFlags() == FLAG_IS_DIVIDER_BAR;
|
||||
final boolean isMainChild = mainRoot.equals(change.getParent());
|
||||
final boolean isSideChild = sideRoot.equals(change.getParent());
|
||||
if (isEnter && (isMainChild || isSideChild)) {
|
||||
// Reset child tasks bounds on finish.
|
||||
mFinishTransaction.setPosition(leash,
|
||||
change.getEndRelOffset().x, change.getEndRelOffset().y);
|
||||
mFinishTransaction.setCrop(leash, null);
|
||||
} else if (isTopRoot) {
|
||||
// Ensure top root is visible at start.
|
||||
t.setAlpha(leash, 1.f);
|
||||
t.show(leash);
|
||||
} else if (isEnter && isMainRoot || isSideRoot) {
|
||||
t.setPosition(leash, change.getEndAbsBounds().left, change.getEndAbsBounds().top);
|
||||
t.setWindowCrop(leash, change.getEndAbsBounds().width(),
|
||||
change.getEndAbsBounds().height());
|
||||
} else if (isDivider) {
|
||||
t.setPosition(leash, change.getEndAbsBounds().left, change.getEndAbsBounds().top);
|
||||
t.setLayer(leash, Integer.MAX_VALUE);
|
||||
t.show(leash);
|
||||
}
|
||||
|
||||
// We want to use child tasks to animate so ignore split root container and non task
|
||||
// except divider change.
|
||||
if (isTopRoot || isMainRoot || isSideRoot
|
||||
|| (change.getTaskInfo() == null && !isDivider)) {
|
||||
continue;
|
||||
}
|
||||
if (isEnter && mPendingEnter.mResizeAnim) {
|
||||
// We will run animation in next transition so skip anim here
|
||||
continue;
|
||||
} else if (isPendingDismiss(transition)
|
||||
&& mPendingDismiss.mReason == EXIT_REASON_DRAG_DIVIDER) {
|
||||
// TODO(b/280020345): need to refine animation for this but just skip anim now.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Because cross fade might be looked more flicker during animation
|
||||
// (surface become black in middle of animation), we only do fade-out
|
||||
// and show opening surface directly.
|
||||
boolean isOpening = TransitionUtil.isOpeningType(info.getType());
|
||||
if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) {
|
||||
// fade out
|
||||
startFadeAnimation(leash, false /* show */);
|
||||
} else if (mode == TRANSIT_CHANGE && change.getSnapshot() != null) {
|
||||
t.reparent(change.getSnapshot(), info.getRoot(rootIdx).getLeash());
|
||||
// Ensure snapshot it on the top of all transition surfaces
|
||||
t.setLayer(change.getSnapshot(), info.getChanges().size() + 1);
|
||||
t.setPosition(change.getSnapshot(), change.getStartAbsBounds().left,
|
||||
change.getStartAbsBounds().top);
|
||||
t.show(change.getSnapshot());
|
||||
startFadeAnimation(change.getSnapshot(), false /* show */);
|
||||
}
|
||||
}
|
||||
t.apply();
|
||||
onFinish(null /* wct */);
|
||||
}
|
||||
|
||||
/** Play animation for drag divider dismiss transition. */
|
||||
void playDragDismissAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||
@NonNull SurfaceControl.Transaction startTransaction,
|
||||
@NonNull SurfaceControl.Transaction finishTransaction,
|
||||
@NonNull Transitions.TransitionFinishCallback finishCallback,
|
||||
@NonNull WindowContainerToken toTopRoot, @NonNull SplitDecorManager toTopDecor,
|
||||
@NonNull WindowContainerToken topRoot) {
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playDragDismissAnimation: transition=%d",
|
||||
info.getDebugId());
|
||||
initTransition(transition, finishTransaction, finishCallback);
|
||||
|
||||
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||
final SurfaceControl leash = change.getLeash();
|
||||
|
||||
if (toTopRoot.equals(change.getContainer())) {
|
||||
startTransaction.setAlpha(leash, 1.f);
|
||||
startTransaction.show(leash);
|
||||
|
||||
ValueAnimator va = new ValueAnimator();
|
||||
mAnimations.add(va);
|
||||
|
||||
toTopDecor.onResized(startTransaction, animated -> {
|
||||
mAnimations.remove(va);
|
||||
if (animated) {
|
||||
mTransitions.getMainExecutor().execute(() -> {
|
||||
onFinish(null /* wct */);
|
||||
});
|
||||
}
|
||||
});
|
||||
} else if (topRoot.equals(change.getContainer())) {
|
||||
// Ensure it on top of all changes in transition.
|
||||
startTransaction.setLayer(leash, Integer.MAX_VALUE);
|
||||
startTransaction.setAlpha(leash, 1.f);
|
||||
startTransaction.show(leash);
|
||||
}
|
||||
}
|
||||
startTransaction.apply();
|
||||
onFinish(null /* wct */);
|
||||
}
|
||||
|
||||
/** Play animation for resize transition. */
|
||||
void playResizeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
|
||||
@NonNull SurfaceControl.Transaction startTransaction,
|
||||
@NonNull SurfaceControl.Transaction finishTransaction,
|
||||
@NonNull Transitions.TransitionFinishCallback finishCallback,
|
||||
@NonNull WindowContainerToken mainRoot, @NonNull WindowContainerToken sideRoot,
|
||||
@NonNull SplitDecorManager mainDecor, @NonNull SplitDecorManager sideDecor) {
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "playResizeAnimation: transition=%d", info.getDebugId());
|
||||
initTransition(transition, finishTransaction, finishCallback);
|
||||
|
||||
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
|
||||
final TransitionInfo.Change change = info.getChanges().get(i);
|
||||
if (mainRoot.equals(change.getContainer()) || sideRoot.equals(change.getContainer())) {
|
||||
final SurfaceControl leash = change.getLeash();
|
||||
startTransaction.setPosition(leash, change.getEndAbsBounds().left,
|
||||
change.getEndAbsBounds().top);
|
||||
startTransaction.setWindowCrop(leash, change.getEndAbsBounds().width(),
|
||||
change.getEndAbsBounds().height());
|
||||
|
||||
SplitDecorManager decor = mainRoot.equals(change.getContainer())
|
||||
? mainDecor : sideDecor;
|
||||
|
||||
// This is to ensure onFinished be called after all animations ended.
|
||||
ValueAnimator va = new ValueAnimator();
|
||||
mAnimations.add(va);
|
||||
|
||||
decor.setScreenshotIfNeeded(change.getSnapshot(), startTransaction);
|
||||
decor.onResized(startTransaction, animated -> {
|
||||
mAnimations.remove(va);
|
||||
if (animated) {
|
||||
mTransitions.getMainExecutor().execute(() -> {
|
||||
onFinish(null /* wct */);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
startTransaction.apply();
|
||||
onFinish(null /* wct */);
|
||||
}
|
||||
|
||||
boolean isPendingTransition(IBinder transition) {
|
||||
return getPendingTransition(transition) != null;
|
||||
}
|
||||
|
||||
boolean isPendingEnter(IBinder transition) {
|
||||
return mPendingEnter != null && mPendingEnter.mTransition == transition;
|
||||
}
|
||||
|
||||
boolean isPendingDismiss(IBinder transition) {
|
||||
return mPendingDismiss != null && mPendingDismiss.mTransition == transition;
|
||||
}
|
||||
|
||||
boolean isPendingResize(IBinder transition) {
|
||||
return mPendingResize != null && mPendingResize.mTransition == transition;
|
||||
}
|
||||
|
||||
boolean isPendingPassThrough(IBinder transition) {
|
||||
return mPendingRemotePassthrough != null &&
|
||||
mPendingRemotePassthrough.mTransition == transition;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private TransitSession getPendingTransition(IBinder transition) {
|
||||
if (isPendingEnter(transition)) {
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved enter transition");
|
||||
return mPendingEnter;
|
||||
} else if (isPendingDismiss(transition)) {
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved dismiss transition");
|
||||
return mPendingDismiss;
|
||||
} else if (isPendingResize(transition)) {
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved resize transition");
|
||||
return mPendingResize;
|
||||
} else if (isPendingPassThrough(transition)) {
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "\tresolved passThrough transition");
|
||||
return mPendingRemotePassthrough;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void startFullscreenTransition(WindowContainerTransaction wct,
|
||||
@Nullable RemoteTransition handler) {
|
||||
OneShotRemoteHandler fullscreenHandler =
|
||||
new OneShotRemoteHandler(mTransitions.getMainExecutor(), handler);
|
||||
fullscreenHandler.setTransition(mTransitions
|
||||
.startTransition(TRANSIT_OPEN, wct, fullscreenHandler));
|
||||
}
|
||||
|
||||
|
||||
/** Starts a transition to enter split with a remote transition animator. */
|
||||
IBinder startEnterTransition(
|
||||
@WindowManager.TransitionType int transitType,
|
||||
WindowContainerTransaction wct,
|
||||
@Nullable RemoteTransition remoteTransition,
|
||||
Transitions.TransitionHandler handler,
|
||||
int extraTransitType, boolean resizeAnim) {
|
||||
if (mPendingEnter != null) {
|
||||
ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
|
||||
+ " skip to start enter split transition since it already exist. ");
|
||||
return null;
|
||||
}
|
||||
if (mSplitInvocationListenerExecutor != null && mSplitInvocationListener != null) {
|
||||
mSplitInvocationListenerExecutor.execute(() -> mSplitInvocationListener
|
||||
.onSplitAnimationInvoked(true /*animationRunning*/));
|
||||
}
|
||||
final IBinder transition = mTransitions.startTransition(transitType, wct, handler);
|
||||
setEnterTransition(transition, remoteTransition, extraTransitType, resizeAnim);
|
||||
return transition;
|
||||
}
|
||||
|
||||
/** Sets a transition to enter split. */
|
||||
void setEnterTransition(@NonNull IBinder transition,
|
||||
@Nullable RemoteTransition remoteTransition,
|
||||
int extraTransitType, boolean resizeAnim) {
|
||||
mPendingEnter = new EnterSession(
|
||||
transition, remoteTransition, extraTransitType, resizeAnim);
|
||||
|
||||
ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
|
||||
+ " deduced Enter split screen");
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setEnterTransition: transitType=%d resize=%b",
|
||||
extraTransitType, resizeAnim);
|
||||
}
|
||||
|
||||
/** Sets a transition to enter split. */
|
||||
void setRemotePassThroughTransition(@NonNull IBinder transition,
|
||||
@Nullable RemoteTransition remoteTransition) {
|
||||
mPendingRemotePassthrough = new TransitSession(
|
||||
transition, null, null,
|
||||
remoteTransition, Transitions.TRANSIT_SPLIT_PASSTHROUGH);
|
||||
|
||||
ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
|
||||
+ " deduced remote passthrough split screen");
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setRemotePassThrough: transitType=%d remote=%s",
|
||||
Transitions.TRANSIT_SPLIT_PASSTHROUGH, remoteTransition);
|
||||
}
|
||||
|
||||
/** Starts a transition to dismiss split. */
|
||||
IBinder startDismissTransition(WindowContainerTransaction wct,
|
||||
Transitions.TransitionHandler handler, @SplitScreen.StageType int dismissTop,
|
||||
@SplitScreenController.ExitReason int reason) {
|
||||
if (mPendingDismiss != null) {
|
||||
ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
|
||||
+ " skip to start dismiss split transition since it already exist. reason to "
|
||||
+ " dismiss = %s", exitReasonToString(reason));
|
||||
return null;
|
||||
}
|
||||
final int type = reason == EXIT_REASON_DRAG_DIVIDER
|
||||
? TRANSIT_SPLIT_DISMISS_SNAP : TRANSIT_SPLIT_DISMISS;
|
||||
IBinder transition = mTransitions.startTransition(type, wct, handler);
|
||||
setDismissTransition(transition, dismissTop, reason);
|
||||
return transition;
|
||||
}
|
||||
|
||||
/** Sets a transition to dismiss split. */
|
||||
void setDismissTransition(@NonNull IBinder transition, @SplitScreen.StageType int dismissTop,
|
||||
@SplitScreenController.ExitReason int reason) {
|
||||
mPendingDismiss = new DismissSession(transition, reason, dismissTop);
|
||||
|
||||
ProtoLog.v(WM_SHELL_TRANSITIONS, " splitTransition "
|
||||
+ " deduced Dismiss due to %s. toTop=%s",
|
||||
exitReasonToString(reason), stageTypeToString(dismissTop));
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setDismissTransition: reason=%s dismissTop=%s",
|
||||
exitReasonToString(reason), stageTypeToString(dismissTop));
|
||||
}
|
||||
|
||||
IBinder startResizeTransition(WindowContainerTransaction wct,
|
||||
Transitions.TransitionHandler handler,
|
||||
@Nullable TransitionConsumedCallback consumedCallback,
|
||||
@Nullable TransitionFinishedCallback finishCallback,
|
||||
@NonNull SplitDecorManager mainDecor, @NonNull SplitDecorManager sideDecor) {
|
||||
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
|
||||
" splitTransition deduced Resize split screen.");
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "setResizeTransition: hasPendingResize=%b",
|
||||
mPendingResize != null);
|
||||
if (mPendingResize != null) {
|
||||
mainDecor.cancelRunningAnimations();
|
||||
sideDecor.cancelRunningAnimations();
|
||||
mPendingResize.cancel(null);
|
||||
mAnimations.clear();
|
||||
onFinish(null /* wct */);
|
||||
}
|
||||
|
||||
IBinder transition = mTransitions.startTransition(TRANSIT_CHANGE, wct, handler);
|
||||
mPendingResize = new TransitSession(transition, consumedCallback, finishCallback);
|
||||
return transition;
|
||||
}
|
||||
|
||||
void mergeAnimation(IBinder transition, TransitionInfo info, SurfaceControl.Transaction t,
|
||||
IBinder mergeTarget, Transitions.TransitionFinishCallback finishCallback) {
|
||||
if (mergeTarget != mAnimatingTransition) return;
|
||||
|
||||
if (mActiveRemoteHandler != null) {
|
||||
mActiveRemoteHandler.mergeAnimation(transition, info, t, mergeTarget, finishCallback);
|
||||
} else {
|
||||
for (int i = mAnimations.size() - 1; i >= 0; --i) {
|
||||
final Animator anim = mAnimations.get(i);
|
||||
mTransitions.getAnimExecutor().execute(anim::end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean end() {
|
||||
// If It's remote, there's nothing we can do right now.
|
||||
if (mActiveRemoteHandler != null) return false;
|
||||
for (int i = mAnimations.size() - 1; i >= 0; --i) {
|
||||
final Animator anim = mAnimations.get(i);
|
||||
mTransitions.getAnimExecutor().execute(anim::end);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
|
||||
@Nullable SurfaceControl.Transaction finishT) {
|
||||
if (isPendingEnter(transition)) {
|
||||
if (!aborted) {
|
||||
// An entering transition got merged, appends the rest operations to finish entering
|
||||
// split screen.
|
||||
mStageCoordinator.finishEnterSplitScreen(finishT);
|
||||
}
|
||||
|
||||
mPendingEnter.onConsumed(aborted);
|
||||
mPendingEnter = null;
|
||||
mStageCoordinator.notifySplitAnimationFinished();
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for enter transition");
|
||||
} else if (isPendingDismiss(transition)) {
|
||||
mPendingDismiss.onConsumed(aborted);
|
||||
mPendingDismiss = null;
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for dismiss transition");
|
||||
} else if (isPendingResize(transition)) {
|
||||
mPendingResize.onConsumed(aborted);
|
||||
mPendingResize = null;
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for resize transition");
|
||||
} else if (isPendingPassThrough(transition)) {
|
||||
mPendingRemotePassthrough.onConsumed(aborted);
|
||||
mPendingRemotePassthrough.mRemoteHandler.onTransitionConsumed(transition, aborted,
|
||||
finishT);
|
||||
mPendingRemotePassthrough = null;
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onTransitionConsumed for passThrough transition");
|
||||
}
|
||||
|
||||
// TODO: handle transition consumed for active remote handler
|
||||
}
|
||||
|
||||
void onFinish(WindowContainerTransaction wct) {
|
||||
if (!mAnimations.isEmpty()) return;
|
||||
|
||||
if (wct == null) wct = new WindowContainerTransaction();
|
||||
if (isPendingEnter(mAnimatingTransition)) {
|
||||
mPendingEnter.onFinished(wct, mFinishTransaction);
|
||||
mPendingEnter = null;
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for enter transition");
|
||||
} else if (isPendingDismiss(mAnimatingTransition)) {
|
||||
mPendingDismiss.onFinished(wct, mFinishTransaction);
|
||||
mPendingDismiss = null;
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for dismiss transition");
|
||||
} else if (isPendingResize(mAnimatingTransition)) {
|
||||
mPendingResize.onFinished(wct, mFinishTransaction);
|
||||
mPendingResize = null;
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for resize transition");
|
||||
} else if (isPendingPassThrough(mAnimatingTransition)) {
|
||||
mPendingRemotePassthrough.onFinished(wct, mFinishTransaction);
|
||||
mPendingRemotePassthrough = null;
|
||||
ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onFinish for passThrough transition");
|
||||
}
|
||||
|
||||
mActiveRemoteHandler = null;
|
||||
mAnimatingTransition = null;
|
||||
|
||||
mOnFinish.run();
|
||||
if (mFinishCallback != null) {
|
||||
Transitions.TransitionFinishCallback currentFinishCallback = mFinishCallback;
|
||||
mFinishCallback = null;
|
||||
currentFinishCallback.onTransitionFinished(wct /* wct */);
|
||||
}
|
||||
}
|
||||
|
||||
private void startFadeAnimation(@NonNull SurfaceControl leash, boolean show) {
|
||||
final float end = show ? 1.f : 0.f;
|
||||
final float start = 1.f - end;
|
||||
final ValueAnimator va = ValueAnimator.ofFloat(start, end);
|
||||
va.setDuration(FADE_DURATION);
|
||||
va.setInterpolator(show ? ALPHA_IN : ALPHA_OUT);
|
||||
va.addUpdateListener(animation -> {
|
||||
float fraction = animation.getAnimatedFraction();
|
||||
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
|
||||
transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction);
|
||||
transaction.apply();
|
||||
mTransactionPool.release(transaction);
|
||||
});
|
||||
va.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
final SurfaceControl.Transaction transaction = mTransactionPool.acquire();
|
||||
transaction.setAlpha(leash, end);
|
||||
transaction.apply();
|
||||
mTransactionPool.release(transaction);
|
||||
mTransitions.getMainExecutor().execute(() -> {
|
||||
mAnimations.remove(va);
|
||||
onFinish(null /* wct */);
|
||||
});
|
||||
}
|
||||
});
|
||||
mAnimations.add(va);
|
||||
mTransitions.getAnimExecutor().execute(va::start);
|
||||
}
|
||||
|
||||
public void registerSplitAnimListener(@NonNull SplitScreen.SplitInvocationListener listener,
|
||||
@NonNull Executor executor) {
|
||||
mSplitInvocationListener = listener;
|
||||
mSplitInvocationListenerExecutor = executor;
|
||||
}
|
||||
|
||||
/** Calls when the transition got consumed. */
|
||||
interface TransitionConsumedCallback {
|
||||
void onConsumed(boolean aborted);
|
||||
}
|
||||
|
||||
/** Calls when the transition finished. */
|
||||
interface TransitionFinishedCallback {
|
||||
void onFinished(WindowContainerTransaction wct, SurfaceControl.Transaction t);
|
||||
}
|
||||
|
||||
/** Session for a transition and its clean-up callback. */
|
||||
class TransitSession {
|
||||
final IBinder mTransition;
|
||||
TransitionConsumedCallback mConsumedCallback;
|
||||
TransitionFinishedCallback mFinishedCallback;
|
||||
OneShotRemoteHandler mRemoteHandler;
|
||||
|
||||
/** Whether the transition was canceled. */
|
||||
boolean mCanceled;
|
||||
|
||||
/** A note for extra transit type, to help indicate custom transition. */
|
||||
final int mExtraTransitType;
|
||||
|
||||
TransitSession(IBinder transition,
|
||||
@Nullable TransitionConsumedCallback consumedCallback,
|
||||
@Nullable TransitionFinishedCallback finishedCallback) {
|
||||
this(transition, consumedCallback, finishedCallback, null /* remoteTransition */, 0);
|
||||
}
|
||||
|
||||
TransitSession(IBinder transition,
|
||||
@Nullable TransitionConsumedCallback consumedCallback,
|
||||
@Nullable TransitionFinishedCallback finishedCallback,
|
||||
@Nullable RemoteTransition remoteTransition, int extraTransitType) {
|
||||
mTransition = transition;
|
||||
mConsumedCallback = consumedCallback;
|
||||
mFinishedCallback = finishedCallback;
|
||||
|
||||
if (remoteTransition != null) {
|
||||
// Wrapping the remote transition for ease-of-use. (OneShot handles all the binder
|
||||
// linking/death stuff)
|
||||
mRemoteHandler = new OneShotRemoteHandler(
|
||||
mTransitions.getMainExecutor(), remoteTransition);
|
||||
mRemoteHandler.setTransition(transition);
|
||||
}
|
||||
mExtraTransitType = extraTransitType;
|
||||
}
|
||||
|
||||
/** Sets transition consumed callback. */
|
||||
void setConsumedCallback(@Nullable TransitionConsumedCallback callback) {
|
||||
mConsumedCallback = callback;
|
||||
}
|
||||
|
||||
/** Sets transition finished callback. */
|
||||
void setFinishedCallback(@Nullable TransitionFinishedCallback callback) {
|
||||
mFinishedCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels the transition. This should be called before playing animation. A canceled
|
||||
* transition will skip playing animation.
|
||||
*
|
||||
* @param finishedCb new finish callback to override.
|
||||
*/
|
||||
void cancel(@Nullable TransitionFinishedCallback finishedCb) {
|
||||
mCanceled = true;
|
||||
setFinishedCallback(finishedCb);
|
||||
}
|
||||
|
||||
void onConsumed(boolean aborted) {
|
||||
if (mConsumedCallback != null) {
|
||||
mConsumedCallback.onConsumed(aborted);
|
||||
}
|
||||
}
|
||||
|
||||
void onFinished(WindowContainerTransaction finishWct,
|
||||
SurfaceControl.Transaction finishT) {
|
||||
if (mFinishedCallback != null) {
|
||||
mFinishedCallback.onFinished(finishWct, finishT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Bundled information of enter transition. */
|
||||
class EnterSession extends TransitSession {
|
||||
final boolean mResizeAnim;
|
||||
|
||||
EnterSession(IBinder transition,
|
||||
@Nullable RemoteTransition remoteTransition,
|
||||
int extraTransitType, boolean resizeAnim) {
|
||||
super(transition, null /* consumedCallback */, null /* finishedCallback */,
|
||||
remoteTransition, extraTransitType);
|
||||
this.mResizeAnim = resizeAnim;
|
||||
}
|
||||
}
|
||||
|
||||
/** Bundled information of dismiss transition. */
|
||||
class DismissSession extends TransitSession {
|
||||
final int mReason;
|
||||
final @SplitScreen.StageType int mDismissTop;
|
||||
|
||||
DismissSession(IBinder transition, int reason, int dismissTop) {
|
||||
super(transition, null /* consumedCallback */, null /* finishedCallback */);
|
||||
this.mReason = reason;
|
||||
this.mDismissTop = dismissTop;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user