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

Commit 5419b31e authored by Sergey Pinkevich's avatar Sergey Pinkevich
Browse files

Check if the display is desktop first for Notification shade launch

Bug: 441574781
Flag: com.android.systemui.shade_app_launch_animation_skip_in_desktop
Test: atest com.android.systemui.statusbar.phone.LegacyActivityStarterInternalImplTest com.android.systemui.animation.ActivityTransitionAnimatorTest com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarterTest

Change-Id: I759e162752bbfbc2969d861b45bd2c98b795d1f4
parent 859372e2
Loading
Loading
Loading
Loading
+143 −4
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@ import com.android.systemui.animation.LaunchableView
import com.android.systemui.assist.AssistManager
import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor
import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.desktop.DesktopFirstRepository
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.kosmos.testScope
@@ -118,11 +119,14 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
    @Mock private lateinit var perDisplaySysUiStateRepository: PerDisplayRepository<SysUiState>
    @Mock private lateinit var sysUIState: SysUiState
    @Mock private lateinit var remoteAnimationAdapter: RemoteAnimationAdapter
    @Mock private lateinit var mDesktopFirstRepository: DesktopFirstRepository
    private lateinit var underTest: LegacyActivityStarterInternalImpl
    private val kosmos = testKosmos()
    private val mainExecutor = FakeExecutor(FakeSystemClock())
    private val shadeAnimationInteractor =
        ShadeAnimationInteractorLegacyImpl(ShadeAnimationRepository(), FakeShadeRepository())
    private val desktopFirstStateForDefaultDisplay = mutableMapOf(DISPLAY_ID to true)
    private val nonDesktopFirstStateForDefaultDisplay = mutableMapOf(DISPLAY_ID to false)

    @Before
    fun setUp() {
@@ -158,12 +162,15 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
                communalSceneInteractor = communalSceneInteractor,
                communalSettingsInteractor = communalSettingsInteractor,
                perDisplaySysUiStateRepository = perDisplaySysUiStateRepository,
                desktopFirstRepository = mDesktopFirstRepository,
            )
        `when`(userTracker.userHandle).thenReturn(UserHandle.OWNER)
        `when`(communalSceneInteractor.isCommunalVisible).thenReturn(MutableStateFlow(false))
        `when`(communalSceneInteractor.isIdleOnCommunal).thenReturn(MutableStateFlow(false))
        `when`(communalSceneInteractor.isLaunchingWidget).thenReturn(MutableStateFlow(false))
        `when`(shadeDialogContextInteractor.context).thenReturn(context)
        `when`(mDesktopFirstRepository.isDisplayDesktopFirst)
            .thenReturn(nonDesktopFirstStateForDefaultDisplay)
    }

    @Test
