summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author William Xiao <wxyz@google.com> 2025-01-31 20:56:51 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2025-01-31 20:56:51 -0800
commitcacd557c781996a092faca122ae2f95389f6b377 (patch)
tree7f739fcb657db59ab69c7de0560e4eac0d8c18f3
parentabccf87d9fb33e87e105abbca35f921535eb19ed (diff)
parentb3d5a84ac5bf4c3a346a1d266c425053b33346cc (diff)
Merge changes I5a85e152,Iaa9f6743 into main
* changes: Allow showing hub immediately on view load Resolve circular dependency
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt3
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSceneRepositoryImplTest.kt (renamed from packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt)60
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt44
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt23
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java3
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt7
8 files changed, 119 insertions, 29 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
index 3ffbabb09710..4a4607b6e8fc 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt
@@ -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(
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSceneRepositoryImplTest.kt
index fd0bf4dae198..293d32471713 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSceneRepositoryImplTest.kt
@@ -1,5 +1,5 @@
/*
- * 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 {
- CommunalSceneRepositoryImpl(
- kosmos.applicationCoroutineScope,
- kosmos.applicationCoroutineScope,
- kosmos.sceneDataSource,
- )
- }
+ private val delegator = mock<SceneDataSourceDelegator> {}
+
+ private val Kosmos.underTest by
+ Kosmos.Fixture {
+ CommunalSceneRepositoryImpl(
+ 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)
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index c266a5b47cff..0b66a0ffb711 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -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
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
index 643d185f1939..8b6322720118 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSceneRepository.kt
@@ -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
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
index 476493225857..3d9e93036dbc 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt
@@ -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) }
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index 099a85926020..49003a735fbd 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -45,7 +45,7 @@ abstract class BaseCommunalViewModel(
val mediaHost: MediaHost,
val mediaCarouselController: MediaCarouselController,
) {
- val currentScene: Flow<SceneKey> = communalSceneInteractor.currentScene
+ val currentScene: StateFlow<SceneKey> = communalSceneInteractor.currentScene
/** Used to animate showing or hiding the communal content. */
open val isCommunalContentVisible: Flow<Boolean> = MutableStateFlow(false)
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 312d2ffd74e4..4110a05170b3 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -18,7 +18,6 @@ package com.android.keyguard;
import static android.app.StatusBarManager.SESSION_KEYGUARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.hardware.biometrics.BiometricAuthenticator.TYPE_ANY_BIOMETRIC;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT;
@@ -2717,7 +2716,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
() -> mAlternateBouncerInteractor,
() -> mJavaAdapter,
() -> mSceneInteractor,
- mCommunalSceneInteractor);
+ () -> mCommunalSceneInteractor);
setAlternateBouncerVisibility(false);
setPrimaryBouncerVisibility(false);
setStrongAuthTracker(KeyguardUpdateMonitorTest.this.mStrongAuthTracker);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt
index b3c1411243c1..3f35bb9f3520 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/data/repository/FakeCommunalSceneRepository.kt
@@ -17,8 +17,7 @@ import kotlinx.coroutines.launch
/** Fake implementation of [CommunalSceneRepository]. */
class FakeCommunalSceneRepository(
private val applicationScope: CoroutineScope,
- override val currentScene: MutableStateFlow<SceneKey> =
- MutableStateFlow(CommunalScenes.Default),
+ override val currentScene: MutableStateFlow<SceneKey> = MutableStateFlow(CommunalScenes.Default),
) : CommunalSceneRepository {
override fun changeScene(toScene: SceneKey, transitionKey: TransitionKey?) =
@@ -31,6 +30,10 @@ class FakeCommunalSceneRepository(
}
}
+ override suspend fun showHubFromPowerButton() {
+ snapToScene(CommunalScenes.Communal)
+ }
+
private val defaultTransitionState = ObservableTransitionState.Idle(CommunalScenes.Default)
private val _transitionState = MutableStateFlow<Flow<ObservableTransitionState>?>(null)
override val transitionState: StateFlow<ObservableTransitionState> =