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

Commit 5a784415 authored by Darrell Shi's avatar Darrell Shi
Browse files

Fix hub to edit mode transition

Previously while transitioning from hub to edit mode, the communal scene
stays on as keyguard transitions to GONE. This was to coordiate a
smooth transition between the two states. But this breaks the state
syncing between communal scene layout and KTF.

This change makes it so that KTF does not immediately transition to
GONE, but let the CommunalSceneTransitionInteractor handle it when the
communal scene is ready to transition away.

Test: atest FromPrimaryBouncerTransitionInteractorTest
Test: atest FromAlternateBouncerTransitionInteractorTest
Test: manually tested the transition between hub and edit mode
Test: b/411422599 does not reproduce
Fix: 411422599
Flag: com.android.systemui.hub_edit_mode_transition
Change-Id: Ib4b3be1504d352a26f2ac220722fe1c3be157394
parent c2d71886
Loading
Loading
Loading
Loading
+10 −0
Original line number Original line Diff line number Diff line
@@ -2070,3 +2070,13 @@ flag {
        purpose: PURPOSE_BUGFIX
        purpose: PURPOSE_BUGFIX
    }
    }
}
}

flag {
   name: "hub_edit_mode_transition"
   namespace: "systemui"
   description: "Fix hub to edit mode transition issues"
   bug: "411422599"
   metadata {
     purpose: PURPOSE_BUGFIX
   }
}
+57 −0
Original line number Original line Diff line number Diff line
@@ -45,6 +45,7 @@ import com.android.systemui.communal.domain.interactor.communalInteractor
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.EditModeState
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -61,6 +62,7 @@ import com.android.systemui.power.shared.model.WakeSleepReason
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.testKosmos
import com.android.systemui.testKosmos
import com.google.common.truth.Truth
import com.google.common.truth.Truth
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.advanceTimeBy
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.runTest
@@ -71,6 +73,7 @@ import org.mockito.Mockito.reset
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
import platform.test.runner.parameterized.Parameters


