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

Commit 7f73178c authored by Gustav Sennton's avatar Gustav Sennton
Browse files

Update default freeform animations for (un)minimize transitions

Cases handled by this CL:
- TRANSIT_OPEN/TRANSIT_TO_FRONT transition + TRANSIT_TO_BACK Change =
    minimize the Task in the Change
- TRANSIT_TO_FRONT transition + TRANSIT_TO_FRONT Change =
    unminimize the Task in the Change

Bug: 349285733
Flag: com.android.window.flags.enable_desktop_windowing_mode
Test: minimize a Desktop app
Test: unminimize a non-hotseat app through alt-tab
Test: unminimize a non-hotseat app through taskbar click

Change-Id: I93816398cc20992f1365fae101a7f77f1bfb0820
parent 002dd68f
Loading
Loading
Loading
Loading
+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.
     */
    private 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;
    }
}
+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