/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
Java | 730 lines | 496 code | 76 blank | 158 comment | 88 complexity | 070d49e213bc00b8f211274377bd637e MD5 | raw file
Possible License(s): Apache-2.0
- /*
- * 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.transition;
- import static android.view.WindowManager.TRANSIT_CHANGE;
- import static android.view.WindowManager.TRANSIT_CLOSE;
- import static android.view.WindowManager.TRANSIT_FIRST_CUSTOM;
- import static android.view.WindowManager.TRANSIT_OPEN;
- import static android.view.WindowManager.TRANSIT_TO_BACK;
- import static android.view.WindowManager.TRANSIT_TO_FRONT;
- import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
- import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
- import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
- import android.annotation.NonNull;
- import android.annotation.Nullable;
- import android.content.ContentResolver;
- import android.content.Context;
- import android.database.ContentObserver;
- import android.os.IBinder;
- import android.os.RemoteException;
- import android.os.SystemProperties;
- import android.provider.Settings;
- import android.util.Log;
- import android.view.SurfaceControl;
- import android.view.WindowManager;
- import android.window.IRemoteTransition;
- import android.window.ITransitionPlayer;
- import android.window.TransitionFilter;
- import android.window.TransitionInfo;
- import android.window.TransitionRequestInfo;
- import android.window.WindowContainerTransaction;
- import android.window.WindowContainerTransactionCallback;
- import android.window.WindowOrganizer;
- import androidx.annotation.BinderThread;
- import com.android.internal.R;
- import com.android.internal.annotations.VisibleForTesting;
- import com.android.internal.protolog.common.ProtoLog;
- import com.android.wm.shell.ShellTaskOrganizer;
- import com.android.wm.shell.common.RemoteCallable;
- import com.android.wm.shell.common.ShellExecutor;
- import com.android.wm.shell.common.TransactionPool;
- import com.android.wm.shell.common.annotations.ExternalThread;
- import com.android.wm.shell.protolog.ShellProtoLogGroup;
- import java.util.ArrayList;
- import java.util.Arrays;
- /** Plays transition animations */
- public class Transitions implements RemoteCallable<Transitions> {
- static final String TAG = "ShellTransitions";
- /** Set to {@code true} to enable shell transitions. */
- public static final boolean ENABLE_SHELL_TRANSITIONS =
- SystemProperties.getBoolean("persist.debug.shell_transit", false);
- /** Transition type for dismissing split-screen via dragging the divider off the screen. */
- public static final int TRANSIT_SPLIT_DISMISS_SNAP = TRANSIT_FIRST_CUSTOM + 1;
- /** Transition type for launching 2 tasks simultaneously. */
- public static final int TRANSIT_SPLIT_SCREEN_PAIR_OPEN = TRANSIT_FIRST_CUSTOM + 2;
- private final WindowOrganizer mOrganizer;
- private final Context mContext;
- private final ShellExecutor mMainExecutor;
- private final ShellExecutor mAnimExecutor;
- private final TransitionPlayerImpl mPlayerImpl;
- private final RemoteTransitionHandler mRemoteTransitionHandler;
- private final ShellTransitionImpl mImpl = new ShellTransitionImpl();
- /** List of possible handlers. Ordered by specificity (eg. tapped back to front). */
- private final ArrayList<TransitionHandler> mHandlers = new ArrayList<>();
- private float mTransitionAnimationScaleSetting = 1.0f;
- private static final class ActiveTransition {
- IBinder mToken = null;
- TransitionHandler mHandler = null;
- boolean mMerged = false;
- TransitionInfo mInfo = null;
- SurfaceControl.Transaction mStartT = null;
- SurfaceControl.Transaction mFinishT = null;
- }
- /** Keeps track of currently playing transitions in the order of receipt. */
- private final ArrayList<ActiveTransition> mActiveTransitions = new ArrayList<>();
- public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool,
- @NonNull Context context, @NonNull ShellExecutor mainExecutor,
- @NonNull ShellExecutor animExecutor) {
- mOrganizer = organizer;
- mContext = context;
- mMainExecutor = mainExecutor;
- mAnimExecutor = animExecutor;
- mPlayerImpl = new TransitionPlayerImpl();
- // The very last handler (0 in the list) should be the default one.
- mHandlers.add(new DefaultTransitionHandler(pool, context, mainExecutor, animExecutor));
- // Next lowest priority is remote transitions.
- mRemoteTransitionHandler = new RemoteTransitionHandler(mainExecutor);
- mHandlers.add(mRemoteTransitionHandler);
- ContentResolver resolver = context.getContentResolver();
- mTransitionAnimationScaleSetting = Settings.Global.getFloat(resolver,
- Settings.Global.TRANSITION_ANIMATION_SCALE,
- context.getResources().getFloat(
- R.dimen.config_appTransitionAnimationDurationScaleDefault));
- dispatchAnimScaleSetting(mTransitionAnimationScaleSetting);
- resolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.TRANSITION_ANIMATION_SCALE), false,
- new SettingsObserver());
- }
- private Transitions() {
- mOrganizer = null;
- mContext = null;
- mMainExecutor = null;
- mAnimExecutor = null;
- mPlayerImpl = null;
- mRemoteTransitionHandler = null;
- }
- public ShellTransitions asRemoteTransitions() {
- return mImpl;
- }
- @Override
- public Context getContext() {
- return mContext;
- }
- @Override
- public ShellExecutor getRemoteCallExecutor() {
- return mMainExecutor;
- }
- private void dispatchAnimScaleSetting(float scale) {
- for (int i = mHandlers.size() - 1; i >= 0; --i) {
- mHandlers.get(i).setAnimScaleSetting(scale);
- }
- }
- /** Create an empty/non-registering transitions object for system-ui tests. */
- @VisibleForTesting
- public static ShellTransitions createEmptyForTesting() {
- return new ShellTransitions() {
- @Override
- public void registerRemote(@androidx.annotation.NonNull TransitionFilter filter,
- @androidx.annotation.NonNull IRemoteTransition remoteTransition) {
- // Do nothing
- }
- @Override
- public void unregisterRemote(
- @androidx.annotation.NonNull IRemoteTransition remoteTransition) {
- // Do nothing
- }
- };
- }
- /** Register this transition handler with Core */
- public void register(ShellTaskOrganizer taskOrganizer) {
- if (mPlayerImpl == null) return;
- taskOrganizer.registerTransitionPlayer(mPlayerImpl);
- }
- /**
- * Adds a handler candidate.
- * @see TransitionHandler
- */
- public void addHandler(@NonNull TransitionHandler handler) {
- mHandlers.add(handler);
- }
- public ShellExecutor getMainExecutor() {
- return mMainExecutor;
- }
- public ShellExecutor getAnimExecutor() {
- return mAnimExecutor;
- }
- /** Only use this in tests. This is used to avoid running animations during tests. */
- @VisibleForTesting
- void replaceDefaultHandlerForTest(TransitionHandler handler) {
- mHandlers.set(0, handler);
- }
- /** Register a remote transition to be used when `filter` matches an incoming transition */
- public void registerRemote(@NonNull TransitionFilter filter,
- @NonNull IRemoteTransition remoteTransition) {
- mRemoteTransitionHandler.addFiltered(filter, remoteTransition);
- }
- /** Unregisters a remote transition and all associated filters */
- public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) {
- mRemoteTransitionHandler.removeFiltered(remoteTransition);
- }
- /** @return true if the transition was triggered by opening something vs closing something */
- public static boolean isOpeningType(@WindowManager.TransitionType int type) {
- return type == TRANSIT_OPEN
- || type == TRANSIT_TO_FRONT
- || type == WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
- }
- /** @return true if the transition was triggered by closing something vs opening something */
- public static boolean isClosingType(@WindowManager.TransitionType int type) {
- return type == TRANSIT_CLOSE || type == TRANSIT_TO_BACK;
- }
- /**
- * Sets up visibility/alpha/transforms to resemble the starting state of an animation.
- */
- private static void setupStartState(@NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
- boolean isOpening = isOpeningType(info.getType());
- 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();
- // Don't move anything that isn't independent within its parents
- if (!TransitionInfo.isIndependent(change, info)) {
- if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT || mode == TRANSIT_CHANGE) {
- t.show(leash);
- t.setMatrix(leash, 1, 0, 0, 1);
- t.setAlpha(leash, 1.f);
- t.setPosition(leash, change.getEndRelOffset().x, change.getEndRelOffset().y);
- }
- continue;
- }
- if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
- t.show(leash);
- t.setMatrix(leash, 1, 0, 0, 1);
- if (isOpening
- // If this is a transferred starting window, we want it immediately visible.
- && (change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) == 0) {
- t.setAlpha(leash, 0.f);
- // fix alpha in finish transaction in case the animator itself no-ops.
- finishT.setAlpha(leash, 1.f);
- }
- } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
- // Wallpaper is a bit of an anomaly: it's visibility is tied to other WindowStates.
- // As a result, we actually can't hide it's WindowToken because there may not be a
- // transition associated with it becoming visible again. Fortunately, since it is
- // always z-ordered to the back, we don't have to worry about it flickering to the
- // front during reparenting, so the hide here isn't necessary for it.
- if ((change.getFlags() & FLAG_IS_WALLPAPER) == 0) {
- finishT.hide(leash);
- }
- }
- }
- }
- /**
- * Reparents all participants into a shared parent and orders them based on: the global transit
- * type, their transit mode, and their destination z-order.
- */
- private static void setupAnimHierarchy(@NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
- boolean isOpening = isOpeningType(info.getType());
- if (info.getRootLeash().isValid()) {
- t.show(info.getRootLeash());
- }
- // Put animating stuff above this line and put static stuff below it.
- int zSplitLine = info.getChanges().size();
- // changes should be ordered top-to-bottom in z
- 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();
- // Don't reparent anything that isn't independent within its parents
- if (!TransitionInfo.isIndependent(change, info)) {
- continue;
- }
- boolean hasParent = change.getParent() != null;
- if (!hasParent) {
- t.reparent(leash, info.getRootLeash());
- t.setPosition(leash, change.getStartAbsBounds().left - info.getRootOffset().x,
- change.getStartAbsBounds().top - info.getRootOffset().y);
- }
- // Put all the OPEN/SHOW on top
- if (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT) {
- if (isOpening) {
- // put on top
- t.setLayer(leash, zSplitLine + info.getChanges().size() - i);
- } else {
- // put on bottom
- t.setLayer(leash, zSplitLine - i);
- }
- } else if (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK) {
- if (isOpening) {
- // put on bottom and leave visible
- t.setLayer(leash, zSplitLine - i);
- } else {
- // put on top
- t.setLayer(leash, zSplitLine + info.getChanges().size() - i);
- }
- } else { // CHANGE or other
- t.setLayer(leash, zSplitLine + info.getChanges().size() - i);
- }
- }
- }
- private int findActiveTransition(IBinder token) {
- for (int i = mActiveTransitions.size() - 1; i >= 0; --i) {
- if (mActiveTransitions.get(i).mToken == token) return i;
- }
- return -1;
- }
- @VisibleForTesting
- void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "onTransitionReady %s: %s",
- transitionToken, info);
- final int activeIdx = findActiveTransition(transitionToken);
- if (activeIdx < 0) {
- throw new IllegalStateException("Got transitionReady for non-active transition "
- + transitionToken + ". expecting one of "
- + Arrays.toString(mActiveTransitions.stream().map(
- activeTransition -> activeTransition.mToken).toArray()));
- }
- if (!info.getRootLeash().isValid()) {
- // Invalid root-leash implies that the transition is empty/no-op, so just do
- // housekeeping and return.
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Invalid root leash (%s): %s",
- transitionToken, info);
- t.apply();
- onAbort(transitionToken);
- return;
- }
- final ActiveTransition active = mActiveTransitions.get(activeIdx);
- active.mInfo = info;
- active.mStartT = t;
- active.mFinishT = finishT;
- setupStartState(active.mInfo, active.mStartT, active.mFinishT);
- if (activeIdx > 0) {
- // This is now playing at the same time as an existing animation, so try merging it.
- attemptMergeTransition(mActiveTransitions.get(0), active);
- return;
- }
- // The normal case, just play it.
- playTransition(active);
- }
- /**
- * Attempt to merge by delegating the transition start to the handler of the currently
- * playing transition.
- */
- void attemptMergeTransition(@NonNull ActiveTransition playing,
- @NonNull ActiveTransition merging) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s ready while"
- + " another transition %s is still animating. Notify the animating transition"
- + " in case they can be merged", merging.mToken, playing.mToken);
- playing.mHandler.mergeAnimation(merging.mToken, merging.mInfo, merging.mStartT,
- playing.mToken, (wct, cb) -> onFinish(merging.mToken, wct, cb));
- }
- boolean startAnimation(@NonNull ActiveTransition active, TransitionHandler handler) {
- return handler.startAnimation(active.mToken, active.mInfo, active.mStartT,
- (wct, cb) -> onFinish(active.mToken, wct, cb));
- }
- void playTransition(@NonNull ActiveTransition active) {
- setupAnimHierarchy(active.mInfo, active.mStartT, active.mFinishT);
- // If a handler already chose to run this animation, try delegating to it first.
- if (active.mHandler != null) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try firstHandler %s",
- active.mHandler);
- if (startAnimation(active, active.mHandler)) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by firstHandler");
- return;
- }
- }
- // Otherwise give every other handler a chance (in order)
- for (int i = mHandlers.size() - 1; i >= 0; --i) {
- if (mHandlers.get(i) == active.mHandler) continue;
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " try handler %s",
- mHandlers.get(i));
- if (startAnimation(active, mHandlers.get(i))) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " animated by %s",
- mHandlers.get(i));
- active.mHandler = mHandlers.get(i);
- return;
- }
- }
- throw new IllegalStateException(
- "This shouldn't happen, maybe the default handler is broken.");
- }
- /** Special version of finish just for dealing with no-op/invalid transitions. */
- private void onAbort(IBinder transition) {
- final int activeIdx = findActiveTransition(transition);
- if (activeIdx < 0) return;
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
- "Transition animation aborted due to no-op, notifying core %s", transition);
- mActiveTransitions.remove(activeIdx);
- mOrganizer.finishTransition(transition, null /* wct */, null /* wctCB */);
- }
- private void onFinish(IBinder transition,
- @Nullable WindowContainerTransaction wct,
- @Nullable WindowContainerTransactionCallback wctCB) {
- int activeIdx = findActiveTransition(transition);
- if (activeIdx < 0) {
- Log.e(TAG, "Trying to finish a non-running transition. Either remote crashed or "
- + " a handler didn't properly deal with a merge.", new RuntimeException());
- return;
- } else if (activeIdx > 0) {
- // This transition was merged.
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged: %s",
- transition);
- final ActiveTransition active = mActiveTransitions.get(activeIdx);
- active.mMerged = true;
- if (active.mHandler != null) {
- active.mHandler.onTransitionMerged(active.mToken);
- }
- return;
- }
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
- "Transition animation finished, notifying core %s", transition);
- // Merge all relevant transactions together
- SurfaceControl.Transaction fullFinish = mActiveTransitions.get(activeIdx).mFinishT;
- for (int iA = activeIdx + 1; iA < mActiveTransitions.size(); ++iA) {
- final ActiveTransition toMerge = mActiveTransitions.get(iA);
- if (!toMerge.mMerged) break;
- // Include start. It will be a no-op if it was already applied. Otherwise, we need it
- // to maintain consistent state.
- fullFinish.merge(mActiveTransitions.get(iA).mStartT);
- fullFinish.merge(mActiveTransitions.get(iA).mFinishT);
- }
- fullFinish.apply();
- // Now perform all the finishes.
- mActiveTransitions.remove(activeIdx);
- mOrganizer.finishTransition(transition, wct, wctCB);
- while (activeIdx < mActiveTransitions.size()) {
- if (!mActiveTransitions.get(activeIdx).mMerged) break;
- ActiveTransition merged = mActiveTransitions.remove(activeIdx);
- mOrganizer.finishTransition(merged.mToken, null /* wct */, null /* wctCB */);
- }
- if (mActiveTransitions.size() <= activeIdx) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "All active transition animations "
- + "finished");
- return;
- }
- // Start animating the next active transition
- final ActiveTransition next = mActiveTransitions.get(activeIdx);
- if (next.mInfo == null) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Pending transition after one"
- + " finished, but it isn't ready yet.");
- return;
- }
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Pending transitions after one"
- + " finished, so start the next one.");
- playTransition(next);
- // Now try to merge the rest of the transitions (re-acquire activeIdx since next may have
- // finished immediately)
- activeIdx = findActiveTransition(next.mToken);
- if (activeIdx < 0) {
- // This means 'next' finished immediately and thus re-entered this function. Since
- // that is the case, just return here since all relevant logic has already run in the
- // re-entered call.
- return;
- }
- // This logic is also convoluted because 'next' may finish immediately in response to any of
- // the merge requests (eg. if it decided to "cancel" itself).
- int mergeIdx = activeIdx + 1;
- while (mergeIdx < mActiveTransitions.size()) {
- ActiveTransition mergeCandidate = mActiveTransitions.get(mergeIdx);
- if (mergeCandidate.mMerged) {
- throw new IllegalStateException("Can't merge a transition after not-merging"
- + " a preceding one.");
- }
- attemptMergeTransition(next, mergeCandidate);
- mergeIdx = findActiveTransition(mergeCandidate.mToken);
- if (mergeIdx < 0) {
- // This means 'next' finished immediately and thus re-entered this function. Since
- // that is the case, just return here since all relevant logic has already run in
- // the re-entered call.
- return;
- }
- ++mergeIdx;
- }
- }
- void requestStartTransition(@NonNull IBinder transitionToken,
- @Nullable TransitionRequestInfo request) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition requested: %s %s",
- transitionToken, request);
- if (findActiveTransition(transitionToken) >= 0) {
- throw new RuntimeException("Transition already started " + transitionToken);
- }
- final ActiveTransition active = new ActiveTransition();
- WindowContainerTransaction wct = null;
- for (int i = mHandlers.size() - 1; i >= 0; --i) {
- wct = mHandlers.get(i).handleRequest(transitionToken, request);
- if (wct != null) {
- active.mHandler = mHandlers.get(i);
- break;
- }
- }
- active.mToken = mOrganizer.startTransition(
- request.getType(), transitionToken, wct);
- mActiveTransitions.add(active);
- }
- /** Start a new transition directly. */
- public IBinder startTransition(@WindowManager.TransitionType int type,
- @NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
- final ActiveTransition active = new ActiveTransition();
- active.mHandler = handler;
- active.mToken = mOrganizer.startTransition(type, null /* token */, wct);
- mActiveTransitions.add(active);
- return active.mToken;
- }
- /**
- * Interface for a callback that must be called after a TransitionHandler finishes playing an
- * animation.
- */
- public interface TransitionFinishCallback {
- /**
- * This must be called on the main thread when a transition finishes playing an animation.
- * The transition must not touch the surfaces after this has been called.
- *
- * @param wct A WindowContainerTransaction to run along with the transition clean-up.
- * @param wctCB A sync callback that will be run when the transition clean-up is done and
- * wct has been applied.
- */
- void onTransitionFinished(@Nullable WindowContainerTransaction wct,
- @Nullable WindowContainerTransactionCallback wctCB);
- }
- /**
- * Interface for something which can handle a subset of transitions.
- */
- public interface TransitionHandler {
- /**
- * Starts a transition animation. This is always called if handleRequest returned non-null
- * for a particular transition. Otherwise, it is only called if no other handler before
- * it handled the transition.
- *
- * @param finishCallback Call this when finished. This MUST be called on main thread.
- * @return true if transition was handled, false if not (falls-back to default).
- */
- boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t,
- @NonNull TransitionFinishCallback finishCallback);
- /**
- * Attempts to merge a different transition's animation into an animation that this handler
- * is currently playing. If a merge is not possible/supported, this should be a no-op.
- *
- * This gets called if another transition becomes ready while this handler is still playing
- * an animation. This is called regardless of whether this handler claims to support that
- * particular transition or not.
- *
- * When this happens, there are 2 options:
- * 1. Do nothing. This effectively rejects the merge request. This is the "safest" option.
- * 2. Merge the incoming transition into this one. The implementation is up to this
- * handler. To indicate that this handler has "consumed" the merge transition, it
- * must call the finishCallback immediately, or at-least before the original
- * transition's finishCallback is called.
- *
- * @param transition This is the transition that wants to be merged.
- * @param info Information about what is changing in the transition.
- * @param t Contains surface changes that resulted from the transition.
- * @param mergeTarget This is the transition that we are attempting to merge with (ie. the
- * one this handler is currently already animating).
- * @param finishCallback Call this if merged. This MUST be called on main thread.
- */
- default void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
- @NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
- @NonNull TransitionFinishCallback finishCallback) { }
- /**
- * Potentially handles a startTransition request.
- *
- * @param transition The transition whose start is being requested.
- * @param request Information about what is requested.
- * @return WCT to apply with transition-start or null. If a WCT is returned here, this
- * handler will be the first in line to animate.
- */
- @Nullable
- WindowContainerTransaction handleRequest(@NonNull IBinder transition,
- @NonNull TransitionRequestInfo request);
- /**
- * Called when a transition which was already "claimed" by this handler has been merged
- * into another animation. Gives this handler a chance to clean-up any expectations.
- */
- default void onTransitionMerged(@NonNull IBinder transition) { }
- /**
- * Sets transition animation scale settings value to handler.
- *
- * @param scale The setting value of transition animation scale.
- */
- default void setAnimScaleSetting(float scale) {}
- }
- @BinderThread
- private class TransitionPlayerImpl extends ITransitionPlayer.Stub {
- @Override
- public void onTransitionReady(IBinder iBinder, TransitionInfo transitionInfo,
- SurfaceControl.Transaction t, SurfaceControl.Transaction finishT)
- throws RemoteException {
- mMainExecutor.execute(() -> Transitions.this.onTransitionReady(
- iBinder, transitionInfo, t, finishT));
- }
- @Override
- public void requestStartTransition(IBinder iBinder,
- TransitionRequestInfo request) throws RemoteException {
- mMainExecutor.execute(() -> Transitions.this.requestStartTransition(iBinder, request));
- }
- }
- /**
- * The interface for calls from outside the Shell, within the host process.
- */
- @ExternalThread
- private class ShellTransitionImpl implements ShellTransitions {
- private IShellTransitionsImpl mIShellTransitions;
- @Override
- public IShellTransitions createExternalInterface() {
- if (mIShellTransitions != null) {
- mIShellTransitions.invalidate();
- }
- mIShellTransitions = new IShellTransitionsImpl(Transitions.this);
- return mIShellTransitions;
- }
- @Override
- public void registerRemote(@NonNull TransitionFilter filter,
- @NonNull IRemoteTransition remoteTransition) {
- mMainExecutor.execute(() -> {
- mRemoteTransitionHandler.addFiltered(filter, remoteTransition);
- });
- }
- @Override
- public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) {
- mMainExecutor.execute(() -> {
- mRemoteTransitionHandler.removeFiltered(remoteTransition);
- });
- }
- }
- /**
- * The interface for calls from outside the host process.
- */
- @BinderThread
- private static class IShellTransitionsImpl extends IShellTransitions.Stub {
- private Transitions mTransitions;
- IShellTransitionsImpl(Transitions transitions) {
- mTransitions = transitions;
- }
- /**
- * Invalidates this instance, preventing future calls from updating the controller.
- */
- void invalidate() {
- mTransitions = null;
- }
- @Override
- public void registerRemote(@NonNull TransitionFilter filter,
- @NonNull IRemoteTransition remoteTransition) {
- executeRemoteCallWithTaskPermission(mTransitions, "registerRemote",
- (transitions) -> {
- transitions.mRemoteTransitionHandler.addFiltered(filter, remoteTransition);
- });
- }
- @Override
- public void unregisterRemote(@NonNull IRemoteTransition remoteTransition) {
- executeRemoteCallWithTaskPermission(mTransitions, "unregisterRemote",
- (transitions) -> {
- transitions.mRemoteTransitionHandler.removeFiltered(remoteTransition);
- });
- }
- }
- private class SettingsObserver extends ContentObserver {
- SettingsObserver() {
- super(null);
- }
- @Override
- public void onChange(boolean selfChange) {
- super.onChange(selfChange);
- mTransitionAnimationScaleSetting = Settings.Global.getFloat(
- mContext.getContentResolver(), Settings.Global.TRANSITION_ANIMATION_SCALE,
- mTransitionAnimationScaleSetting);
- mMainExecutor.execute(() -> dispatchAnimScaleSetting(mTransitionAnimationScaleSetting));
- }
- }
- }