Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d9509f2e authored by Ming-Shin Lu's avatar Ming-Shin Lu
Browse files

Fix a transition issue when starting an activity with CLEAR_TOP flag

If the app hooks the back key event to start the previous activity
with FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
flags in order to clear the top activity and bring the exist previous
activity to front,

The activity transition should playing the close animation
instead the initiated open animation made by startActivity.

Fix: 306166200
Test: atest ShellTransitionTests#\
         testCloseTransitAnimationWhenClosingChangesExists
Change-Id: Ib287c224368827e87c7ec280980e4662368cee6f
parent 7a0ccd8b
Loading
Loading
Loading
Loading
+3 −1
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.app.ActivityOptions.ANIM_CUSTOM;


import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_NONE;
import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;
import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;
import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionTypeFromInfo;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
@@ -253,7 +254,8 @@ class ActivityEmbeddingAnimationSpec {


    private boolean shouldShowBackdrop(@NonNull TransitionInfo info,
    private boolean shouldShowBackdrop(@NonNull TransitionInfo info,
            @NonNull TransitionInfo.Change change) {
            @NonNull TransitionInfo.Change change) {
        final Animation a = loadAttributeAnimation(info, change, WALLPAPER_TRANSITION_NONE,
        final int type = getTransitionTypeFromInfo(info);
        final Animation a = loadAttributeAnimation(type, info, change, WALLPAPER_TRANSITION_NONE,
                mTransitionAnimation, false);
                mTransitionAnimation, false);
        return a != null && a.getShowBackdrop();
        return a != null && a.getShowBackdrop();
    }
    }
+7 −6
Original line number Original line Diff line number Diff line
@@ -60,6 +60,7 @@ import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITI
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN;
import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITION_OPEN;
import static com.android.wm.shell.transition.TransitionAnimationHelper.edgeExtendWindow;
import static com.android.wm.shell.transition.TransitionAnimationHelper.edgeExtendWindow;
import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionBackgroundColorIfSet;
import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionBackgroundColorIfSet;
import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionTypeFromInfo;
import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;
import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;


import android.animation.Animator;
import android.animation.Animator;
@@ -424,7 +425,8 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
            // Don't animate anything that isn't independent.
            // Don't animate anything that isn't independent.
            if (!TransitionInfo.isIndependent(change, info)) continue;
            if (!TransitionInfo.isIndependent(change, info)) continue;


            Animation a = loadAnimation(info, change, wallpaperTransit, isDreamTransition);
            final int type = getTransitionTypeFromInfo(info);
            Animation a = loadAnimation(type, info, change, wallpaperTransit, isDreamTransition);
            if (a != null) {
            if (a != null) {
                if (isTask) {
                if (isTask) {
                    final boolean isTranslucent = (change.getFlags() & FLAG_TRANSLUCENT) != 0;
                    final boolean isTranslucent = (change.getFlags() & FLAG_TRANSLUCENT) != 0;
@@ -660,12 +662,11 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
    }
    }


    @Nullable
    @Nullable
    private Animation loadAnimation(@NonNull TransitionInfo info,
    private Animation loadAnimation(@WindowManager.TransitionType int type,
            @NonNull TransitionInfo.Change change, int wallpaperTransit,
            @NonNull TransitionInfo info, @NonNull TransitionInfo.Change change,
            boolean isDreamTransition) {
            int wallpaperTransit, boolean isDreamTransition) {
        Animation a;
        Animation a;


        final int type = info.getType();
        final int flags = info.getFlags();
        final int flags = info.getFlags();
        final int changeMode = change.getMode();
        final int changeMode = change.getMode();
        final int changeFlags = change.getFlags();
        final int changeFlags = change.getFlags();
@@ -721,7 +722,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
            return null;
            return null;
        } else {
        } else {
            a = loadAttributeAnimation(
            a = loadAttributeAnimation(
                    info, change, wallpaperTransit, mTransitionAnimation, isDreamTransition);
                    type, info, change, wallpaperTransit, mTransitionAnimation, isDreamTransition);
        }
        }


        if (a != null) {
        if (a != null) {
+36 −2
Original line number Original line Diff line number Diff line
@@ -24,6 +24,7 @@ import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.transitTypeToString;
import static android.view.WindowManager.transitTypeToString;
import static android.window.TransitionInfo.FLAGS_IS_NON_APP_WINDOW;
import static android.window.TransitionInfo.FLAGS_IS_NON_APP_WINDOW;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;


@@ -45,6 +46,7 @@ import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.Shader;
import android.view.Surface;
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.view.animation.Transformation;
import android.window.ScreenCapture;
import android.window.ScreenCapture;
@@ -61,10 +63,10 @@ public class TransitionAnimationHelper {


    /** Loads the animation that is defined through attribute id for the given transition. */
    /** Loads the animation that is defined through attribute id for the given transition. */
    @Nullable
    @Nullable
    public static Animation loadAttributeAnimation(@NonNull TransitionInfo info,
    public static Animation loadAttributeAnimation(@WindowManager.TransitionType int type,
            @NonNull TransitionInfo info,
            @NonNull TransitionInfo.Change change, int wallpaperTransit,
            @NonNull TransitionInfo.Change change, int wallpaperTransit,
            @NonNull TransitionAnimation transitionAnimation, boolean isDreamTransition) {
            @NonNull TransitionAnimation transitionAnimation, boolean isDreamTransition) {
        final int type = info.getType();
        final int changeMode = change.getMode();
        final int changeMode = change.getMode();
        final int changeFlags = change.getFlags();
        final int changeFlags = change.getFlags();
        final boolean enter = TransitionUtil.isOpeningType(changeMode);
        final boolean enter = TransitionUtil.isOpeningType(changeMode);
@@ -186,6 +188,38 @@ public class TransitionAnimationHelper {
        return options.getCustomActivityTransition(isOpen);
        return options.getCustomActivityTransition(isOpen);
    }
    }


    /**
     * Gets the final transition type from {@link TransitionInfo} for determining the animation.
     */
    public static int getTransitionTypeFromInfo(@NonNull TransitionInfo info) {
        final int type = info.getType();
        // If the info transition type is opening transition, iterate its changes to see if it
        // has any opening change, if none, returns TRANSIT_CLOSE type for closing animation.
        if (type == TRANSIT_OPEN) {
            boolean hasOpenTransit = false;
            for (TransitionInfo.Change change : info.getChanges()) {
                if ((change.getTaskInfo() != null || change.hasFlags(FLAG_IS_DISPLAY))
                        && !TransitionUtil.isOrderOnly(change)) {
                    // This isn't an activity-level transition.
                    return type;
                }
                if (change.getTaskInfo() != null
                        && change.hasFlags(FLAG_IS_DISPLAY | FLAGS_IS_NON_APP_WINDOW)) {
                    // Ignore non-activity containers.
                    continue;
                }
                if (change.getMode() == TRANSIT_OPEN) {
                    hasOpenTransit = true;
                    break;
                }
            }
            if (!hasOpenTransit) {
                return TRANSIT_CLOSE;
            }
        }
        return type;
    }

    static Animation loadCustomActivityTransition(
    static Animation loadCustomActivityTransition(
            @NonNull TransitionInfo.AnimationOptions.CustomActivityTransition transitionAnim,
            @NonNull TransitionInfo.AnimationOptions.CustomActivityTransition transitionAnim,
            TransitionInfo.AnimationOptions options, boolean enter,
            TransitionInfo.AnimationOptions options, boolean enter,
+41 −0
Original line number Original line Diff line number Diff line
@@ -40,6 +40,8 @@ import static android.window.TransitionInfo.FLAG_SYNC;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
import static android.window.TransitionInfo.FLAG_TRANSLUCENT;


import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.wm.shell.transition.TransitionAnimationHelper.getTransitionTypeFromInfo;


import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertFalse;
@@ -93,6 +95,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.platform.app.InstrumentationRegistry;


import com.android.internal.R;
import com.android.internal.policy.TransitionAnimation;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.ShellTestCase;
import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.TestShellExecutor;
@@ -1463,6 +1467,43 @@ public class ShellTransitionTests extends ShellTestCase {
        assertEquals(0, mDefaultHandler.activeCount());
        assertEquals(0, mDefaultHandler.activeCount());
    }
    }


    @Test
    public void testCloseTransitAnimationWhenClosingChangesExists() {
        Transitions transitions = createTestTransitions();
        Transitions.TransitionObserver observer = mock(Transitions.TransitionObserver.class);
        transitions.registerObserver(observer);
        transitions.replaceDefaultHandlerForTest(mDefaultHandler);
        final TransitionAnimation transitionAnimation = new TransitionAnimation(mContext, false,
                Transitions.TAG);
        spyOn(transitionAnimation);

        // Creating a transition by the app hooking the back key event to start the
        // previous activity with FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
        // flags in order to clear the top activity and bring the exist previous activity to front.
        // Expects the activity transition should playing the close animation instead the initiated
        // open animation made by startActivity.
        IBinder transitToken = new Binder();
        transitions.requestStartTransition(transitToken,
                new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */));
        TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
                .addChange(TRANSIT_CLOSE).addChange(TRANSIT_TO_FRONT).build();
        transitions.onTransitionReady(transitToken, info, new StubTransaction(),
                new StubTransaction());

        final int type = getTransitionTypeFromInfo(info);
        assertEquals(TRANSIT_CLOSE, type);

        TransitionAnimationHelper.loadAttributeAnimation(type, info, info.getChanges().get(0), 0,
                transitionAnimation, false);
        verify(transitionAnimation).loadDefaultAnimationAttr(
                eq(R.styleable.WindowAnimation_activityCloseExitAnimation), anyBoolean());

        TransitionAnimationHelper.loadAttributeAnimation(type, info, info.getChanges().get(1), 0,
                transitionAnimation, false);
        verify(transitionAnimation).loadDefaultAnimationAttr(
                eq(R.styleable.WindowAnimation_activityCloseEnterAnimation), anyBoolean());
    }

    class ChangeBuilder {
    class ChangeBuilder {
        final TransitionInfo.Change mChange;
        final TransitionInfo.Change mChange;