@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
@RunWith(ParameterizedAndroidJunit4::class)
class FromAlternateBouncerTransitionInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
class FromAlternateBouncerTransitionInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@@ -148,6 +151,60 @@ class FromAlternateBouncerTransitionInteractorTest(flags: FlagsParameterization)
                .startedTransition(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE)
                .startedTransition(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE)
        }
        }


    @Test
    @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR, Flags.FLAG_HUB_EDIT_MODE_TRANSITION)
    @DisableSceneContainer
    fun transitionToGone_whenEnteringHubEditMode_flagOff_transitionToGone() =
        kosmos.runTest {
            transitionRepository.transitionTo(
                from = KeyguardState.LOCKSCREEN,
                to = KeyguardState.ALTERNATE_BOUNCER,
            )
            reset(transitionRepository)

            fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(null)
            fakeKeyguardRepository.setKeyguardOccluded(true)
            runCurrent()
            assertThat(transitionRepository).noTransitionsStarted()

            communalSceneInteractor.setEditModeState(EditModeState.STARTING)

            fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(true)
            runCurrent()
            fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(null)
            runCurrent()

            assertThat(transitionRepository)
                .startedTransition(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE)
        }

    @Test
    @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR)
    @EnableFlags(Flags.FLAG_HUB_EDIT_MODE_TRANSITION)
    @DisableSceneContainer
    fun transitionToGone_whenEnteringHubEditMode_flagOn_doNothing() =
        kosmos.runTest {
            transitionRepository.transitionTo(
                from = KeyguardState.LOCKSCREEN,
                to = KeyguardState.ALTERNATE_BOUNCER,
            )
            reset(transitionRepository)

            fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(null)
            fakeKeyguardRepository.setKeyguardOccluded(true)
            runCurrent()
            assertThat(transitionRepository).noTransitionsStarted()

            communalSceneInteractor.setEditModeState(EditModeState.STARTING)

            fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(true)
            runCurrent()
            fakeKeyguardBouncerRepository.setKeyguardAuthenticatedBiometrics(null)
            runCurrent()

            assertThat(transitionRepository).noTransitionsStarted()
        }

    @Test
    @Test
    fun noTransition_keyguardNotOccluded_biometricAuthenticated() =
    fun noTransition_keyguardNotOccluded_biometricAuthenticated() =
        testScope.runTest {
        testScope.runTest {
+46 −0
Original line number Original line Diff line number Diff line
@@ -16,11 +16,13 @@


package com.android.systemui.keyguard.domain.interactor
package com.android.systemui.keyguard.domain.interactor


import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.annotations.EnableFlags
import android.platform.test.flag.junit.FlagsParameterization
import android.platform.test.flag.junit.FlagsParameterization
import androidx.test.filters.SmallTest
import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.Flags.FLAG_GLANCEABLE_HUB_V2
import com.android.systemui.Flags.FLAG_HUB_EDIT_MODE_TRANSITION
import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
import com.android.systemui.Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR
import com.android.systemui.SysuiTestCase
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository
@@ -29,6 +31,7 @@ import com.android.systemui.communal.domain.interactor.communalSceneInteractor
import com.android.systemui.communal.domain.interactor.setCommunalV2Available
import com.android.systemui.communal.domain.interactor.setCommunalV2Available
import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
import com.android.systemui.communal.domain.interactor.setCommunalV2ConfigEnabled
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.EditModeState
import com.android.systemui.coroutines.collectValues
import com.android.systemui.coroutines.collectValues
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
@@ -48,6 +51,7 @@ import com.android.systemui.power.domain.interactor.powerInteractor
import com.android.systemui.testKosmos
import com.android.systemui.testKosmos
import com.google.common.truth.Truth
import com.google.common.truth.Truth
import junit.framework.Assert.assertEquals
import junit.framework.Assert.assertEquals
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.test.runTest
@@ -58,6 +62,7 @@ import org.mockito.Mockito.reset
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.ParameterizedAndroidJunit4
import platform.test.runner.parameterized.Parameters
import platform.test.runner.parameterized.Parameters


@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@SmallTest
@RunWith(ParameterizedAndroidJunit4::class)
@RunWith(ParameterizedAndroidJunit4::class)
class FromPrimaryBouncerTransitionInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
class FromPrimaryBouncerTransitionInteractorTest(flags: FlagsParameterization) : SysuiTestCase() {
@@ -217,6 +222,47 @@ class FromPrimaryBouncerTransitionInteractorTest(flags: FlagsParameterization) :
                )
                )
        }
        }


    @Test
    @EnableFlags(FLAG_HUB_EDIT_MODE_TRANSITION)
    @DisableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
    @DisableSceneContainer
    fun testPrimaryBouncerToGone_whenEnteringHubEditMode_flagOn_doNothing() =
        kosmos.runTest {
            underTest.start()

            transitionRepository.transitionTo(
                from = KeyguardState.LOCKSCREEN,
                to = KeyguardState.PRIMARY_BOUNCER,
            )
            runCurrent()

            reset(transitionRepository)
            communalSceneInteractor.setEditModeState(EditModeState.STARTING)
            fakeKeyguardRepository.setKeyguardGoingAway(true)
            runCurrent()

            assertThat(transitionRepository).noTransitionsStarted()
        }

    @Test
    @DisableFlags(FLAG_HUB_EDIT_MODE_TRANSITION, FLAG_KEYGUARD_WM_STATE_REFACTOR)
    @DisableSceneContainer
    fun testPrimaryBouncerToGone_whenEnteringHubEditMode_flagOff_transitionToGone() =
        kosmos.runTest {
            underTest.start()

            transitionRepository.transitionTo(
                from = KeyguardState.LOCKSCREEN,
                to = KeyguardState.PRIMARY_BOUNCER,
            )
            communalSceneInteractor.setEditModeState(EditModeState.STARTING)
            fakeKeyguardRepository.setKeyguardGoingAway(true)
            runCurrent()

            assertThat(transitionRepository)
                .startedTransition(from = KeyguardState.PRIMARY_BOUNCER, to = KeyguardState.GONE)
        }

    @Test
    @Test
    @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
    @EnableFlags(FLAG_KEYGUARD_WM_STATE_REFACTOR)
    @DisableSceneContainer
    @DisableSceneContainer
+9 −8
Original line number Original line Diff line number Diff line
@@ -59,7 +59,6 @@ import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onEach
@@ -118,7 +117,9 @@ constructor(
            ),
            ),
            communalInteractor.editModeOpen,
            communalInteractor.editModeOpen,
        )
        )
            .filter { it }

    /** Emits when the hub has transitioned out, and edit mode is ready to transition in. */
    val hubTransitionOut = canShowEditMode


    // Only widgets are editable.
    // Only widgets are editable.
    override val communalContent: Flow<List<CommunalContentModel>> =
    override val communalContent: Flow<List<CommunalContentModel>> =
