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

Commit f64bb7c1 authored by Luca Zuccarini's avatar Luca Zuccarini
Browse files

3.4 Update ActivityStarterInternal implementations.

They now branch to use the new ActivityTransitionAnimator APIs when
the flag is enabled.

Trying to make each of the CLs as small as possible to keep them
digestible and low risk. For the refactor plan see
go/animlib-shell-refactor-plan.

Bug: 397180418
Flag: com.android.systemui.animation_library_shell_migration
Test: atest ActivityStarterInternalImplTest LegacyActivityStarterInternalImplTest + manual
Change-Id: Ic04a5e67f9e8774ce8898cd3dc623efad9c84f64
parent ab161f7f
Loading
Loading
Loading
Loading
+84 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.systemui.statusbar.phone

import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityTransitionAnimator
import kotlin.test.assertEquals
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever

@SmallTest
@RunWith(AndroidJUnit4::class)
class ActivityStarterUtilsTest : SysuiTestCase() {
    @Mock private lateinit var controller: ActivityTransitionAnimator.Controller

    @Before
    fun setUp() {
        MockitoAnnotations.openMocks(this)
    }

    @Test
    fun addCookieIfNeeded_addsACookie() {
        // GIVEN a controller with no cookie.
        whenever(controller.transitionCookie).thenReturn(null)

        // WHEN addCookieIfNeeded() is called with the given controller.
        val controllerWithCookie = addCookieIfNeeded(controller)

        // THEN the returned controller has a new cookie.
        assertNotNull(controllerWithCookie)
        assertNotNull(controllerWithCookie.transitionCookie)
        assertEquals(
            ActivityTransitionAnimator.TransitionCookie("$controller"),
            controllerWithCookie.transitionCookie,
        )
    }

    @Test
    fun addCookieIfNeeded_doesNotAddACookie_ifControllerIsNull() {
        // WHEN addCookieIfNeeded() is called with a null controller.
        val controllerWithCookie = addCookieIfNeeded(null)

        // THEN the returned controller is also null.
        assertNull(controllerWithCookie)
    }

    @Test
    fun addCookieIfNeeded_doesNotAddACookie_ifControllerAlreadyHasOne() {
        // GIVEN a controller with a cookie
        val cookie = mock<ActivityTransitionAnimator.TransitionCookie>()
        whenever(controller.transitionCookie).thenReturn(cookie)

        // WHEN addCookieIfNeeded() is called with the given controller.
        val controllerWithCookie = addCookieIfNeeded(controller)

        // THEN the returned controller has the same cookie.
        assertNotNull(controllerWithCookie)
        assertNotNull(controllerWithCookie.transitionCookie)
        assertEquals(controller.transitionCookie, controllerWithCookie.transitionCookie)
    }
}
+337 −1

File changed.

Preview size limit exceeded, changes collapsed.

+122 −45

File changed.

Preview size limit exceeded, changes collapsed.

+81 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.systemui.statusbar.phone

import android.annotation.SuppressLint
import android.app.ActivityOptions
import android.os.Bundle
import android.os.IBinder
import android.window.RemoteTransition
import android.window.SplashScreen
import com.android.systemui.animation.ActivityTransitionAnimator
import com.android.systemui.animation.DelegateTransitionAnimatorController

/**
 * Returns an [ActivityOptions] bundle created using the given parameters.
 *
 * @param displayId The ID of the display to launch the activity in. Typically this would be the
 *   display the status bar is on.
 * @param transition The animation driver used to start this activity, or null for the default
 *   animation.
 * @param cookie The launch cookie associated with this activity, or null. Only used if [transition]
 *   is also not null.
 */
fun createActivityOptions(displayId: Int, transition: RemoteTransition?, cookie: IBinder?): Bundle {
    return createDefaultActivityOptions(transition, cookie)
        .apply {
            launchDisplayId = displayId
            callerDisplayId = displayId
            isPendingIntentBackgroundActivityLaunchAllowed = true
        }
        .toBundle()
}

@SuppressLint("MissingPermission")
private fun createDefaultActivityOptions(
    transition: RemoteTransition?,
    cookie: IBinder?,
): ActivityOptions {
    val options =
        if (transition != null) {
            ActivityOptions.makeRemoteTransition(transition).apply { launchCookie = cookie }
        } else {
            ActivityOptions.makeBasic()
        }
    options.splashScreenStyle = SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR
    return options
}

/**
 * If [controller] is not null and does not already have a transition cookie, this function
 * generates a new unique cookie and returns a new [ActivityTransitionAnimator.Controller] with it
 * based on [controller]. Otherwise it just returns [controller] unchanged.
 */
fun addCookieIfNeeded(
    controller: ActivityTransitionAnimator.Controller?
): ActivityTransitionAnimator.Controller? {
    return if (controller?.transitionCookie != null) {
        controller
    } else if (controller != null) {
        object : DelegateTransitionAnimatorController(controller) {
            override val transitionCookie =
                ActivityTransitionAnimator.TransitionCookie("$controller")
        }
    } else {
        null
    }
}
+122 −46

File changed.

Preview size limit exceeded, changes collapsed.