summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Will Leshner <wleshner@google.com> 2024-12-12 14:12:11 -0800
committer Will Leshner <wleshner@google.com> 2024-12-17 11:12:47 -0800
commit363bcf0dc5200d47926640d2b7981b58c42b67a5 (patch)
tree1ceda05f36e1b1a63510639f0c23d247d283609a
parent95bc2bfbc9f128057342d54e91f85295015df935 (diff)
Several fixes to GH bottom area on mobile.
1. Always show the "go to dream" button if charging. 2. Position the "go to dream" button in the bottom right corner. 3. Tapping the "go to dream" button when dreams are disabled goes to settings. 4. Lock icon is always at the bottom of the screen, even on devices with UDFPS. Bug: 382693988 Bug: 382739998 Test: atest CommunalSettingsRepositoryImplTest Test: atest CommunalToDreamButtonViewModelTest Flag: com.android.systemui.glanceable_hub_v2 Change-Id: Id7413b41c4d4d52780431fde2b1878fe8c1fd02b
-rw-r--r--packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt81
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt28
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt42
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt17
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt32
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt4
7 files changed, 176 insertions, 34 deletions
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
index a17a1d46554f..0a0003ee9a8a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt
@@ -19,17 +19,20 @@ package com.android.systemui.communal.ui.compose
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Measurable
+import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import com.android.compose.animation.scene.SceneScope
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.communal.smartspace.SmartspaceInteractionHandler
import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection
import com.android.systemui.communal.ui.compose.section.CommunalPopupSection
@@ -39,8 +42,11 @@ import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
+import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
import javax.inject.Inject
+import kotlin.math.min
+import kotlin.math.roundToInt
/** Renders the content of the glanceable hub. */
class CommunalContent
@@ -48,6 +54,7 @@ class CommunalContent
constructor(
private val viewModel: CommunalViewModel,
private val interactionHandler: SmartspaceInteractionHandler,
+ private val communalSettingsInteractor: CommunalSettingsInteractor,
private val dialogFactory: SystemUIDialogFactory,
private val lockSection: LockSection,
private val bottomAreaSection: BottomAreaSection,
@@ -77,11 +84,20 @@ constructor(
sceneScope = this@Content,
)
}
- with(lockSection) {
- LockIcon(
- overrideColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ if (communalSettingsInteractor.isV2FlagEnabled()) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_lock),
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.onPrimaryContainer,
modifier = Modifier.element(Communal.Elements.LockIcon),
)
+ } else {
+ with(lockSection) {
+ LockIcon(
+ overrideColor = MaterialTheme.colorScheme.onPrimaryContainer,
+ modifier = Modifier.element(Communal.Elements.LockIcon),
+ )
+ }
}
with(bottomAreaSection) {
IndicationArea(
@@ -98,14 +114,42 @@ constructor(
val noMinConstraints = constraints.copy(minWidth = 0, minHeight = 0)
- val lockIconPlaceable = lockIconMeasurable.measure(noMinConstraints)
+ val lockIconPlaceable =
+ if (communalSettingsInteractor.isV2FlagEnabled()) {
+ val lockIconSizeInt = lockIconSize.roundToPx()
+ lockIconMeasurable.measure(
+ Constraints.fixed(width = lockIconSizeInt, height = lockIconSizeInt)
+ )
+ } else {
+ lockIconMeasurable.measure(noMinConstraints)
+ }
val lockIconBounds =
- IntRect(
- left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
- top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
- right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
- bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
- )
+ if (communalSettingsInteractor.isV2FlagEnabled()) {
+ val lockIconDistanceFromBottom =
+ min(
+ (constraints.maxHeight * lockIconPercentDistanceFromBottom)
+ .roundToInt(),
+ lockIconMinDistanceFromBottom.roundToPx(),
+ )
+ val x = constraints.maxWidth / 2 - lockIconPlaceable.width / 2
+ val y =
+ constraints.maxHeight -
+ lockIconDistanceFromBottom -
+ lockIconPlaceable.height
+ IntRect(
+ left = x,
+ top = y,
+ right = x + lockIconPlaceable.width,
+ bottom = y + lockIconPlaceable.height,
+ )
+ } else {
+ IntRect(
+ left = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Left],
+ top = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Top],
+ right = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Right],
+ bottom = lockIconPlaceable[BlueprintAlignmentLines.LockIcon.Bottom],
+ )
+ }
val bottomAreaPlaceable = bottomAreaMeasurable.measure(noMinConstraints)
@@ -129,12 +173,17 @@ constructor(
val bottomAreaTop = constraints.maxHeight - bottomAreaPlaceable.height
bottomAreaPlaceable.place(x = 0, y = bottomAreaTop)
+
+ val screensaverButtonPaddingInt = screensaverButtonPadding.roundToPx()
screensaverButtonPlaceable?.place(
x =
constraints.maxWidth -
screensaverButtonSizeInt -
- Dimensions.ItemSpacing.roundToPx(),
- y = lockIconBounds.top,
+ screensaverButtonPaddingInt,
+ y =
+ constraints.maxHeight -
+ screensaverButtonSizeInt -
+ screensaverButtonPaddingInt,
)
}
}
@@ -142,6 +191,12 @@ constructor(
}
companion object {
- val screensaverButtonSize: Dp = 64.dp
+ private val screensaverButtonSize: Dp = 64.dp
+ private val screensaverButtonPadding: Dp = 24.dp
+ // TODO(b/382739998): Remove these hardcoded values once lock icon size and bottom area
+ // position are sorted.
+ private val lockIconSize: Dp = 54.dp
+ private val lockIconPercentDistanceFromBottom = 0.1f
+ private val lockIconMinDistanceFromBottom = 70.dp
}
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
index b66727e492cf..9bc7cdf93f59 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/data/repository/CommunalSettingsRepositoryImplTest.kt
@@ -295,6 +295,34 @@ class CommunalSettingsRepositoryImplTest : SysuiTestCase() {
}
}
+ @Test
+ fun screensaverDisabledByUser() =
+ testScope.runTest {
+ val enabledState by collectLastValue(underTest.getScreensaverEnabledState(PRIMARY_USER))
+
+ kosmos.fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ENABLED,
+ 0,
+ PRIMARY_USER.id,
+ )
+
+ assertThat(enabledState).isFalse()
+ }
+
+ @Test
+ fun screensaverEnabledByUser() =
+ testScope.runTest {
+ val enabledState by collectLastValue(underTest.getScreensaverEnabledState(PRIMARY_USER))
+
+ kosmos.fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ENABLED,
+ 1,
+ PRIMARY_USER.id,
+ )
+
+ assertThat(enabledState).isTrue()
+ }
+
private fun setKeyguardFeaturesDisabled(user: UserInfo, disabledFlags: Int) {
whenever(kosmos.devicePolicyManager.getKeyguardDisabledFeatures(nullable(), eq(user.id)))
.thenReturn(disabledFlags)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
index 88206850eb60..b78080885b0a 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.communal.ui.viewmodel
import android.platform.test.annotations.EnableFlags
+import android.provider.Settings
import android.service.dream.dreamManager
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
@@ -29,12 +30,16 @@ import com.android.systemui.kosmos.runCurrent
import com.android.systemui.kosmos.runTest
import com.android.systemui.kosmos.testScope
import com.android.systemui.lifecycle.activateIn
+import com.android.systemui.plugins.activityStarter
import com.android.systemui.statusbar.policy.batteryController
import com.android.systemui.testKosmos
+import com.android.systemui.user.data.repository.fakeUserRepository
+import com.android.systemui.util.settings.fakeSettings
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mockito.verify
import org.mockito.kotlin.any
import org.mockito.kotlin.whenever
@@ -56,10 +61,9 @@ class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
}
@Test
- fun shouldShowDreamButtonOnHub_trueWhenCanDream() =
+ fun shouldShowDreamButtonOnHub_trueWhenPluggedIn() =
with(kosmos) {
runTest {
- whenever(dreamManager.canStartDreaming(any())).thenReturn(true)
whenever(batteryController.isPluggedIn()).thenReturn(true)
val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
@@ -68,11 +72,10 @@ class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
}
@Test
- fun shouldShowDreamButtonOnHub_falseWhenCannotDream() =
+ fun shouldShowDreamButtonOnHub_falseWhenNotPluggedIn() =
with(kosmos) {
runTest {
- whenever(dreamManager.canStartDreaming(any())).thenReturn(false)
- whenever(batteryController.isPluggedIn()).thenReturn(true)
+ whenever(batteryController.isPluggedIn()).thenReturn(false)
val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
assertThat(shouldShowButton).isFalse()
@@ -80,25 +83,40 @@ class CommunalToDreamButtonViewModelTest : SysuiTestCase() {
}
@Test
- fun shouldShowDreamButtonOnHub_falseWhenNotPluggedIn() =
+ fun onShowDreamButtonTap_dreamsEnabled_startsDream() =
with(kosmos) {
runTest {
- whenever(dreamManager.canStartDreaming(any())).thenReturn(true)
- whenever(batteryController.isPluggedIn()).thenReturn(false)
+ val currentUser = fakeUserRepository.asMainUser()
+ kosmos.fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ENABLED,
+ 1,
+ currentUser.id,
+ )
+ runCurrent()
- val shouldShowButton by collectLastValue(underTest.shouldShowDreamButtonOnHub)
- assertThat(shouldShowButton).isFalse()
+ underTest.onShowDreamButtonTap()
+ runCurrent()
+
+ verify(dreamManager).startDream()
}
}
@Test
- fun onShowDreamButtonTap_startsDream() =
+ fun onShowDreamButtonTap_dreamsDisabled_startsActivity() =
with(kosmos) {
runTest {
+ val currentUser = fakeUserRepository.asMainUser()
+ kosmos.fakeSettings.putIntForUser(
+ Settings.Secure.SCREENSAVER_ENABLED,
+ 0,
+ currentUser.id,
+ )
+ runCurrent()
+
underTest.onShowDreamButtonTap()
runCurrent()
- verify(dreamManager).startDream()
+ verify(activityStarter).postStartActivityDismissingKeyguard(any(), anyInt())
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
index 26abb48ce7db..73c0179cf8ec 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalSettingsRepository.kt
@@ -55,6 +55,8 @@ interface CommunalSettingsRepository {
/** A [CommunalEnabledState] for the specified user. */
fun getEnabledState(user: UserInfo): Flow<CommunalEnabledState>
+ fun getScreensaverEnabledState(user: UserInfo): Flow<Boolean>
+
/**
* Returns true if any glanceable hub functionality should be enabled via configs and flags.
*
@@ -138,6 +140,20 @@ constructor(
.flowOn(bgDispatcher)
}
+ override fun getScreensaverEnabledState(user: UserInfo): Flow<Boolean> =
+ secureSettings
+ .observerFlow(userId = user.id, names = arrayOf(Settings.Secure.SCREENSAVER_ENABLED))
+ // Force an update
+ .onStart { emit(Unit) }
+ .map {
+ secureSettings.getIntForUser(
+ Settings.Secure.SCREENSAVER_ENABLED,
+ SCREENSAVER_ENABLED_SETTING_DEFAULT,
+ user.id,
+ ) == 1
+ }
+ .flowOn(bgDispatcher)
+
override fun getAllowedByDevicePolicy(user: UserInfo): Flow<Boolean> =
broadcastDispatcher
.broadcastFlow(
@@ -182,6 +198,7 @@ constructor(
companion object {
const val GLANCEABLE_HUB_BACKGROUND_SETTING = "glanceable_hub_background"
private const val ENABLED_SETTING_DEFAULT = 1
+ private const val SCREENSAVER_ENABLED_SETTING_DEFAULT = 0
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
index 862b05bc9b5d..c1f21e4046a3 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSettingsInteractor.kt
@@ -69,6 +69,12 @@ constructor(
// Start this eagerly since the value is accessed synchronously in many places.
.stateIn(scope = bgScope, started = SharingStarted.Eagerly, initialValue = false)
+ /** Whether or not screensaver (dreams) is enabled for the currently selected user. */
+ val isScreensaverEnabled: Flow<Boolean> =
+ userInteractor.selectedUserInfo.flatMapLatest { user ->
+ repository.getScreensaverEnabledState(user)
+ }
+
/**
* Returns true if any glanceable hub functionality should be enabled via configs and flags.
*
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
index 7d5b196dfaa8..c6f96e198b91 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModel.kt
@@ -18,10 +18,15 @@ package com.android.systemui.communal.ui.viewmodel
import android.annotation.SuppressLint
import android.app.DreamManager
+import android.content.Intent
+import android.provider.Settings
+import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.lifecycle.ExclusiveActivatable
+import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.util.kotlin.isDevicePluggedIn
+import com.android.systemui.util.kotlin.sample
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlin.coroutines.CoroutineContext
@@ -31,7 +36,6 @@ import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOn
-import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -41,6 +45,8 @@ class CommunalToDreamButtonViewModel
constructor(
@Background private val backgroundContext: CoroutineContext,
batteryController: BatteryController,
+ private val settingsInteractor: CommunalSettingsInteractor,
+ private val activityStarter: ActivityStarter,
private val dreamManager: DreamManager,
) : ExclusiveActivatable() {
@@ -49,11 +55,7 @@ constructor(
/** Whether we should show a button on hub to switch to dream. */
@SuppressLint("MissingPermission")
val shouldShowDreamButtonOnHub =
- batteryController
- .isDevicePluggedIn()
- .distinctUntilChanged()
- .map { isPluggedIn -> isPluggedIn && dreamManager.canStartDreaming(true) }
- .flowOn(backgroundContext)
+ batteryController.isDevicePluggedIn().distinctUntilChanged().flowOn(backgroundContext)
/** Handle a tap on the "show dream" button. */
fun onShowDreamButtonTap() {
@@ -63,9 +65,21 @@ constructor(
@SuppressLint("MissingPermission")
override suspend fun onActivated(): Nothing = coroutineScope {
launch {
- _requests.receiveAsFlow().collectLatest {
- withContext(backgroundContext) { dreamManager.startDream() }
- }
+ _requests
+ .receiveAsFlow()
+ .sample(settingsInteractor.isScreensaverEnabled)
+ .collectLatest { enabled ->
+ withContext(backgroundContext) {
+ if (enabled) {
+ dreamManager.startDream()
+ } else {
+ activityStarter.postStartActivityDismissingKeyguard(
+ Intent(Settings.ACTION_DREAM_SETTINGS),
+ 0,
+ )
+ }
+ }
+ }
}
awaitCancellation()
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
index b407b1ba227a..e3cfb80489e3 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/communal/ui/viewmodel/CommunalToDreamButtonViewModelKosmos.kt
@@ -17,8 +17,10 @@
package com.android.systemui.communal.ui.viewmodel
import android.service.dream.dreamManager
+import com.android.systemui.communal.domain.interactor.communalSettingsInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testDispatcher
+import com.android.systemui.plugins.activityStarter
import com.android.systemui.statusbar.policy.batteryController
val Kosmos.communalToDreamButtonViewModel by
@@ -26,6 +28,8 @@ val Kosmos.communalToDreamButtonViewModel by
CommunalToDreamButtonViewModel(
backgroundContext = testDispatcher,
batteryController = batteryController,
+ settingsInteractor = communalSettingsInteractor,
+ activityStarter = activityStarter,
dreamManager = dreamManager,
)
}