+45 −23
Original line number Original line Diff line number Diff line
@@ -36,6 +36,7 @@ import androidx.lifecycle.lifecycleScope
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.theme.PlatformTheme
import com.android.compose.theme.PlatformTheme
import com.android.internal.logging.UiEventLogger
import com.android.internal.logging.UiEventLogger
import com.android.systemui.Flags
import com.android.systemui.Flags.communalEditWidgetsActivityFinishFix
import com.android.systemui.Flags.communalEditWidgetsActivityFinishFix
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.communal.shared.log.CommunalUiEvent
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.communal.shared.model.CommunalScenes
@@ -53,6 +54,7 @@ import com.android.systemui.log.dagger.CommunalLog
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.settings.UserTracker
import com.android.systemui.settings.UserTracker
import javax.inject.Inject
import javax.inject.Inject
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.first


/** An Activity for editing the widgets that appear in hub mode. */
/** An Activity for editing the widgets that appear in hub mode. */
@@ -176,6 +178,9 @@ constructor(
        if (communalEditWidgetsActivityFinishFix()) ActivityControllerImpl(this)
        if (communalEditWidgetsActivityFinishFix()) ActivityControllerImpl(this)
        else NopActivityController()
        else NopActivityController()


    // Completes when the activity UI is rendered and ready for the hub to edit mode transition.
    private val readyDeferred = CompletableDeferred<Unit>()

    override fun onCreate(savedInstanceState: Bundle?) {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        super.onCreate(savedInstanceState)


@@ -212,7 +217,14 @@ constructor(
    // Handle scene change to show the activity and animate in its content
    // Handle scene change to show the activity and animate in its content
    private fun listenForTransitionAndChangeScene() {
    private fun listenForTransitionAndChangeScene() {
        lifecycleScope.launch {
        lifecycleScope.launch {
            communalViewModel.canShowEditMode.collect {
            if (Flags.hubEditModeTransition()) {
                // Wait for the edit mode activity to be ready underneath the hub before starting
                // the hub to edit mode transition.
                readyDeferred.await()
            } else {
                communalViewModel.canShowEditMode.first { it }
            }

            if (!SceneContainerFlag.isEnabled) {
            if (!SceneContainerFlag.isEnabled) {
                communalViewModel.changeScene(
                communalViewModel.changeScene(
                    scene = CommunalScenes.Blank,
                    scene = CommunalScenes.Blank,
@@ -220,11 +232,16 @@ constructor(
                    transitionKey = CommunalTransitionKeys.ToEditMode,
                    transitionKey = CommunalTransitionKeys.ToEditMode,
                    keyguardState = KeyguardState.GONE,
                    keyguardState = KeyguardState.GONE,
                )
                )
                    // wait till transitioned to Blank scene, then animate in communal content in

                    // edit mode
                // Wait for scene change to BLANK.
                communalViewModel.currentScene.first { it == CommunalScenes.Blank }
                communalViewModel.currentScene.first { it == CommunalScenes.Blank }
            }
            }


            if (Flags.hubEditModeTransition()) {
                // Wait for hub to fully transition out.
                communalViewModel.hubTransitionOut.first { it }
            }

            // Wait for dream to exit, if we were previously dreaming.
            // Wait for dream to exit, if we were previously dreaming.
            keyguardInteractor.isDreaming.first { !it }
            keyguardInteractor.isDreaming.first { !it }


@@ -241,7 +258,6 @@ constructor(
            }
            }
        }
        }
    }
    }
    }


    private fun onOpenWidgetPicker() {
    private fun onOpenWidgetPicker() {
        lifecycleScope.launch {
        lifecycleScope.launch {
@@ -348,6 +364,12 @@ constructor(
        uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_EDIT_MODE_SHOWN)
        uiEventLogger.log(CommunalUiEvent.COMMUNAL_HUB_EDIT_MODE_SHOWN)
    }
    }


    override fun onResume() {
        super.onResume()

        readyDeferred.complete(Unit)
    }

    override fun onStop() {
    override fun onStop() {
        super.onStop()
        super.onStop()
        communalViewModel.setEditActivityShowing(false)
        communalViewModel.setEditActivityShowing(false)
Loading