Loading core/java/com/android/internal/policy/TransitionAnimation.java +28 −0 Original line number Diff line number Diff line Loading @@ -180,6 +180,7 @@ public class TransitionAnimation { "android", com.android.internal.R.anim.cross_profile_apps_thumbnail_enter); } /** Load animation by resource Id from specific LayoutParams. */ @Nullable private Animation loadAnimationRes(LayoutParams lp, int resId) { Context context = mContext; Loading @@ -193,6 +194,7 @@ public class TransitionAnimation { return null; } /** Load animation by resource Id from specific package. */ @Nullable private Animation loadAnimationRes(String packageName, int resId) { if (ResourceId.isValid(resId)) { Loading @@ -204,6 +206,13 @@ public class TransitionAnimation { return null; } /** Load animation by resource Id from android package. */ @Nullable public Animation loadDefaultAnimationRes(int resId) { return loadAnimationRes("android", resId); } /** Load animation by attribute Id from specific LayoutParams */ @Nullable public Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) { int resId = Resources.ID_NULL; Loading @@ -222,6 +231,25 @@ public class TransitionAnimation { return null; } /** Load animation by attribute Id from android package. */ @Nullable public Animation loadDefaultAnimationAttr(int animAttr) { int resId = Resources.ID_NULL; Context context = mContext; if (animAttr >= 0) { AttributeCache.Entry ent = getCachedAnimations("android", mDefaultWindowAnimationStyleResId); if (ent != null) { context = ent.context; resId = ent.array.getResourceId(animAttr, 0); } } if (ResourceId.isValid(resId)) { return loadAnimationSafely(context, resId, mTag); } return null; } @Nullable private AttributeCache.Entry getCachedAnimations(LayoutParams lp) { if (mDebug) { Loading libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +114 −34 Original line number Diff line number Diff line Loading @@ -16,55 +16,78 @@ 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_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.graphics.Rect; import android.os.IBinder; import android.util.ArrayMap; import android.view.Choreographer; import android.view.SurfaceControl; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.Transformation; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; import com.android.internal.R; import com.android.internal.policy.AttributeCache; import com.android.internal.policy.TransitionAnimation; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.protolog.ShellProtoLogGroup; import java.util.ArrayList; /** The default handler that handles anything not already handled. */ public class DefaultTransitionHandler implements Transitions.TransitionHandler { private static final int MAX_ANIMATION_DURATION = 3000; private final TransactionPool mTransactionPool; private final ShellExecutor mMainExecutor; private final ShellExecutor mAnimExecutor; private final TransitionAnimation mTransitionAnimation; /** Keeps track of the currently-running animations associated with each transition. */ private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>(); DefaultTransitionHandler(@NonNull TransactionPool transactionPool, private float mTransitionAnimationScaleSetting = 1.0f; DefaultTransitionHandler(@NonNull TransactionPool transactionPool, Context context, @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) { mTransactionPool = transactionPool; mMainExecutor = mainExecutor; mAnimExecutor = animExecutor; mTransitionAnimation = new TransitionAnimation(context, false /* debug */, Transitions.TAG); AttributeCache.init(context); } @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull Transitions.TransitionFinishCallback finishCallback) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "start default transition animation, info = %s", info); if (mAnimations.containsKey(transition)) { throw new IllegalStateException("Got a duplicate startAnimation call for " + transition); } final ArrayList<Animator> animations = new ArrayList<>(); mAnimations.put(transition, animations); final boolean isOpening = Transitions.isOpeningType(info.getType()); final Runnable onAnimFinish = () -> { if (!animations.isEmpty()) return; Loading @@ -77,19 +100,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { // Don't animate anything with an animating parent if (change.getParent() != null) continue; final int mode = change.getMode(); if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { // This received a transferred starting window, so don't animate continue; } // fade in startExampleAnimation( animations, change.getLeash(), true /* show */, onAnimFinish); } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) { // fade out startExampleAnimation( animations, change.getLeash(), false /* show */, onAnimFinish); Animation a = loadAnimation(info.getType(), change); if (a != null) { startAnimInternal(animations, a, change.getLeash(), onAnimFinish); } } t.apply(); Loading @@ -105,32 +118,93 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { return null; } // TODO(shell-transitions): real animations private void startExampleAnimation(@NonNull ArrayList<Animator> animations, @NonNull SurfaceControl leash, boolean show, @NonNull Runnable finishCallback) { final float end = show ? 1.f : 0.f; final float start = 1.f - end; @Override public void setAnimScaleSetting(float scale) { mTransitionAnimationScaleSetting = scale; } @Nullable private Animation loadAnimation(int type, TransitionInfo.Change change) { // TODO(b/178678389): It should handle more type animation here Animation a = null; final boolean isOpening = Transitions.isOpeningType(type); final int mode = change.getMode(); final int flags = change.getFlags(); if (mode == TRANSIT_OPEN && isOpening) { if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { // This received a transferred starting window, so don't animate return null; } if (change.getTaskInfo() != null) { a = mTransitionAnimation.loadDefaultAnimationAttr( R.styleable.WindowAnimation_taskOpenEnterAnimation); } else { a = mTransitionAnimation.loadDefaultAnimationRes((flags & FLAG_TRANSLUCENT) == 0 ? R.anim.activity_open_enter : R.anim.activity_translucent_open_enter); } } else if (mode == TRANSIT_TO_FRONT && isOpening) { if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { // This received a transferred starting window, so don't animate return null; } a = mTransitionAnimation.loadDefaultAnimationAttr( R.styleable.WindowAnimation_taskToFrontEnterAnimation); } else if (mode == TRANSIT_CLOSE && !isOpening) { if (change.getTaskInfo() != null) { a = mTransitionAnimation.loadDefaultAnimationAttr( R.styleable.WindowAnimation_taskCloseExitAnimation); } else { a = mTransitionAnimation.loadDefaultAnimationRes((flags & FLAG_TRANSLUCENT) == 0 ? R.anim.activity_close_exit : R.anim.activity_translucent_close_exit); } } else if (mode == TRANSIT_TO_BACK && !isOpening) { a = mTransitionAnimation.loadDefaultAnimationAttr( R.styleable.WindowAnimation_taskToBackExitAnimation); } else if (mode == TRANSIT_CHANGE) { // In the absence of a specific adapter, we just want to keep everything stationary. a = new AlphaAnimation(1.f, 1.f); a.setDuration(TransitionAnimation.DEFAULT_APP_TRANSITION_DURATION); } if (a != null) { Rect start = change.getStartAbsBounds(); Rect end = change.getEndAbsBounds(); a.restrictDuration(MAX_ANIMATION_DURATION); a.initialize(end.width(), end.height(), start.width(), start.height()); a.scaleCurrentDuration(mTransitionAnimationScaleSetting); } return a; } private void startAnimInternal(@NonNull ArrayList<Animator> animations, @NonNull Animation anim, @NonNull SurfaceControl leash, @NonNull Runnable finishCallback) { final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); final ValueAnimator va = ValueAnimator.ofFloat(start, end); va.setDuration(500); final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f); final Transformation transformation = new Transformation(); final float[] matrix = new float[9]; // Animation length is already expected to be scaled. va.overrideDurationScale(1.0f); va.setDuration(anim.computeDurationHint()); va.addUpdateListener(animation -> { float fraction = animation.getAnimatedFraction(); transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction); transaction.apply(); final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime()); applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix); }); final Runnable finisher = () -> { transaction.setAlpha(leash, end); transaction.apply(); applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix); mTransactionPool.release(transaction); mMainExecutor.execute(() -> { animations.remove(va); finishCallback.run(); }); }; va.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } va.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { finisher.run(); Loading @@ -140,11 +214,17 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { public void onAnimationCancel(Animator animation) { finisher.run(); } @Override public void onAnimationRepeat(Animator animation) { } }); animations.add(va); mAnimExecutor.execute(va::start); } private static void applyTransformation(long time, SurfaceControl.Transaction t, SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix) { anim.getTransformation(time, transformation); t.setMatrix(leash, transformation.getMatrix(), matrix); t.setAlpha(leash, transformation.getAlpha()); t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId()); t.apply(); } } libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +54 −2 Original line number Diff line number Diff line Loading @@ -25,9 +25,13 @@ import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPI 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.ArrayMap; import android.util.Log; import android.view.SurfaceControl; Loading @@ -43,6 +47,7 @@ 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; Loading @@ -63,6 +68,7 @@ public class Transitions { SystemProperties.getBoolean("persist.debug.shell_transit", false); private final WindowOrganizer mOrganizer; private final Context mContext; private final ShellExecutor mMainExecutor; private final ShellExecutor mAnimExecutor; private final TransitionPlayerImpl mPlayerImpl; Loading @@ -72,6 +78,8 @@ public class Transitions { /** 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 { TransitionHandler mFirstHandler = null; } Loading @@ -84,26 +92,46 @@ public class Transitions { } public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool, @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) { @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, mainExecutor, animExecutor)); 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; } 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 RemoteTransitions createEmptyForTesting() { Loading Loading @@ -368,6 +396,13 @@ public class Transitions { @Nullable WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request); /** * Sets transition animation scale settings value to handler. * * @param scale The setting value of transition animation scale. */ default void setAnimScaleSetting(float scale) {} } @BinderThread Loading Loading @@ -404,4 +439,21 @@ public class Transitions { }); } } 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)); } } } libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +12 −8 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; Loading @@ -58,6 +59,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.ShellExecutor; Loading @@ -78,6 +80,8 @@ public class ShellTransitionTests { private final WindowOrganizer mOrganizer = mock(WindowOrganizer.class); private final TransactionPool mTransactionPool = mock(TransactionPool.class); private final Context mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); private final TestShellExecutor mMainExecutor = new TestShellExecutor(); private final ShellExecutor mAnimExecutor = new TestShellExecutor(); private final TestTransitionHandler mDefaultHandler = new TestTransitionHandler(); Loading @@ -90,8 +94,8 @@ public class ShellTransitionTests { @Test public void testBasicTransitionFlow() { Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, mAnimExecutor); Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext, mMainExecutor, mAnimExecutor); transitions.replaceDefaultHandlerForTest(mDefaultHandler); IBinder transitToken = new Binder(); Loading @@ -109,8 +113,8 @@ public class ShellTransitionTests { @Test public void testNonDefaultHandler() { Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, mAnimExecutor); Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext, mMainExecutor, mAnimExecutor); transitions.replaceDefaultHandlerForTest(mDefaultHandler); final WindowContainerTransaction handlerWCT = new WindowContainerTransaction(); Loading Loading @@ -188,8 +192,8 @@ public class ShellTransitionTests { @Test public void testRequestRemoteTransition() { Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, mAnimExecutor); Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext, mMainExecutor, mAnimExecutor); transitions.replaceDefaultHandlerForTest(mDefaultHandler); final boolean[] remoteCalled = new boolean[]{false}; Loading Loading @@ -255,8 +259,8 @@ public class ShellTransitionTests { @Test public void testRegisteredRemoteTransition() { Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, mAnimExecutor); Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext, mMainExecutor, mAnimExecutor); transitions.replaceDefaultHandlerForTest(mDefaultHandler); final boolean[] remoteCalled = new boolean[]{false}; Loading packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +2 −2 Original line number Diff line number Diff line Loading @@ -380,9 +380,9 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool, @ShellMainThread ShellExecutor mainExecutor, Context context, @ShellMainThread ShellExecutor mainExecutor, @ShellAnimationThread ShellExecutor animExecutor) { return new Transitions(organizer, pool, mainExecutor, animExecutor); return new Transitions(organizer, pool, context, mainExecutor, animExecutor); } // Loading Loading
core/java/com/android/internal/policy/TransitionAnimation.java +28 −0 Original line number Diff line number Diff line Loading @@ -180,6 +180,7 @@ public class TransitionAnimation { "android", com.android.internal.R.anim.cross_profile_apps_thumbnail_enter); } /** Load animation by resource Id from specific LayoutParams. */ @Nullable private Animation loadAnimationRes(LayoutParams lp, int resId) { Context context = mContext; Loading @@ -193,6 +194,7 @@ public class TransitionAnimation { return null; } /** Load animation by resource Id from specific package. */ @Nullable private Animation loadAnimationRes(String packageName, int resId) { if (ResourceId.isValid(resId)) { Loading @@ -204,6 +206,13 @@ public class TransitionAnimation { return null; } /** Load animation by resource Id from android package. */ @Nullable public Animation loadDefaultAnimationRes(int resId) { return loadAnimationRes("android", resId); } /** Load animation by attribute Id from specific LayoutParams */ @Nullable public Animation loadAnimationAttr(LayoutParams lp, int animAttr, int transit) { int resId = Resources.ID_NULL; Loading @@ -222,6 +231,25 @@ public class TransitionAnimation { return null; } /** Load animation by attribute Id from android package. */ @Nullable public Animation loadDefaultAnimationAttr(int animAttr) { int resId = Resources.ID_NULL; Context context = mContext; if (animAttr >= 0) { AttributeCache.Entry ent = getCachedAnimations("android", mDefaultWindowAnimationStyleResId); if (ent != null) { context = ent.context; resId = ent.array.getResourceId(animAttr, 0); } } if (ResourceId.isValid(resId)) { return loadAnimationSafely(context, resId, mTag); } return null; } @Nullable private AttributeCache.Entry getCachedAnimations(LayoutParams lp) { if (mDebug) { Loading
libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java +114 −34 Original line number Diff line number Diff line Loading @@ -16,55 +16,78 @@ 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_OPEN; import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ValueAnimator; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.graphics.Rect; import android.os.IBinder; import android.util.ArrayMap; import android.view.Choreographer; import android.view.SurfaceControl; import android.view.animation.AlphaAnimation; import android.view.animation.Animation; import android.view.animation.Transformation; import android.window.TransitionInfo; import android.window.TransitionRequestInfo; import android.window.WindowContainerTransaction; import com.android.internal.R; import com.android.internal.policy.AttributeCache; import com.android.internal.policy.TransitionAnimation; import com.android.internal.protolog.common.ProtoLog; import com.android.wm.shell.common.ShellExecutor; import com.android.wm.shell.common.TransactionPool; import com.android.wm.shell.protolog.ShellProtoLogGroup; import java.util.ArrayList; /** The default handler that handles anything not already handled. */ public class DefaultTransitionHandler implements Transitions.TransitionHandler { private static final int MAX_ANIMATION_DURATION = 3000; private final TransactionPool mTransactionPool; private final ShellExecutor mMainExecutor; private final ShellExecutor mAnimExecutor; private final TransitionAnimation mTransitionAnimation; /** Keeps track of the currently-running animations associated with each transition. */ private final ArrayMap<IBinder, ArrayList<Animator>> mAnimations = new ArrayMap<>(); DefaultTransitionHandler(@NonNull TransactionPool transactionPool, private float mTransitionAnimationScaleSetting = 1.0f; DefaultTransitionHandler(@NonNull TransactionPool transactionPool, Context context, @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) { mTransactionPool = transactionPool; mMainExecutor = mainExecutor; mAnimExecutor = animExecutor; mTransitionAnimation = new TransitionAnimation(context, false /* debug */, Transitions.TAG); AttributeCache.init(context); } @Override public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info, @NonNull SurfaceControl.Transaction t, @NonNull Transitions.TransitionFinishCallback finishCallback) { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "start default transition animation, info = %s", info); if (mAnimations.containsKey(transition)) { throw new IllegalStateException("Got a duplicate startAnimation call for " + transition); } final ArrayList<Animator> animations = new ArrayList<>(); mAnimations.put(transition, animations); final boolean isOpening = Transitions.isOpeningType(info.getType()); final Runnable onAnimFinish = () -> { if (!animations.isEmpty()) return; Loading @@ -77,19 +100,9 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { // Don't animate anything with an animating parent if (change.getParent() != null) continue; final int mode = change.getMode(); if (isOpening && (mode == TRANSIT_OPEN || mode == TRANSIT_TO_FRONT)) { if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { // This received a transferred starting window, so don't animate continue; } // fade in startExampleAnimation( animations, change.getLeash(), true /* show */, onAnimFinish); } else if (!isOpening && (mode == TRANSIT_CLOSE || mode == TRANSIT_TO_BACK)) { // fade out startExampleAnimation( animations, change.getLeash(), false /* show */, onAnimFinish); Animation a = loadAnimation(info.getType(), change); if (a != null) { startAnimInternal(animations, a, change.getLeash(), onAnimFinish); } } t.apply(); Loading @@ -105,32 +118,93 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { return null; } // TODO(shell-transitions): real animations private void startExampleAnimation(@NonNull ArrayList<Animator> animations, @NonNull SurfaceControl leash, boolean show, @NonNull Runnable finishCallback) { final float end = show ? 1.f : 0.f; final float start = 1.f - end; @Override public void setAnimScaleSetting(float scale) { mTransitionAnimationScaleSetting = scale; } @Nullable private Animation loadAnimation(int type, TransitionInfo.Change change) { // TODO(b/178678389): It should handle more type animation here Animation a = null; final boolean isOpening = Transitions.isOpeningType(type); final int mode = change.getMode(); final int flags = change.getFlags(); if (mode == TRANSIT_OPEN && isOpening) { if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { // This received a transferred starting window, so don't animate return null; } if (change.getTaskInfo() != null) { a = mTransitionAnimation.loadDefaultAnimationAttr( R.styleable.WindowAnimation_taskOpenEnterAnimation); } else { a = mTransitionAnimation.loadDefaultAnimationRes((flags & FLAG_TRANSLUCENT) == 0 ? R.anim.activity_open_enter : R.anim.activity_translucent_open_enter); } } else if (mode == TRANSIT_TO_FRONT && isOpening) { if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) { // This received a transferred starting window, so don't animate return null; } a = mTransitionAnimation.loadDefaultAnimationAttr( R.styleable.WindowAnimation_taskToFrontEnterAnimation); } else if (mode == TRANSIT_CLOSE && !isOpening) { if (change.getTaskInfo() != null) { a = mTransitionAnimation.loadDefaultAnimationAttr( R.styleable.WindowAnimation_taskCloseExitAnimation); } else { a = mTransitionAnimation.loadDefaultAnimationRes((flags & FLAG_TRANSLUCENT) == 0 ? R.anim.activity_close_exit : R.anim.activity_translucent_close_exit); } } else if (mode == TRANSIT_TO_BACK && !isOpening) { a = mTransitionAnimation.loadDefaultAnimationAttr( R.styleable.WindowAnimation_taskToBackExitAnimation); } else if (mode == TRANSIT_CHANGE) { // In the absence of a specific adapter, we just want to keep everything stationary. a = new AlphaAnimation(1.f, 1.f); a.setDuration(TransitionAnimation.DEFAULT_APP_TRANSITION_DURATION); } if (a != null) { Rect start = change.getStartAbsBounds(); Rect end = change.getEndAbsBounds(); a.restrictDuration(MAX_ANIMATION_DURATION); a.initialize(end.width(), end.height(), start.width(), start.height()); a.scaleCurrentDuration(mTransitionAnimationScaleSetting); } return a; } private void startAnimInternal(@NonNull ArrayList<Animator> animations, @NonNull Animation anim, @NonNull SurfaceControl leash, @NonNull Runnable finishCallback) { final SurfaceControl.Transaction transaction = mTransactionPool.acquire(); final ValueAnimator va = ValueAnimator.ofFloat(start, end); va.setDuration(500); final ValueAnimator va = ValueAnimator.ofFloat(0f, 1f); final Transformation transformation = new Transformation(); final float[] matrix = new float[9]; // Animation length is already expected to be scaled. va.overrideDurationScale(1.0f); va.setDuration(anim.computeDurationHint()); va.addUpdateListener(animation -> { float fraction = animation.getAnimatedFraction(); transaction.setAlpha(leash, start * (1.f - fraction) + end * fraction); transaction.apply(); final long currentPlayTime = Math.min(va.getDuration(), va.getCurrentPlayTime()); applyTransformation(currentPlayTime, transaction, leash, anim, transformation, matrix); }); final Runnable finisher = () -> { transaction.setAlpha(leash, end); transaction.apply(); applyTransformation(va.getDuration(), transaction, leash, anim, transformation, matrix); mTransactionPool.release(transaction); mMainExecutor.execute(() -> { animations.remove(va); finishCallback.run(); }); }; va.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } va.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { finisher.run(); Loading @@ -140,11 +214,17 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler { public void onAnimationCancel(Animator animation) { finisher.run(); } @Override public void onAnimationRepeat(Animator animation) { } }); animations.add(va); mAnimExecutor.execute(va::start); } private static void applyTransformation(long time, SurfaceControl.Transaction t, SurfaceControl leash, Animation anim, Transformation transformation, float[] matrix) { anim.getTransformation(time, transformation); t.setMatrix(leash, transformation.getMatrix(), matrix); t.setAlpha(leash, transformation.getAlpha()); t.setFrameTimelineVsync(Choreographer.getInstance().getVsyncId()); t.apply(); } }
libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +54 −2 Original line number Diff line number Diff line Loading @@ -25,9 +25,13 @@ import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPI 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.ArrayMap; import android.util.Log; import android.view.SurfaceControl; Loading @@ -43,6 +47,7 @@ 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; Loading @@ -63,6 +68,7 @@ public class Transitions { SystemProperties.getBoolean("persist.debug.shell_transit", false); private final WindowOrganizer mOrganizer; private final Context mContext; private final ShellExecutor mMainExecutor; private final ShellExecutor mAnimExecutor; private final TransitionPlayerImpl mPlayerImpl; Loading @@ -72,6 +78,8 @@ public class Transitions { /** 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 { TransitionHandler mFirstHandler = null; } Loading @@ -84,26 +92,46 @@ public class Transitions { } public Transitions(@NonNull WindowOrganizer organizer, @NonNull TransactionPool pool, @NonNull ShellExecutor mainExecutor, @NonNull ShellExecutor animExecutor) { @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, mainExecutor, animExecutor)); 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; } 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 RemoteTransitions createEmptyForTesting() { Loading Loading @@ -368,6 +396,13 @@ public class Transitions { @Nullable WindowContainerTransaction handleRequest(@NonNull IBinder transition, @NonNull TransitionRequestInfo request); /** * Sets transition animation scale settings value to handler. * * @param scale The setting value of transition animation scale. */ default void setAnimScaleSetting(float scale) {} } @BinderThread Loading Loading @@ -404,4 +439,21 @@ public class Transitions { }); } } 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)); } } }
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +12 −8 Original line number Diff line number Diff line Loading @@ -41,6 +41,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.ActivityManager.RunningTaskInfo; import android.content.Context; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; Loading @@ -58,6 +59,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import com.android.wm.shell.TestShellExecutor; import com.android.wm.shell.common.ShellExecutor; Loading @@ -78,6 +80,8 @@ public class ShellTransitionTests { private final WindowOrganizer mOrganizer = mock(WindowOrganizer.class); private final TransactionPool mTransactionPool = mock(TransactionPool.class); private final Context mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); private final TestShellExecutor mMainExecutor = new TestShellExecutor(); private final ShellExecutor mAnimExecutor = new TestShellExecutor(); private final TestTransitionHandler mDefaultHandler = new TestTransitionHandler(); Loading @@ -90,8 +94,8 @@ public class ShellTransitionTests { @Test public void testBasicTransitionFlow() { Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, mAnimExecutor); Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext, mMainExecutor, mAnimExecutor); transitions.replaceDefaultHandlerForTest(mDefaultHandler); IBinder transitToken = new Binder(); Loading @@ -109,8 +113,8 @@ public class ShellTransitionTests { @Test public void testNonDefaultHandler() { Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, mAnimExecutor); Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext, mMainExecutor, mAnimExecutor); transitions.replaceDefaultHandlerForTest(mDefaultHandler); final WindowContainerTransaction handlerWCT = new WindowContainerTransaction(); Loading Loading @@ -188,8 +192,8 @@ public class ShellTransitionTests { @Test public void testRequestRemoteTransition() { Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, mAnimExecutor); Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext, mMainExecutor, mAnimExecutor); transitions.replaceDefaultHandlerForTest(mDefaultHandler); final boolean[] remoteCalled = new boolean[]{false}; Loading Loading @@ -255,8 +259,8 @@ public class ShellTransitionTests { @Test public void testRegisteredRemoteTransition() { Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mMainExecutor, mAnimExecutor); Transitions transitions = new Transitions(mOrganizer, mTransactionPool, mContext, mMainExecutor, mAnimExecutor); transitions.replaceDefaultHandlerForTest(mDefaultHandler); final boolean[] remoteCalled = new boolean[]{false}; Loading
packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java +2 −2 Original line number Diff line number Diff line Loading @@ -380,9 +380,9 @@ public abstract class WMShellBaseModule { @WMSingleton @Provides static Transitions provideTransitions(ShellTaskOrganizer organizer, TransactionPool pool, @ShellMainThread ShellExecutor mainExecutor, Context context, @ShellMainThread ShellExecutor mainExecutor, @ShellAnimationThread ShellExecutor animExecutor) { return new Transitions(organizer, pool, mainExecutor, animExecutor); return new Transitions(organizer, pool, context, mainExecutor, animExecutor); } // Loading