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

Commit 1f51823f authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes I53e4d9ef,I93816398 into main

* changes:
  Windowing: Reparent minimizing-change to keep task behind others
  Update default freeform animations for (un)minimize transitions
parents e2dd9b2a 848dac4f
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECI
import static android.view.WindowManager.TRANSIT_CHANGE;
import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
import static android.view.WindowManager.TRANSIT_RELAUNCH;
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_OWNER_THUMBNAIL;
import static android.window.TransitionInfo.FLAG_CROSS_PROFILE_WORK_THUMBNAIL;
import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS;
@@ -63,6 +64,7 @@ import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITI
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.getTransitionTypeFromInfo;
import static com.android.wm.shell.transition.TransitionAnimationHelper.isCoveredByOpaqueFullscreenChange;
import static com.android.wm.shell.transition.TransitionAnimationHelper.loadAttributeAnimation;

import android.animation.Animator;
@@ -353,6 +355,7 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
                continue;
            }
            final boolean isTask = change.getTaskInfo() != null;
            final boolean isFreeform = isTask && change.getTaskInfo().isFreeform();
            final int mode = change.getMode();
            boolean isSeamlessDisplayChange = false;

@@ -459,6 +462,16 @@ public class DefaultTransitionHandler implements Transitions.TransitionHandler {
                            final int layer = zSplitLine + numChanges - i;
                            startTransaction.setLayer(change.getLeash(), layer);
                        }
                    } else if (!isCoveredByOpaqueFullscreenChange(info, change)
                            && isFreeform
                            && TransitionUtil.isOpeningMode(type)
                            && change.getMode() == TRANSIT_TO_BACK) {
                        // Reparent the minimize-change to the root task so the minimizing Task
                        // isn't shown in front of other Tasks.
                        mRootTDAOrganizer.reparentToDisplayArea(
                                change.getTaskInfo().displayId,
                                change.getLeash(),
                                startTransaction);
                    } else if (isOnlyTranslucent && TransitionUtil.isOpeningType(info.getType())
                                && TransitionUtil.isClosingType(mode)) {
                        // If there is a closing translucent task in an OPENING transition, we will
+43 −0
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import static com.android.internal.policy.TransitionAnimation.WALLPAPER_TRANSITI
import android.annotation.ColorInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WindowConfiguration;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
@@ -72,6 +73,9 @@ public class TransitionAnimationHelper {
        final int changeFlags = change.getFlags();
        final boolean enter = TransitionUtil.isOpeningType(changeMode);
        final boolean isTask = change.getTaskInfo() != null;
        final boolean isFreeform = isTask && change.getTaskInfo().isFreeform();
        final boolean isCoveredByOpaqueFullscreenChange =
                isCoveredByOpaqueFullscreenChange(info, change);
        final TransitionInfo.AnimationOptions options;
        if (Flags.moveAnimationOptionsToChange()) {
            options = change.getAnimationOptions();
@@ -107,6 +111,24 @@ public class TransitionAnimationHelper {
            animAttr = enter
                    ? R.styleable.WindowAnimation_wallpaperCloseEnterAnimation
                    : R.styleable.WindowAnimation_wallpaperCloseExitAnimation;
        } else if (!isCoveredByOpaqueFullscreenChange
                && isFreeform
                && TransitionUtil.isOpeningMode(type)
                && change.getMode() == TRANSIT_TO_BACK) {
            // Set translucent here so TransitionAnimation loads the appropriate animations for
            // translucent activities and tasks later
            translucent = (changeFlags & FLAG_TRANSLUCENT) != 0;
            // The main Task is launching or being brought to front, this Task is being minimized
            animAttr = R.styleable.WindowAnimation_activityCloseExitAnimation;
        } else if (!isCoveredByOpaqueFullscreenChange
                && isFreeform
                && type == TRANSIT_TO_FRONT
                && change.getMode() == TRANSIT_TO_FRONT) {
            // Set translucent here so TransitionAnimation loads the appropriate animations for
            // translucent activities and tasks later
            translucent = (changeFlags & FLAG_TRANSLUCENT) != 0;
            // Bring the minimized Task back to front
            animAttr = R.styleable.WindowAnimation_activityOpenEnterAnimation;
        } else if (type == TRANSIT_OPEN) {
            // We will translucent open animation for translucent activities and tasks. Choose
            // WindowAnimation_activityOpenEnterAnimation and set translucent here, then
@@ -417,4 +439,25 @@ public class TransitionAnimationHelper {

        return edgeExtensionLayer;
    }

    /**
     * Returns whether there is an opaque fullscreen Change positioned in front of the given Change
     * in the given TransitionInfo.
     */
    static boolean isCoveredByOpaqueFullscreenChange(
            TransitionInfo info, TransitionInfo.Change change) {
        // TransitionInfo#getChanges() are ordered from front to back
        for (TransitionInfo.Change coveringChange : info.getChanges()) {
            if (coveringChange == change) {
                return false;
            }
            if ((coveringChange.getFlags() & FLAG_TRANSLUCENT) == 0
                    && coveringChange.getTaskInfo() != null
                    && coveringChange.getTaskInfo().getWindowingMode()
                    == WindowConfiguration.WINDOWING_MODE_FULLSCREEN) {
                return true;
            }
        }
        return false;
    }
}
+73 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.wm.shell.transition;

import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.view.WindowManager.TRANSIT_SLEEP;
@@ -185,6 +186,73 @@ public class DefaultTransitionHandlerTest extends ShellTestCase {
        verify(startT, never()).setColor(any(), any());
    }

    @Test
    public void startAnimation_freeformOpenChange_doesntReparentTask() {
        final TransitionInfo.Change openChange = new ChangeBuilder(TRANSIT_OPEN)
                .setTask(createTaskInfo(
                        /* taskId= */ 1, /* windowingMode= */ WINDOWING_MODE_FULLSCREEN))
                .build();
        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
                .addChange(openChange)
                .build();
        final IBinder token = new Binder();
        final SurfaceControl.Transaction startT = MockTransactionPool.create();
        final SurfaceControl.Transaction finishT = MockTransactionPool.create();

        mTransitionHandler.startAnimation(token, info, startT, finishT,
                mock(Transitions.TransitionFinishCallback.class));

        verify(startT, never()).reparent(any(), any());
    }

    @Test
    public void startAnimation_freeformMinimizeChange_underFullscreenChange_doesntReparentTask() {
        final TransitionInfo.Change openChange = new ChangeBuilder(TRANSIT_OPEN)
                .setTask(createTaskInfo(
                        /* taskId= */ 1, /* windowingMode= */ WINDOWING_MODE_FULLSCREEN))
                .build();
        final TransitionInfo.Change toBackChange = new ChangeBuilder(TRANSIT_TO_BACK)
                .setTask(createTaskInfo(
                        /* taskId= */ 2, /* windowingMode= */ WINDOWING_MODE_FREEFORM))
                .build();
        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
                .addChange(openChange)
                .addChange(toBackChange)
                .build();
        final IBinder token = new Binder();
        final SurfaceControl.Transaction startT = MockTransactionPool.create();
        final SurfaceControl.Transaction finishT = MockTransactionPool.create();

        mTransitionHandler.startAnimation(token, info, startT, finishT,
                mock(Transitions.TransitionFinishCallback.class));

        verify(startT, never()).reparent(any(), any());
    }

    @Test
    public void startAnimation_freeform_minimizeAnimation_reparentsTask() {
        final TransitionInfo.Change openChange = new ChangeBuilder(TRANSIT_OPEN)
                .setTask(createTaskInfo(
                        /* taskId= */ 1, /* windowingMode= */ WINDOWING_MODE_FREEFORM))
                .build();
        final TransitionInfo.Change toBackChange = new ChangeBuilder(TRANSIT_TO_BACK)
                .setTask(createTaskInfo(
                        /* taskId= */ 2, /* windowingMode= */ WINDOWING_MODE_FREEFORM))
                .build();
        final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
                .addChange(openChange)
                .addChange(toBackChange)
                .build();
        final IBinder token = new Binder();
        final SurfaceControl.Transaction startT = MockTransactionPool.create();
        final SurfaceControl.Transaction finishT = MockTransactionPool.create();

        mTransitionHandler.startAnimation(token, info, startT, finishT,
                mock(Transitions.TransitionFinishCallback.class));

        verify(startT).reparent(any(), any());
    }

    private static void mergeSync(Transitions.TransitionHandler handler, IBinder token) {
        handler.mergeAnimation(
                new Binder(),
@@ -195,10 +263,14 @@ public class DefaultTransitionHandlerTest extends ShellTestCase {
    }

    private static RunningTaskInfo createTaskInfo(int taskId) {
        return createTaskInfo(taskId, WINDOWING_MODE_FULLSCREEN);
    }

    private static RunningTaskInfo createTaskInfo(int taskId, int windowingMode) {
        RunningTaskInfo taskInfo = new RunningTaskInfo();
        taskInfo.taskId = taskId;
        taskInfo.topActivityType = ACTIVITY_TYPE_STANDARD;
        taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
        taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
        taskInfo.configuration.windowConfiguration.setActivityType(taskInfo.topActivityType);
        taskInfo.token = mock(WindowContainerToken.class);
        return taskInfo;
+126 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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 android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration
import android.view.WindowManager
import android.window.TransitionInfo
import android.window.TransitionInfo.FLAG_TRANSLUCENT
import com.android.internal.R
import com.android.internal.policy.TransitionAnimation
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
import org.junit.Test
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.verify

class TransitionAnimationHelperTest : ShellTestCase() {

    @Mock
    lateinit var transitionAnimation: TransitionAnimation

    @Test
    fun loadAttributeAnimation_freeform_taskOpen_taskToBackChange_returnsMinimizeAnim() {
        val openChange = ChangeBuilder(WindowManager.TRANSIT_OPEN)
            .setTask(createTaskInfo(WindowConfiguration.WINDOWING_MODE_FREEFORM))
            .build()
        val toBackChange = ChangeBuilder(WindowManager.TRANSIT_TO_BACK)
            .setTask(createTaskInfo(WindowConfiguration.WINDOWING_MODE_FREEFORM))
            .build()
        val info = TransitionInfoBuilder(WindowManager.TRANSIT_OPEN)
            .addChange(openChange)
            .addChange(toBackChange)
            .build()

        loadAttributeAnimation(WindowManager.TRANSIT_OPEN, info, toBackChange)

        verify(transitionAnimation).loadDefaultAnimationAttr(
            eq(R.styleable.WindowAnimation_activityCloseExitAnimation), anyBoolean())
    }

    @Test
    fun loadAttributeAnimation_freeform_taskToFront_taskToFrontChange_returnsUnminimizeAnim() {
        val toFrontChange = ChangeBuilder(WindowManager.TRANSIT_TO_FRONT)
            .setTask(createTaskInfo(WindowConfiguration.WINDOWING_MODE_FREEFORM))
            .build()
        val info = TransitionInfoBuilder(WindowManager.TRANSIT_TO_FRONT)
            .addChange(toFrontChange)
            .build()

        loadAttributeAnimation(WindowManager.TRANSIT_TO_FRONT, info, toFrontChange)

        verify(transitionAnimation).loadDefaultAnimationAttr(
            eq(R.styleable.WindowAnimation_activityOpenEnterAnimation),
            /* translucent= */ anyBoolean())
    }

    @Test
    fun loadAttributeAnimation_fullscreen_taskOpen_returnsTaskOpenEnterAnim() {
        val openChange = ChangeBuilder(WindowManager.TRANSIT_OPEN)
            .setTask(createTaskInfo(WindowConfiguration.WINDOWING_MODE_FULLSCREEN))
            .build()
        val info = TransitionInfoBuilder(WindowManager.TRANSIT_OPEN).addChange(openChange).build()

        loadAttributeAnimation(WindowManager.TRANSIT_OPEN, info, openChange)

        verify(transitionAnimation).loadDefaultAnimationAttr(
            eq(R.styleable.WindowAnimation_taskOpenEnterAnimation),
            /* translucent= */ anyBoolean())
    }

    @Test
    fun loadAttributeAnimation_freeform_taskOpen_taskToBackChange_passesTranslucent() {
        val openChange = ChangeBuilder(WindowManager.TRANSIT_OPEN)
            .setTask(createTaskInfo(WindowConfiguration.WINDOWING_MODE_FREEFORM))
            .build()
        val toBackChange = ChangeBuilder(WindowManager.TRANSIT_TO_BACK)
            .setTask(createTaskInfo(WindowConfiguration.WINDOWING_MODE_FREEFORM))
            .setFlags(FLAG_TRANSLUCENT)
            .build()
        val info = TransitionInfoBuilder(WindowManager.TRANSIT_OPEN)
            .addChange(openChange)
            .addChange(toBackChange)
            .build()

        loadAttributeAnimation(WindowManager.TRANSIT_OPEN, info, toBackChange)

        verify(transitionAnimation).loadDefaultAnimationAttr(
            eq(R.styleable.WindowAnimation_activityCloseExitAnimation),
            /* translucent= */ eq(true))
    }

    private fun loadAttributeAnimation(
        @WindowManager.TransitionType type: Int,
        info: TransitionInfo,
        change: TransitionInfo.Change,
        wallpaperTransit: Int = TransitionAnimation.WALLPAPER_TRANSITION_NONE,
        isDreamTransition: Boolean = false,
    ) {
        TransitionAnimationHelper.loadAttributeAnimation(
            type, info, change, wallpaperTransit, transitionAnimation, isDreamTransition)
    }

    private fun createTaskInfo(windowingMode: Int): RunningTaskInfo {
        val taskInfo = TestRunningTaskInfoBuilder()
            .setWindowingMode(windowingMode)
            .build()
        return taskInfo
    }
}
 No newline at end of file