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

Commit cacd557c authored by William Xiao's avatar William Xiao Committed by Android (Google) Code Review
Browse files

Merge changes I5a85e152,Iaa9f6743 into main

* changes:
  Allow showing hub immediately on view load
  Resolve circular dependency
parents abccf87d b3d5a84a
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -159,8 +159,7 @@ fun CommunalContainer(
    content: CommunalContent,
) {
    val coroutineScope = rememberCoroutineScope()
    val currentSceneKey: SceneKey by
        viewModel.currentScene.collectAsStateWithLifecycle(CommunalScenes.Blank)
    val currentSceneKey: SceneKey by viewModel.currentScene.collectAsStateWithLifecycle()
    val touchesAllowed by viewModel.touchesAllowed.collectAsStateWithLifecycle()
    val backgroundType by
        viewModel.communalBackground.collectAsStateWithLifecycle(
+42 −18
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 The Android Open Source Project
 * 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.
@@ -21,34 +21,44 @@ import androidx.test.filters.SmallTest
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.shared.model.CommunalScenes
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
import com.android.systemui.kosmos.testScope
import com.android.systemui.scene.shared.model.sceneDataSource
import com.android.systemui.kosmos.backgroundScope
import com.android.systemui.kosmos.collectLastValue
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.scene.shared.model.SceneDataSource
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.mock
import org.mockito.kotlin.verify

@SmallTest
@RunWith(AndroidJUnit4::class)
class CommunalRepositoryImplTest : SysuiTestCase() {
class CommunalSceneRepositoryImplTest : SysuiTestCase() {
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()

    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val underTest by lazy {
    private val delegator = mock<SceneDataSourceDelegator> {}

    private val Kosmos.underTest by
        Kosmos.Fixture {
            CommunalSceneRepositoryImpl(
            kosmos.applicationCoroutineScope,
            kosmos.applicationCoroutineScope,
            kosmos.sceneDataSource,
                applicationScope = applicationCoroutineScope,
                backgroundScope = backgroundScope,
                sceneDataSource = delegator,
                delegator = delegator,
            )
        }

    @Test
    fun transitionState_idleByDefault() =
        testScope.runTest {
        kosmos.runTest {
            val transitionState by collectLastValue(underTest.transitionState)
            assertThat(transitionState)
                .isEqualTo(ObservableTransitionState.Idle(CommunalScenes.Default))
@@ -56,7 +66,7 @@ class CommunalRepositoryImplTest : SysuiTestCase() {

    @Test
    fun transitionState_setTransitionState_returnsNewValue() =
        testScope.runTest {
        kosmos.runTest {
            val expectedSceneKey = CommunalScenes.Communal
            underTest.setTransitionState(flowOf(ObservableTransitionState.Idle(expectedSceneKey)))

@@ -66,7 +76,7 @@ class CommunalRepositoryImplTest : SysuiTestCase() {

    @Test
    fun transitionState_setNullTransitionState_returnsDefaultValue() =
        testScope.runTest {
        kosmos.runTest {
            // Set a value for the transition state flow.
            underTest.setTransitionState(
                flowOf(ObservableTransitionState.Idle(CommunalScenes.Communal))
@@ -80,4 +90,18 @@ class CommunalRepositoryImplTest : SysuiTestCase() {
            assertThat(transitionState)
                .isEqualTo(ObservableTransitionState.Idle(CommunalScenes.Default))
        }

    @Test
    fun showHubFromPowerButton() =
        kosmos.runTest {
            fakeKeyguardRepository.setKeyguardShowing(false)

            underTest.showHubFromPowerButton()

            argumentCaptor<SceneDataSource>().apply {
                verify(delegator).setDelegate(capture())

                assertThat(firstValue.currentScene.value).isEqualTo(CommunalScenes.Communal)
            }
        }
}
+3 −3
Original line number Diff line number Diff line
@@ -296,7 +296,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
    private final Provider<JavaAdapter> mJavaAdapter;
    private final Provider<SceneInteractor> mSceneInteractor;
    private final Provider<AlternateBouncerInteractor> mAlternateBouncerInteractor;
    private final CommunalSceneInteractor mCommunalSceneInteractor;
    private final Provider<CommunalSceneInteractor> mCommunalSceneInteractor;
    private final AuthController mAuthController;
    private final UiEventLogger mUiEventLogger;
    private final Set<String> mAllowFingerprintOnOccludingActivitiesFromPackage;
@@ -2210,7 +2210,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
            Provider<AlternateBouncerInteractor> alternateBouncerInteractor,
            Provider<JavaAdapter> javaAdapter,
            Provider<SceneInteractor> sceneInteractor,
            CommunalSceneInteractor communalSceneInteractor) {
            Provider<CommunalSceneInteractor> communalSceneInteractor) {
        mContext = context;
        mSubscriptionManager = subscriptionManager;
        mUserTracker = userTracker;
@@ -2543,7 +2543,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab

        if (glanceableHubV2()) {
            mJavaAdapter.get().alwaysCollectFlow(
                    mCommunalSceneInteractor.isCommunalVisible(),
                    mCommunalSceneInteractor.get().isCommunalVisible(),
                    this::onCommunalShowingChanged
            );
        }
+43 −1
Original line number Diff line number Diff line
@@ -16,7 +16,9 @@

package com.android.systemui.communal.data.repository

import com.android.app.tracing.coroutines.launchTraced as launch
import com.android.compose.animation.scene.ObservableTransitionState
import com.android.compose.animation.scene.OverlayKey
import com.android.compose.animation.scene.SceneKey
import com.android.compose.animation.scene.TransitionKey
import com.android.systemui.communal.dagger.Communal
@@ -25,16 +27,17 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.scene.shared.model.SceneDataSource
import com.android.systemui.scene.shared.model.SceneDataSourceDelegator
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn
import com.android.app.tracing.coroutines.launchTraced as launch

/** Encapsulates the state of communal mode. */
interface CommunalSceneRepository {
@@ -52,6 +55,9 @@ interface CommunalSceneRepository {
    /** Immediately snaps to the desired scene. */
    fun snapToScene(toScene: SceneKey)

    /** Shows the hub from a power button press. */
    suspend fun showHubFromPowerButton()

    /**
     * Updates the transition state of the hub [SceneTransitionLayout].
     *
@@ -67,6 +73,7 @@ constructor(
    @Application private val applicationScope: CoroutineScope,
    @Background backgroundScope: CoroutineScope,
    @Communal private val sceneDataSource: SceneDataSource,
    @Communal private val delegator: SceneDataSourceDelegator,
) : CommunalSceneRepository {

    override val currentScene: StateFlow<SceneKey> = sceneDataSource.currentScene
@@ -98,6 +105,18 @@ constructor(
        }
    }

    override suspend fun showHubFromPowerButton() {
        // If keyguard is not showing yet, the hub view is not ready and the
        // [SceneDataSourceDelegator] will still be using the default [NoOpSceneDataSource]
        // and initial key, which is Blank. This means that when the hub container loads, it
        // will default to not showing the hub. Attempting to set the scene in this state
        // is simply ignored by the [NoOpSceneDataSource]. Instead, we temporarily override
        // it with a new one that defaults to Communal. This delegate will be overwritten
        // once the [CommunalContainer] loads.
        // TODO(b/392969914): show the hub first instead of forcing the scene.
        delegator.setDelegate(NoOpSceneDataSource(CommunalScenes.Communal))
    }

    /**
     * Updates the transition state of the hub [SceneTransitionLayout].
     *
@@ -106,4 +125,27 @@ constructor(
    override fun setTransitionState(transitionState: Flow<ObservableTransitionState>?) {
        _transitionState.value = transitionState
    }

    /** Noop implementation of a scene data source that always returns the initial [SceneKey]. */
    private class NoOpSceneDataSource(initialSceneKey: SceneKey) : SceneDataSource {
        override val currentScene: StateFlow<SceneKey> =
            MutableStateFlow(initialSceneKey).asStateFlow()

        override val currentOverlays: StateFlow<Set<OverlayKey>> =
            MutableStateFlow(emptySet<OverlayKey>()).asStateFlow()

        override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) = Unit

        override fun snapToScene(toScene: SceneKey) = Unit

        override fun showOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) = Unit

        override fun hideOverlay(overlay: OverlayKey, transitionKey: TransitionKey?) = Unit

        override fun replaceOverlay(
            from: OverlayKey,
            to: OverlayKey,
            transitionKey: TransitionKey?,
        ) = Unit
    }
}
+23 −0
Original line number Diff line number Diff line
@@ -148,6 +148,29 @@ constructor(
        }
    }

    fun showHubFromPowerButton() {
        val loggingReason = "showing hub from power button"
        applicationScope.launch("$TAG#showHubFromPowerButton") {
            if (SceneContainerFlag.isEnabled) {
                sceneInteractor.changeScene(
                    toScene = CommunalScenes.Communal.toSceneContainerSceneKey(),
                    loggingReason = loggingReason,
                )
                return@launch
            }

            if (currentScene.value == CommunalScenes.Communal) return@launch
            logger.logSceneChangeRequested(
                from = currentScene.value,
                to = CommunalScenes.Communal,
                reason = loggingReason,
                isInstant = true,
            )
            notifyListeners(CommunalScenes.Communal, null)
            repository.showHubFromPowerButton()
        }
    }

    private fun notifyListeners(newScene: SceneKey, keyguardState: KeyguardState?) {
        onSceneAboutToChangeListener.forEach { it.onSceneAboutToChange(newScene, keyguardState) }
    }
Loading