@@ -1003,8 +1010,10 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
        Flags.FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP,
    )
    @Test
    fun startActivity_skipAnimInDesktop_flagEnabled_inDesktop_noAnimate() {
    fun startActivity_skipAnimInDesktop_flagEnabled_inDesktop_desktopFirst_noAnimate() {
        setupDesktopMode(enabled = true)
        `when`(mDesktopFirstRepository.isDisplayDesktopFirst)
            .thenReturn(desktopFirstStateForDefaultDisplay)
        val (controller, pendingIntent) = setupLaunchWithOccludedKeyguard()

        underTest.startPendingIntentDismissingKeyguard(
@@ -1028,11 +1037,75 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
            )
    }

    @EnableFlags(
        Flags.FLAG_ANIMATION_LIBRARY_SHELL_MIGRATION,
        Flags.FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP,
    )
    @Test
    fun startActivity_skipAnimInDesktop_flagEnabled_inDesktop_notDesktopFirst_noAnimate() {
        setupDesktopMode(enabled = true)
        `when`(mDesktopFirstRepository.isDisplayDesktopFirst)
            .thenReturn(nonDesktopFirstStateForDefaultDisplay)
        val (controller, pendingIntent) = setupLaunchWithOccludedKeyguard()

        underTest.startPendingIntentDismissingKeyguard(
            intent = pendingIntent,
            dismissShade = true,
            animationController = controller,
            showOverLockscreen = true,
            skipLockscreenChecks = true,
        )
        mainExecutor.runAllReady()

        // Verify animate parameter is false
        verify(activityTransitionAnimator)
            .startPendingIntentWithAnimation(
                nullable(ActivityTransitionAnimator.Controller::class.java),
                eq(kosmos.testScope),
                /* animate */ eq(false),
                eq(false),
                eq(true),
                any(),
            )
    }

    @EnableFlags(Flags.FLAG_ANIMATION_LIBRARY_SHELL_MIGRATION)
    @DisableFlags(Flags.FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP)
    @Test
    fun startActivity_skipAnimInDesktop_flagDisabled_inDesktop_desktopFirst_doAnimate() {
        setupDesktopMode(enabled = true)
        `when`(mDesktopFirstRepository.isDisplayDesktopFirst)
            .thenReturn(desktopFirstStateForDefaultDisplay)
        val (controller, pendingIntent) = setupLaunchWithOccludedKeyguard()

        underTest.startPendingIntentDismissingKeyguard(
            intent = pendingIntent,
            dismissShade = true,
            animationController = controller,
            showOverLockscreen = true,
            skipLockscreenChecks = true,
        )
        mainExecutor.runAllReady()

        // Verify animate parameter is true
        verify(activityTransitionAnimator)
            .startPendingIntentWithAnimation(
                nullable(ActivityTransitionAnimator.Controller::class.java),
                eq(kosmos.testScope),
                /* animate */ eq(true),
                eq(false),
                eq(true),
                any(),
            )
    }

    @EnableFlags(Flags.FLAG_ANIMATION_LIBRARY_SHELL_MIGRATION)
    @DisableFlags(Flags.FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP)
    @Test
    fun startActivity_skipAnimInDesktop_flagDisabled_inDesktop_doAnimate() {
    fun startActivity_skipAnimInDesktop_flagDisabled_inDesktop_noDesktopFirst_doAnimate() {
        setupDesktopMode(enabled = true)
        `when`(mDesktopFirstRepository.isDisplayDesktopFirst)
            .thenReturn(nonDesktopFirstStateForDefaultDisplay)
        val (controller, pendingIntent) = setupLaunchWithOccludedKeyguard()

        underTest.startPendingIntentDismissingKeyguard(
@@ -1061,8 +1134,72 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
        Flags.FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP,
    )
    @Test
    fun startActivity_skipAnimInDesktop_flagEnabled_notInDesktop_doAnimate() {
    fun startActivity_skipAnimInDesktop_flagEnabled_notInDesktop_desktopFirst_notAnimate() {
        setupDesktopMode(enabled = false)
        `when`(mDesktopFirstRepository.isDisplayDesktopFirst)
            .thenReturn(desktopFirstStateForDefaultDisplay)
        val (controller, pendingIntent) = setupLaunchWithOccludedKeyguard()

        underTest.startPendingIntentDismissingKeyguard(
            intent = pendingIntent,
            dismissShade = true,
            animationController = controller,
            showOverLockscreen = true,
            skipLockscreenChecks = true,
        )
        mainExecutor.runAllReady()

        // Verify animate parameter is false
        verify(activityTransitionAnimator)
            .startPendingIntentWithAnimation(
                nullable(ActivityTransitionAnimator.Controller::class.java),
                eq(kosmos.testScope),
                /* animate */ eq(false),
                eq(false),
                eq(true),
                any(),
            )
    }

    @EnableFlags(
        Flags.FLAG_ANIMATION_LIBRARY_SHELL_MIGRATION,
        Flags.FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP,
    )
    @Test
    fun startActivity_skipAnimInDesktop_flagEnabled_notInDesktop_notDesktopFirst_doAnimate() {
        setupDesktopMode(enabled = false)
        `when`(mDesktopFirstRepository.isDisplayDesktopFirst)
            .thenReturn(nonDesktopFirstStateForDefaultDisplay)
        val (controller, pendingIntent) = setupLaunchWithOccludedKeyguard()

        underTest.startPendingIntentDismissingKeyguard(
            intent = pendingIntent,
            dismissShade = true,
            animationController = controller,
            showOverLockscreen = true,
            skipLockscreenChecks = true,
        )
        mainExecutor.runAllReady()

        // Verify animate parameter is true
        verify(activityTransitionAnimator)
            .startPendingIntentWithAnimation(
                nullable(ActivityTransitionAnimator.Controller::class.java),
                eq(kosmos.testScope),
                /* animate */ eq(true),
                eq(false),
                eq(true),
                any(),
            )
    }

    @EnableFlags(Flags.FLAG_ANIMATION_LIBRARY_SHELL_MIGRATION)
    @DisableFlags(Flags.FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP)
    @Test
    fun startActivity_skipAnimInDesktop_flagDisabled_notInDesktop_desktopFirst_doAnimate() {
        setupDesktopMode(enabled = false)
        `when`(mDesktopFirstRepository.isDisplayDesktopFirst)
            .thenReturn(desktopFirstStateForDefaultDisplay)
        val (controller, pendingIntent) = setupLaunchWithOccludedKeyguard()

        underTest.startPendingIntentDismissingKeyguard(
@@ -1089,8 +1226,10 @@ class LegacyActivityStarterInternalImplTest : SysuiTestCase() {
    @EnableFlags(Flags.FLAG_ANIMATION_LIBRARY_SHELL_MIGRATION)
    @DisableFlags(Flags.FLAG_SHADE_APP_LAUNCH_ANIMATION_SKIP_IN_DESKTOP)
    @Test
    fun startActivity_skipAnimInDesktop_flagDisabled_notInDesktop_doAnimate() {
    fun startActivity_skipAnimInDesktop_flagDisabled_notInDesktop_notDesktopFirst_doAnimate() {
        setupDesktopMode(enabled = false)
        `when`(mDesktopFirstRepository.isDisplayDesktopFirst)
            .thenReturn(nonDesktopFirstStateForDefaultDisplay)
        val (controller, pendingIntent) = setupLaunchWithOccludedKeyguard()

        underTest.startPendingIntentDismissingKeyguard(
+48 −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.desktop

import com.android.systemui.dagger.SysUISingleton
import com.android.wm.shell.desktopmode.DesktopMode
import com.android.wm.shell.shared.desktopmode.DesktopFirstListener
import java.util.Optional
import javax.inject.Inject

/**
 * Repository that tracks whether a display is in "Desktop First" mode.
 *
 * "Desktop First" mode for a display means that newly opened applications on that particular
 * display will launch in [WindowConfiguration.WINDOWING_MODE_FREEFORM].
 *
 * This repository listens to changes from [DesktopMode] and maintains the state for each display
 * ID.
 */
@SysUISingleton
class DesktopFirstRepository @Inject constructor(desktopMode: Optional<DesktopMode>) :
    DesktopFirstListener {

    private val _isDisplayDesktopFirst: MutableMap<Int, Boolean> = mutableMapOf()
    val isDisplayDesktopFirst: Map<Int, Boolean> = _isDisplayDesktopFirst.toMap()

    init {
        desktopMode.ifPresent { desktopMode.get().registerDesktopFirstListener(this) }
    }

    override fun onStateChanged(displayId: Int, isDesktopFirstEnabled: Boolean) {
        _isDisplayDesktopFirst[displayId] = isDesktopFirstEnabled
    }
}
+31 −21
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.desktop.DesktopFirstRepository
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
@@ -107,20 +108,22 @@ constructor(
    private val commandQueue: CommandQueue,
    private val lockScreenUserManager: NotificationLockscreenUserManager,
    private val perDisplaySysUiStateRepository: PerDisplayRepository<SysUiState>,
    private val desktopFirstRepository: DesktopFirstRepository,
) : ActivityStarterInternal {
    private val centralSurfaces: CentralSurfaces?
        get() = centralSurfacesOptLazy.get().getOrNull()

    private val context: Context
    private val currentShadeContext: Context
        get() = contextInteractor.context

    private val displayId: Int
        get() = context.displayId
    private val currentShadeDisplayId: Int
        get() = currentShadeContext.displayId

    private val isInDesktopMode: Boolean
        get() =
            ((perDisplaySysUiStateRepository[displayId]?.flags ?: 0) and
                SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0L
    private val shadeSysUiState: Long
        get() = perDisplaySysUiStateRepository[currentShadeDisplayId]?.flags ?: 0

    private val isInDesktopModeOnCurrentShadeDisplay: Boolean
        get() = (shadeSysUiState and SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0L

    override fun registerTransition(
        cookie: ActivityTransitionAnimator.TransitionCookie,
@@ -228,7 +231,7 @@ constructor(
                )

                intent.sendAndReturnResult(
                    context,
                    currentShadeContext,
                    0,
                    fillInIntent,
                    null,
@@ -253,7 +256,7 @@ constructor(
                                ): Int {
                                    return startIntent(
                                        createActivityOptions(
                                            displayId,
                                            currentShadeDisplayId,
                                            transition,
                                            controllerWithCookie?.transitionCookie,
                                        )
@@ -272,7 +275,10 @@ constructor(
                                animationAdapter: RemoteAnimationAdapter?
                            ): Int {
                                return startIntent(
                                    CentralSurfaces.getActivityOptions(displayId, animationAdapter)
                                    CentralSurfaces.getActivityOptions(
                                        currentShadeDisplayId,
                                        animationAdapter,
                                    )
                                )
                            }
                        },
@@ -421,10 +427,10 @@ constructor(
                    result[0] =
                        activityTaskManager.startActivityAsUser(
                            null,
                            context.basePackageName,
                            context.attributionTag,
                            currentShadeContext.basePackageName,
                            currentShadeContext.attributionTag,
                            intent,
                            intent.resolveTypeIfNeeded(context.contentResolver),
                            intent.resolveTypeIfNeeded(currentShadeContext.contentResolver),
                            null,
                            null,
                            0,
@@ -448,7 +454,7 @@ constructor(
                ) { transition: RemoteTransition? ->
                    startIntent(
                        createActivityOptions(
                            displayId,
                            currentShadeDisplayId,
                            transition,
                            controllerWithCookie?.transitionCookie,
                        )
@@ -460,7 +466,7 @@ constructor(
                    animate,
                    intent.getPackage(),
                ) { adapter: RemoteAnimationAdapter? ->
                    startIntent(CentralSurfaces.getActivityOptions(displayId, adapter))
                    startIntent(CentralSurfaces.getActivityOptions(currentShadeDisplayId, adapter))
                }
            }

@@ -545,11 +551,11 @@ constructor(
                animate = animate,
                showOverLockscreen = showOverLockscreenWhenLocked,
            ) { transition: RemoteTransition? ->
                TaskStackBuilder.create(context)
                TaskStackBuilder.create(currentShadeContext)
                    .addNextIntent(intent)
                    .startActivities(
                        createActivityOptions(
                            displayId,
                            currentShadeDisplayId,
                            transition,
                            controllerWithCookie?.transitionCookie,
                        ),
@@ -563,10 +569,10 @@ constructor(
                intent.getPackage(),
                showOverLockscreenWhenLocked,
            ) { adapter: RemoteAnimationAdapter? ->
                TaskStackBuilder.create(context)
                TaskStackBuilder.create(currentShadeContext)
                    .addNextIntent(intent)
                    .startActivities(
                        CentralSurfaces.getActivityOptions(displayId, adapter),
                        CentralSurfaces.getActivityOptions(currentShadeDisplayId, adapter),
                        userHandle,
                    )
            }
@@ -656,7 +662,11 @@ constructor(
            return false
        }

        if (shadeAppLaunchAnimationSkipInDesktop() && isInDesktopMode) {
        if (
            shadeAppLaunchAnimationSkipInDesktop() &&
                (isInDesktopModeOnCurrentShadeDisplay ||
                    desktopFirstRepository.isDisplayDesktopFirst[currentShadeDisplayId] == true)
        ) {
            return false
        }

@@ -732,7 +742,7 @@ constructor(
                    shadeControllerLazy.get(),
                    notifShadeWindowControllerLazy.get(),
                    commandQueue,
                    displayId,
                    currentShadeDisplayId,
                    isLaunchForActivity,
                )
            }
+33 −22
Original line number Diff line number Diff line
@@ -45,6 +45,7 @@ import com.android.systemui.communal.domain.interactor.CommunalSettingsInteracto
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.desktop.DesktopFirstRepository
import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.model.SysUiState
@@ -103,20 +104,22 @@ constructor(
    private val communalSceneInteractor: CommunalSceneInteractor,
    private val communalSettingsInteractor: CommunalSettingsInteractor,
    private val perDisplaySysUiStateRepository: PerDisplayRepository<SysUiState>,
    private val desktopFirstRepository: DesktopFirstRepository,
) : ActivityStarterInternal {
    private val centralSurfaces: CentralSurfaces?
        get() = centralSurfacesOptLazy.get().getOrNull()

    private val context: Context
    private val currentShadeContext: Context
        get() = contextInteractor.context

    private val displayId: Int
        get() = context.displayId
    private val currentShadeDisplayId: Int
        get() = currentShadeContext.displayId

    private val isInDesktopMode: Boolean
        get() =
            ((perDisplaySysUiStateRepository[displayId]?.flags ?: 0) and
                SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0L
    private val shadeSysUiState: Long
        get() = perDisplaySysUiStateRepository[currentShadeDisplayId]?.flags ?: 0

    private val isInDesktopModeOnCurrentShadeDisplay: Boolean
        get() = (shadeSysUiState and SYSUI_STATE_FREEFORM_ACTIVE_IN_DESKTOP_MODE) != 0L

    override fun registerTransition(
        cookie: ActivityTransitionAnimator.TransitionCookie,
@@ -260,10 +263,10 @@ constructor(
                    result[0] =
                        activityTaskManager.startActivityAsUser(
                            null,
                            context.basePackageName,
                            context.attributionTag,
                            currentShadeContext.basePackageName,
                            currentShadeContext.attributionTag,
                            intent,
                            intent.resolveTypeIfNeeded(context.contentResolver),
                            intent.resolveTypeIfNeeded(currentShadeContext.contentResolver),
                            null,
                            null,
                            0,
@@ -287,7 +290,7 @@ constructor(
                ) { transition: RemoteTransition? ->
                    startIntent(
                        createActivityOptions(
                            displayId,
                            currentShadeDisplayId,
                            transition,
                            controllerWithCookie?.transitionCookie,
                        )
@@ -299,7 +302,7 @@ constructor(
                    animate,
                    intent.getPackage(),
                ) { adapter: RemoteAnimationAdapter? ->
                    startIntent(CentralSurfaces.getActivityOptions(displayId, adapter))
                    startIntent(CentralSurfaces.getActivityOptions(currentShadeDisplayId, adapter))
                }
            }

@@ -399,7 +402,7 @@ constructor(
                )

                intent.sendAndReturnResult(
                    context,
                    currentShadeContext,
                    0,
                    fillInIntent,
                    null,
@@ -424,7 +427,7 @@ constructor(
                                ): Int {
                                    return startIntent(
                                        createActivityOptions(
                                            displayId,
                                            currentShadeDisplayId,
                                            transition,
                                            controllerWithCookie?.transitionCookie,
                                        )
@@ -443,7 +446,10 @@ constructor(
                                animationAdapter: RemoteAnimationAdapter?
                            ): Int {
                                return startIntent(
                                    CentralSurfaces.getActivityOptions(displayId, animationAdapter)
                                    CentralSurfaces.getActivityOptions(
                                        currentShadeDisplayId,
                                        animationAdapter,
                                    )
                                )
                            }
                        },
@@ -540,11 +546,11 @@ constructor(
                animate = animate,
                showOverLockscreen = showOverLockscreenWhenLocked,
            ) { transition: RemoteTransition? ->
                TaskStackBuilder.create(context)
                TaskStackBuilder.create(currentShadeContext)
                    .addNextIntent(intent)
                    .startActivities(
                        createActivityOptions(
                            displayId,
                            currentShadeDisplayId,
                            transition,
                            controllerWithCookie?.transitionCookie,
                        ),
@@ -558,10 +564,10 @@ constructor(
                intent.getPackage(),
                showOverLockscreenWhenLocked,
            ) { adapter: RemoteAnimationAdapter? ->
                TaskStackBuilder.create(context)
                TaskStackBuilder.create(currentShadeContext)
                    .addNextIntent(intent)
                    .startActivities(
                        CentralSurfaces.getActivityOptions(displayId, adapter),
                        CentralSurfaces.getActivityOptions(currentShadeDisplayId, adapter),
                        userHandle,
                    )
            }
@@ -690,7 +696,7 @@ constructor(
                    shadeControllerLazy.get(),
                    notifShadeWindowControllerLazy.get(),
                    commandQueue,
                    displayId,
                    currentShadeDisplayId,
                    isLaunchForActivity,
                )
            }
@@ -767,7 +773,8 @@ constructor(

    /** Retrieves the current user handle to start the Activity. */
    private fun getActivityUserHandle(intent: Intent): UserHandle {
        val packages: Array<String> = context.resources.getStringArray(R.array.system_ui_packages)
        val packages: Array<String> =
            currentShadeContext.resources.getStringArray(R.array.system_ui_packages)
        for (pkg in packages) {
            val componentName = intent.component ?: break
            if (pkg == componentName.packageName) {
@@ -792,7 +799,11 @@ constructor(
            return false
        }

        if (shadeAppLaunchAnimationSkipInDesktop() && isInDesktopMode) {
        if (
            shadeAppLaunchAnimationSkipInDesktop() &&
                (isInDesktopModeOnCurrentShadeDisplay ||
                    desktopFirstRepository.isDisplayDesktopFirst[currentShadeDisplayId] == true)
        ) {
            return false
        }