Merge "Add Centering animation for large clock" into main
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
index 53f400f..55f7f69a 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/LockscreenSceneBlueprintModule.kt
@@ -19,7 +19,6 @@
import com.android.systemui.keyguard.ui.composable.blueprint.CommunalBlueprintModule
import com.android.systemui.keyguard.ui.composable.blueprint.DefaultBlueprintModule
import com.android.systemui.keyguard.ui.composable.blueprint.ShortcutsBesideUdfpsBlueprintModule
-import com.android.systemui.keyguard.ui.composable.blueprint.SplitShadeBlueprintModule
import com.android.systemui.keyguard.ui.composable.blueprint.SplitShadeWeatherClockBlueprintModule
import com.android.systemui.keyguard.ui.composable.blueprint.WeatherClockBlueprintModule
import com.android.systemui.keyguard.ui.composable.section.OptionalSectionModule
@@ -32,7 +31,6 @@
DefaultBlueprintModule::class,
OptionalSectionModule::class,
ShortcutsBesideUdfpsBlueprintModule::class,
- SplitShadeBlueprintModule::class,
SplitShadeWeatherClockBlueprintModule::class,
WeatherClockBlueprintModule::class,
],
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt
index c5ff859..d9ed497 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ClockTransition.kt
@@ -38,6 +38,9 @@
from(ClockScenes.largeClockScene, to = ClockScenes.smallClockScene) {
transitioningToSmallClock()
}
+ from(ClockScenes.splitShadeLargeClockScene, to = ClockScenes.largeClockScene) {
+ spec = tween(1000)
+ }
}
private fun TransitionBuilder.transitioningToLargeClock() {
@@ -70,6 +73,8 @@
object ClockScenes {
val smallClockScene = SceneKey("small-clock-scene")
val largeClockScene = SceneKey("large-clock-scene")
+ val splitShadeSmallClockScene = SceneKey("split-shade-small-clock-scene")
+ val splitShadeLargeClockScene = SceneKey("split-shade-large-clock-scene")
}
object ClockElementKeys {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
index 9509fd2..320c455 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt
@@ -22,18 +22,15 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.IntRect
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
-import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
-import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
-import com.android.systemui.keyguard.ui.composable.section.NotificationSection
import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
+import com.android.systemui.keyguard.ui.composable.section.TopAreaSection
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
import dagger.Binds
import dagger.Module
@@ -50,13 +47,11 @@
constructor(
private val viewModel: LockscreenContentViewModel,
private val statusBarSection: StatusBarSection,
- private val clockSection: DefaultClockSection,
- private val notificationSection: NotificationSection,
private val lockSection: LockSection,
private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
private val bottomAreaSection: BottomAreaSection,
private val settingsMenuSection: SettingsMenuSection,
- private val mediaCarouselSection: MediaCarouselSection,
+ private val topAreaSection: TopAreaSection,
) : ComposableLockscreenSceneBlueprint {
override val id: String = "default"
@@ -64,7 +59,6 @@
@Composable
override fun SceneScope.Content(modifier: Modifier) {
val isUdfpsVisible = viewModel.isUdfpsVisible
- val resources = LocalContext.current.resources
LockscreenLongPress(
viewModel = viewModel.longPress,
@@ -77,17 +71,7 @@
modifier = Modifier.fillMaxWidth(),
) {
with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { DefaultClockLayout() }
- with(mediaCarouselSection) { MediaCarousel() }
-
- if (viewModel.areNotificationsVisible(resources = resources)) {
- with(notificationSection) {
- Notifications(
- modifier = Modifier.fillMaxWidth().weight(weight = 1f)
- )
- }
- }
-
+ with(topAreaSection) { DefaultClockLayoutWithNotifications() }
if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
with(ambientIndicationSectionOptional.get()) {
AmbientIndication(modifier = Modifier.fillMaxWidth())
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
index 9abfa42..64c2cb3 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt
@@ -22,18 +22,15 @@
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.IntRect
import com.android.compose.animation.scene.SceneScope
import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
-import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
-import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
-import com.android.systemui.keyguard.ui.composable.section.NotificationSection
import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
+import com.android.systemui.keyguard.ui.composable.section.TopAreaSection
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
import dagger.Binds
import dagger.Module
@@ -50,13 +47,11 @@
constructor(
private val viewModel: LockscreenContentViewModel,
private val statusBarSection: StatusBarSection,
- private val clockSection: DefaultClockSection,
- private val notificationSection: NotificationSection,
private val lockSection: LockSection,
private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
private val bottomAreaSection: BottomAreaSection,
private val settingsMenuSection: SettingsMenuSection,
- private val mediaCarouselSection: MediaCarouselSection,
+ private val topAreaSection: TopAreaSection,
) : ComposableLockscreenSceneBlueprint {
override val id: String = "shortcuts-besides-udfps"
@@ -64,7 +59,6 @@
@Composable
override fun SceneScope.Content(modifier: Modifier) {
val isUdfpsVisible = viewModel.isUdfpsVisible
- val resources = LocalContext.current.resources
LockscreenLongPress(
viewModel = viewModel.longPress,
@@ -77,16 +71,7 @@
modifier = Modifier.fillMaxWidth(),
) {
with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
- with(clockSection) { DefaultClockLayout() }
- with(mediaCarouselSection) { MediaCarousel() }
-
- if (viewModel.areNotificationsVisible(resources = resources)) {
- with(notificationSection) {
- Notifications(
- modifier = Modifier.fillMaxWidth().weight(weight = 1f)
- )
- }
- }
+ with(topAreaSection) { DefaultClockLayoutWithNotifications() }
if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
with(ambientIndicationSectionOptional.get()) {
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
deleted file mode 100644
index 652412d..0000000
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/SplitShadeBlueprint.kt
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2023 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.keyguard.ui.composable.blueprint
-
-import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxHeight
-import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.foundation.layout.padding
-import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.layout.Layout
-import androidx.compose.ui.res.dimensionResource
-import androidx.compose.ui.unit.Dp
-import androidx.compose.ui.unit.IntRect
-import androidx.compose.ui.unit.dp
-import com.android.compose.animation.scene.SceneScope
-import com.android.compose.modifiers.padding
-import com.android.systemui.Flags
-import com.android.systemui.keyguard.ui.composable.LockscreenLongPress
-import com.android.systemui.keyguard.ui.composable.section.AmbientIndicationSection
-import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
-import com.android.systemui.keyguard.ui.composable.section.DefaultClockSection
-import com.android.systemui.keyguard.ui.composable.section.LockSection
-import com.android.systemui.keyguard.ui.composable.section.MediaCarouselSection
-import com.android.systemui.keyguard.ui.composable.section.NotificationSection
-import com.android.systemui.keyguard.ui.composable.section.SettingsMenuSection
-import com.android.systemui.keyguard.ui.composable.section.StatusBarSection
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.res.R
-import com.android.systemui.shade.LargeScreenHeaderHelper
-import dagger.Binds
-import dagger.Module
-import dagger.multibindings.IntoSet
-import java.util.Optional
-import javax.inject.Inject
-
-/**
- * Renders the lockscreen scene when showing with a split shade (e.g. unfolded foldable and/or
- * tablet form factor).
- */
-class SplitShadeBlueprint
-@Inject
-constructor(
- private val viewModel: LockscreenContentViewModel,
- private val statusBarSection: StatusBarSection,
- private val clockSection: DefaultClockSection,
- private val notificationSection: NotificationSection,
- private val lockSection: LockSection,
- private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
- private val bottomAreaSection: BottomAreaSection,
- private val settingsMenuSection: SettingsMenuSection,
- private val mediaCarouselSection: MediaCarouselSection,
- private val largeScreenHeaderHelper: LargeScreenHeaderHelper,
-) : ComposableLockscreenSceneBlueprint {
-
- override val id: String = "split-shade"
-
- @Composable
- override fun SceneScope.Content(modifier: Modifier) {
- val isUdfpsVisible = viewModel.isUdfpsVisible
-
- LockscreenLongPress(
- viewModel = viewModel.longPress,
- modifier = modifier,
- ) { onSettingsMenuPlaced ->
- Layout(
- content = {
- // Constrained to above the lock icon.
- Column(
- modifier = Modifier.fillMaxSize(),
- ) {
- with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
- Row(
- modifier = Modifier.fillMaxSize(),
- ) {
- Column(
- modifier = Modifier.fillMaxHeight().weight(weight = 1f),
- horizontalAlignment = Alignment.CenterHorizontally,
- ) {
- with(clockSection) { DefaultClockLayout() }
- with(mediaCarouselSection) { MediaCarousel() }
- }
- with(notificationSection) {
- val splitShadeTopMargin: Dp =
- if (Flags.centralizedStatusBarHeightFix()) {
- largeScreenHeaderHelper.getLargeScreenHeaderHeight().dp
- } else {
- dimensionResource(
- id = R.dimen.large_screen_shade_header_height
- )
- }
- Notifications(
- modifier =
- Modifier.fillMaxHeight()
- .weight(weight = 1f)
- .padding(top = splitShadeTopMargin)
- )
- }
- }
-
- if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
- with(ambientIndicationSectionOptional.get()) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
- }
-
- with(lockSection) { LockIcon() }
-
- // Aligned to bottom and constrained to below the lock icon.
- Column(modifier = Modifier.fillMaxWidth()) {
- if (isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
- with(ambientIndicationSectionOptional.get()) {
- AmbientIndication(modifier = Modifier.fillMaxWidth())
- }
- }
-
- with(bottomAreaSection) {
- IndicationArea(modifier = Modifier.fillMaxWidth())
- }
- }
-
- // Aligned to bottom and NOT constrained by the lock icon.
- with(bottomAreaSection) {
- Shortcut(isStart = true, applyPadding = true)
- Shortcut(isStart = false, applyPadding = true)
- }
- with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
- },
- modifier = Modifier.fillMaxSize(),
- ) { measurables, constraints ->
- check(measurables.size == 6)
- val aboveLockIconMeasurable = measurables[0]
- val lockIconMeasurable = measurables[1]
- val belowLockIconMeasurable = measurables[2]
- val startShortcutMeasurable = measurables[3]
- val endShortcutMeasurable = measurables[4]
- val settingsMenuMeasurable = measurables[5]
-
- val noMinConstraints =
- constraints.copy(
- minWidth = 0,
- minHeight = 0,
- )
- val lockIconPlaceable = 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],
- )
-
- val aboveLockIconPlaceable =
- aboveLockIconMeasurable.measure(
- noMinConstraints.copy(maxHeight = lockIconBounds.top)
- )
- val belowLockIconPlaceable =
- belowLockIconMeasurable.measure(
- noMinConstraints.copy(
- maxHeight =
- (constraints.maxHeight - lockIconBounds.bottom).coerceAtLeast(0)
- )
- )
- val startShortcutPleaceable = startShortcutMeasurable.measure(noMinConstraints)
- val endShortcutPleaceable = endShortcutMeasurable.measure(noMinConstraints)
- val settingsMenuPlaceable = settingsMenuMeasurable.measure(noMinConstraints)
-
- layout(constraints.maxWidth, constraints.maxHeight) {
- aboveLockIconPlaceable.place(
- x = 0,
- y = 0,
- )
- lockIconPlaceable.place(
- x = lockIconBounds.left,
- y = lockIconBounds.top,
- )
- belowLockIconPlaceable.place(
- x = 0,
- y = constraints.maxHeight - belowLockIconPlaceable.height,
- )
- startShortcutPleaceable.place(
- x = 0,
- y = constraints.maxHeight - startShortcutPleaceable.height,
- )
- endShortcutPleaceable.place(
- x = constraints.maxWidth - endShortcutPleaceable.width,
- y = constraints.maxHeight - endShortcutPleaceable.height,
- )
- settingsMenuPlaceable.place(
- x = (constraints.maxWidth - settingsMenuPlaceable.width) / 2,
- y = constraints.maxHeight - settingsMenuPlaceable.height,
- )
- }
- }
- }
- }
-}
-
-@Module
-interface SplitShadeBlueprintModule {
- @Binds
- @IntoSet
- fun blueprint(blueprint: SplitShadeBlueprint): ComposableLockscreenSceneBlueprint
-}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
index f86623f..ee4e2d6 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/WeatherClockBlueprint.kt
@@ -112,7 +112,7 @@
with(mediaCarouselSection) { MediaCarousel() }
- if (viewModel.areNotificationsVisible(resources = resources)) {
+ if (viewModel.areNotificationsVisible) {
with(notificationSection) {
Notifications(
modifier = Modifier.fillMaxWidth().weight(weight = 1f)
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
index 1ab2bc76..82e19e7c 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/DefaultClockSection.kt
@@ -16,39 +16,29 @@
package com.android.systemui.keyguard.ui.composable.section
+import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
-import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.view.contains
import com.android.compose.animation.scene.SceneScope
-import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.compose.modifiers.padding
import com.android.systemui.customization.R as customizationR
-import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.largeClockElementKey
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.smallClockElementKey
-import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.smartspaceElementKey
-import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.largeClockScene
-import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.smallClockScene
-import com.android.systemui.keyguard.ui.composable.blueprint.ClockTransition.defaultClockTransitions
-import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn
import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
import com.android.systemui.keyguard.ui.composable.modifier.onTopPlacementChanged
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
-import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
-import com.android.systemui.res.R
import javax.inject.Inject
/** Provides small clock and large clock composables for the default clock face. */
@@ -56,91 +46,10 @@
@Inject
constructor(
private val viewModel: KeyguardClockViewModel,
- private val clockInteractor: KeyguardClockInteractor,
private val aodBurnInViewModel: AodBurnInViewModel,
- private val lockscreenContentViewModel: LockscreenContentViewModel,
- private val smartSpaceSection: SmartSpaceSection,
) {
@Composable
- fun DefaultClockLayout(
- modifier: Modifier = Modifier,
- ) {
- val isLargeClockVisible by viewModel.isLargeClockVisible.collectAsState()
- val burnIn = rememberBurnIn(clockInteractor)
- val currentScene =
- if (isLargeClockVisible) {
- largeClockScene
- } else {
- smallClockScene
- }
-
- LaunchedEffect(isLargeClockVisible) {
- if (isLargeClockVisible) {
- burnIn.onSmallClockTopChanged(null)
- }
- }
-
- SceneTransitionLayout(
- modifier = modifier,
- currentScene = currentScene,
- onChangeScene = {},
- transitions = defaultClockTransitions,
- ) {
- scene(smallClockScene) {
- Column {
- SmallClock(
- burnInParams = burnIn.parameters,
- onTopChanged = burnIn.onSmallClockTopChanged,
- modifier = Modifier.element(smallClockElementKey).fillMaxWidth()
- )
- SmartSpaceContent()
- }
- }
-
- scene(largeClockScene) {
- Column {
- SmartSpaceContent()
- LargeClock(modifier = Modifier.element(largeClockElementKey).fillMaxWidth())
- }
- }
- }
- }
-
- @Composable
- private fun SceneScope.SmartSpaceContent(
- modifier: Modifier = Modifier,
- ) {
- val burnIn = rememberBurnIn(clockInteractor)
- val resources = LocalContext.current.resources
-
- MovableElement(key = smartspaceElementKey, modifier = modifier) {
- content {
- with(smartSpaceSection) {
- this@SmartSpaceContent.SmartSpace(
- burnInParams = burnIn.parameters,
- onTopChanged = burnIn.onSmartspaceTopChanged,
- modifier =
- Modifier.fillMaxWidth()
- .padding(
- top = {
- lockscreenContentViewModel.getSmartSpacePaddingTop(
- resources
- )
- },
- bottom = {
- resources.getDimensionPixelSize(
- R.dimen.keyguard_status_view_bottom_margin
- )
- }
- ),
- )
- }
- }
- }
- }
-
- @Composable
- private fun SceneScope.SmallClock(
+ fun SceneScope.SmallClock(
burnInParams: BurnInParameters,
onTopChanged: (top: Float?) -> Unit,
modifier: Modifier = Modifier,
@@ -152,58 +61,60 @@
viewModel.clock = currentClock
val context = LocalContext.current
-
- AndroidView(
- factory = { context ->
- FrameLayout(context).apply {
- val newClockView = checkNotNull(currentClock).smallClock.view
- (newClockView.parent as? ViewGroup)?.removeView(newClockView)
- addView(newClockView)
- }
- },
- update = {
- val newClockView = checkNotNull(currentClock).smallClock.view
- it.removeAllViews()
- (newClockView.parent as? ViewGroup)?.removeView(newClockView)
- it.addView(newClockView)
- },
- modifier =
- modifier
- .padding(
- horizontal = dimensionResource(customizationR.dimen.clock_padding_start)
- )
- .padding(top = { viewModel.getSmallClockTopMargin(context) })
- .onTopPlacementChanged(onTopChanged)
- .burnInAware(
- viewModel = aodBurnInViewModel,
- params = burnInParams,
- ),
- )
+ MovableElement(key = smallClockElementKey, modifier = modifier) {
+ content {
+ AndroidView(
+ factory = { context ->
+ FrameLayout(context).apply {
+ addClockView(checkNotNull(currentClock).smallClock.view)
+ }
+ },
+ update = { it.addClockView(checkNotNull(currentClock).smallClock.view) },
+ modifier =
+ Modifier.padding(
+ horizontal =
+ dimensionResource(customizationR.dimen.clock_padding_start)
+ )
+ .padding(top = { viewModel.getSmallClockTopMargin(context) })
+ .onTopPlacementChanged(onTopChanged)
+ .burnInAware(
+ viewModel = aodBurnInViewModel,
+ params = burnInParams,
+ ),
+ )
+ }
+ }
}
@Composable
- private fun SceneScope.LargeClock(modifier: Modifier = Modifier) {
+ fun SceneScope.LargeClock(modifier: Modifier = Modifier) {
val currentClock by viewModel.currentClock.collectAsState()
viewModel.clock = currentClock
if (currentClock?.largeClock?.view == null) {
return
}
- AndroidView(
- factory = { context ->
- FrameLayout(context).apply {
- val newClockView = checkNotNull(currentClock).largeClock.view
- (newClockView.parent as? ViewGroup)?.removeView(newClockView)
- addView(newClockView)
- }
- },
- update = {
- val newClockView = checkNotNull(currentClock).largeClock.view
- it.removeAllViews()
- (newClockView.parent as? ViewGroup)?.removeView(newClockView)
- it.addView(newClockView)
- },
- modifier = modifier.fillMaxSize()
- )
+ MovableElement(key = largeClockElementKey, modifier = modifier) {
+ content {
+ AndroidView(
+ factory = { context ->
+ FrameLayout(context).apply {
+ addClockView(checkNotNull(currentClock).largeClock.view)
+ }
+ },
+ update = { it.addClockView(checkNotNull(currentClock).largeClock.view) },
+ modifier = Modifier.fillMaxSize()
+ )
+ }
+ }
+ }
+
+ private fun FrameLayout.addClockView(clockView: View) {
+ if (contains(clockView)) {
+ return
+ }
+ removeAllViews()
+ (clockView.parent as? ViewGroup)?.removeView(clockView)
+ addView(clockView)
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
index d1cc53e..fc8b3b9 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/SmartSpaceSection.kt
@@ -30,16 +30,20 @@
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import com.android.compose.animation.scene.SceneScope
+import com.android.compose.modifiers.padding
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys
import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
import com.android.systemui.keyguard.ui.composable.modifier.onTopPlacementChanged
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters
import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
+import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
import com.android.systemui.res.R
import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController
import javax.inject.Inject
@@ -51,6 +55,7 @@
private val keyguardUnlockAnimationController: KeyguardUnlockAnimationController,
private val keyguardSmartspaceViewModel: KeyguardSmartspaceViewModel,
private val aodBurnInViewModel: AodBurnInViewModel,
+ private val lockscreenContentViewModel: LockscreenContentViewModel,
) {
@Composable
fun SceneScope.SmartSpace(
@@ -58,57 +63,71 @@
onTopChanged: (top: Float?) -> Unit,
modifier: Modifier = Modifier,
) {
- Column(
- modifier = modifier.onTopPlacementChanged(onTopChanged),
- ) {
- if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) {
- return
- }
+ val resources = LocalContext.current.resources
- val paddingBelowClockStart = dimensionResource(R.dimen.below_clock_padding_start)
- val paddingBelowClockEnd = dimensionResource(R.dimen.below_clock_padding_end)
+ MovableElement(key = ClockElementKeys.smartspaceElementKey, modifier = modifier) {
+ Column(
+ modifier =
+ modifier
+ .onTopPlacementChanged(onTopChanged)
+ .padding(
+ top = { lockscreenContentViewModel.getSmartSpacePaddingTop(resources) },
+ bottom = {
+ resources.getDimensionPixelSize(
+ R.dimen.keyguard_status_view_bottom_margin
+ )
+ }
+ )
+ ) {
+ if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) {
+ return@Column
+ }
- if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) {
- Row(
- verticalAlignment = Alignment.CenterVertically,
+ val paddingBelowClockStart = dimensionResource(R.dimen.below_clock_padding_start)
+ val paddingBelowClockEnd = dimensionResource(R.dimen.below_clock_padding_end)
+
+ if (keyguardSmartspaceViewModel.isDateWeatherDecoupled) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier =
+ Modifier.fillMaxWidth()
+ // All items will be constrained to be as tall as the shortest item.
+ .height(IntrinsicSize.Min)
+ .padding(
+ start = paddingBelowClockStart,
+ ),
+ ) {
+ Date(
+ modifier =
+ Modifier.burnInAware(
+ viewModel = aodBurnInViewModel,
+ params = burnInParams,
+ ),
+ )
+ Spacer(modifier = Modifier.width(4.dp))
+ Weather(
+ modifier =
+ Modifier.burnInAware(
+ viewModel = aodBurnInViewModel,
+ params = burnInParams,
+ ),
+ )
+ }
+ }
+
+ Card(
modifier =
Modifier.fillMaxWidth()
- // All items will be constrained to be as tall as the shortest item.
- .height(IntrinsicSize.Min)
.padding(
start = paddingBelowClockStart,
- ),
- ) {
- Date(
- modifier =
- Modifier.burnInAware(
+ end = paddingBelowClockEnd,
+ )
+ .burnInAware(
viewModel = aodBurnInViewModel,
params = burnInParams,
),
- )
- Spacer(modifier = Modifier.width(4.dp))
- Weather(
- modifier =
- Modifier.burnInAware(
- viewModel = aodBurnInViewModel,
- params = burnInParams,
- ),
- )
- }
+ )
}
-
- Card(
- modifier =
- Modifier.fillMaxWidth()
- .padding(
- start = paddingBelowClockStart,
- end = paddingBelowClockEnd,
- )
- .burnInAware(
- viewModel = aodBurnInViewModel,
- params = burnInParams,
- ),
- )
}
}
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
new file mode 100644
index 0000000..7635841
--- /dev/null
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/TopAreaSection.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2024 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.keyguard.ui.composable.section
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.dimensionResource
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import com.android.compose.animation.scene.SceneTransitionLayout
+import com.android.systemui.Flags
+import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.largeClockScene
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.smallClockScene
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.splitShadeLargeClockScene
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.splitShadeSmallClockScene
+import com.android.systemui.keyguard.ui.composable.blueprint.ClockTransition
+import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn
+import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
+import com.android.systemui.res.R
+import com.android.systemui.shade.LargeScreenHeaderHelper
+import javax.inject.Inject
+
+class TopAreaSection
+@Inject
+constructor(
+ private val clockViewModel: KeyguardClockViewModel,
+ private val smartSpaceSection: SmartSpaceSection,
+ private val mediaCarouselSection: MediaCarouselSection,
+ private val notificationSection: NotificationSection,
+ private val clockSection: DefaultClockSection,
+ private val clockInteractor: KeyguardClockInteractor,
+) {
+ @Composable
+ fun DefaultClockLayoutWithNotifications(
+ modifier: Modifier = Modifier,
+ ) {
+ val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsState()
+ val currentClockLayout by clockViewModel.currentClockLayout.collectAsState()
+ val currentScene =
+ when (currentClockLayout) {
+ KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_LARGE_CLOCK ->
+ splitShadeLargeClockScene
+ KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_SMALL_CLOCK ->
+ splitShadeSmallClockScene
+ KeyguardClockViewModel.ClockLayout.LARGE_CLOCK -> largeClockScene
+ KeyguardClockViewModel.ClockLayout.SMALL_CLOCK -> smallClockScene
+ }
+
+ val splitShadeTopMargin: Dp =
+ if (Flags.centralizedStatusBarHeightFix()) {
+ LargeScreenHeaderHelper.getLargeScreenHeaderHeight(LocalContext.current).dp
+ } else {
+ dimensionResource(id = R.dimen.large_screen_shade_header_height)
+ }
+ val burnIn = rememberBurnIn(clockInteractor)
+
+ LaunchedEffect(isLargeClockVisible) {
+ if (isLargeClockVisible) {
+ burnIn.onSmallClockTopChanged(null)
+ }
+ }
+
+ SceneTransitionLayout(
+ modifier = modifier.fillMaxSize(),
+ currentScene = currentScene,
+ onChangeScene = {},
+ transitions = ClockTransition.defaultClockTransitions,
+ ) {
+ scene(ClockScenes.splitShadeLargeClockScene) {
+ Row(
+ modifier = Modifier.fillMaxSize(),
+ ) {
+ Column(
+ modifier = Modifier.fillMaxHeight().weight(weight = 1f),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ with(smartSpaceSection) {
+ SmartSpace(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmartspaceTopChanged,
+ )
+ }
+ with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+ }
+ with(notificationSection) {
+ Notifications(
+ modifier =
+ Modifier.fillMaxHeight()
+ .weight(weight = 1f)
+ .padding(top = splitShadeTopMargin)
+ )
+ }
+ }
+ }
+
+ scene(ClockScenes.splitShadeSmallClockScene) {
+ Row(
+ modifier = Modifier.fillMaxSize(),
+ ) {
+ Column(
+ modifier = Modifier.fillMaxHeight().weight(weight = 1f),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ with(clockSection) {
+ SmallClock(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmallClockTopChanged,
+ modifier = Modifier.fillMaxWidth()
+ )
+ }
+ with(smartSpaceSection) {
+ SmartSpace(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmartspaceTopChanged,
+ )
+ }
+ with(mediaCarouselSection) { MediaCarousel() }
+ }
+ with(notificationSection) {
+ Notifications(
+ modifier =
+ Modifier.fillMaxHeight()
+ .weight(weight = 1f)
+ .padding(top = splitShadeTopMargin)
+ )
+ }
+ }
+ }
+
+ scene(ClockScenes.smallClockScene) {
+ Column {
+ with(clockSection) {
+ SmallClock(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmallClockTopChanged,
+ modifier = Modifier.fillMaxWidth()
+ )
+ }
+ with(smartSpaceSection) {
+ SmartSpace(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmartspaceTopChanged,
+ )
+ }
+ with(mediaCarouselSection) { MediaCarousel() }
+ with(notificationSection) {
+ Notifications(
+ modifier =
+ androidx.compose.ui.Modifier.fillMaxWidth().weight(weight = 1f)
+ )
+ }
+ }
+ }
+
+ scene(ClockScenes.largeClockScene) {
+ Column {
+ with(smartSpaceSection) {
+ SmartSpace(
+ burnInParams = burnIn.parameters,
+ onTopChanged = burnIn.onSmartspaceTopChanged,
+ )
+ }
+ with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
index ad1cef1..751ac1d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt
@@ -26,7 +26,8 @@
import com.android.systemui.keyguard.data.repository.fakeKeyguardClockRepository
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.testScope
-import com.android.systemui.res.R
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -47,7 +48,7 @@
fun setup() {
with(kosmos) {
fakeFeatureFlagsClassic.set(Flags.LOCK_SCREEN_LONG_PRESS_ENABLED, true)
- overrideResource(R.bool.config_use_split_notification_shade, false)
+ shadeRepository.setShadeMode(ShadeMode.Single)
underTest = lockscreenContentViewModel
}
}
@@ -92,10 +93,10 @@
fun areNotificationsVisible_splitShadeTrue_true() =
with(kosmos) {
testScope.runTest {
- overrideResource(R.bool.config_use_split_notification_shade, true)
+ shadeRepository.setShadeMode(ShadeMode.Split)
kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
- assertThat(underTest.areNotificationsVisible(context.resources)).isTrue()
+ assertThat(underTest.areNotificationsVisible).isTrue()
}
}
@Test
@@ -103,7 +104,7 @@
with(kosmos) {
testScope.runTest {
kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL)
- assertThat(underTest.areNotificationsVisible(context.resources)).isTrue()
+ assertThat(underTest.areNotificationsVisible).isTrue()
}
}
@@ -112,7 +113,34 @@
with(kosmos) {
testScope.runTest {
kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
- assertThat(underTest.areNotificationsVisible(context.resources)).isFalse()
+ assertThat(underTest.areNotificationsVisible).isFalse()
+ }
+ }
+
+ @Test
+ fun shouldUseSplitNotificationShade_withConfigTrue_true() =
+ with(kosmos) {
+ testScope.runTest {
+ shadeRepository.setShadeMode(ShadeMode.Split)
+ assertThat(underTest.shouldUseSplitNotificationShade).isTrue()
+ }
+ }
+
+ @Test
+ fun shouldUseSplitNotificationShade_withConfigFalse_false() =
+ with(kosmos) {
+ testScope.runTest {
+ shadeRepository.setShadeMode(ShadeMode.Single)
+ assertThat(underTest.shouldUseSplitNotificationShade).isFalse()
+ }
+ }
+
+ @Test
+ fun sceneKey() =
+ with(kosmos) {
+ testScope.runTest {
+ shadeRepository.setShadeMode(ShadeMode.Single)
+ assertThat(underTest.shouldUseSplitNotificationShade).isFalse()
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
index c877192..d39bd3d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractor.kt
@@ -92,7 +92,7 @@
when {
useWeatherClockLayout && useSplitShade -> SPLIT_SHADE_WEATHER_CLOCK_BLUEPRINT_ID
useWeatherClockLayout -> WEATHER_CLOCK_BLUEPRINT_ID
- useSplitShade -> SplitShadeKeyguardBlueprint.ID
+ useSplitShade && !ComposeLockscreen.isEnabled -> SplitShadeKeyguardBlueprint.ID
else -> DefaultKeyguardBlueprint.DEFAULT
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
index 58c45c7..b6622e5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt
@@ -28,8 +28,9 @@
import com.android.systemui.keyguard.shared.model.SettingsClockSize
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.res.R
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
-import com.android.systemui.statusbar.policy.SplitShadeStateController
import com.android.systemui.util.Utils
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -46,8 +47,8 @@
keyguardInteractor: KeyguardInteractor,
private val keyguardClockInteractor: KeyguardClockInteractor,
@Application private val applicationScope: CoroutineScope,
- private val splitShadeStateController: SplitShadeStateController,
notifsKeyguardInteractor: NotificationsKeyguardInteractor,
+ private val shadeInteractor: ShadeInteractor,
) {
var burnInLayer: Layer? = null
val useLargeClock: Boolean
@@ -111,12 +112,33 @@
initialValue = false
)
+ val currentClockLayout: StateFlow<ClockLayout> =
+ combine(isLargeClockVisible, clockShouldBeCentered, shadeInteractor.shadeMode) {
+ isLargeClockVisible,
+ clockShouldBeCentered,
+ shadeMode ->
+ val shouldUseSplitShade = shadeMode == ShadeMode.Split
+ when {
+ shouldUseSplitShade && clockShouldBeCentered -> ClockLayout.LARGE_CLOCK
+ shouldUseSplitShade && isLargeClockVisible ->
+ ClockLayout.SPLIT_SHADE_LARGE_CLOCK
+ shouldUseSplitShade -> ClockLayout.SPLIT_SHADE_SMALL_CLOCK
+ isLargeClockVisible -> ClockLayout.LARGE_CLOCK
+ else -> ClockLayout.SMALL_CLOCK
+ }
+ }
+ .stateIn(
+ scope = applicationScope,
+ started = SharingStarted.WhileSubscribed(),
+ initialValue = ClockLayout.SMALL_CLOCK
+ )
+
/** Calculates the top margin for the small clock. */
fun getSmallClockTopMargin(context: Context): Int {
var topMargin: Int
val statusBarHeight = Utils.getStatusBarHeaderHeightKeyguard(context)
- if (splitShadeStateController.shouldUseSplitNotificationShade(context.resources)) {
+ if (shadeInteractor.shadeMode.value == ShadeMode.Split) {
topMargin =
context.resources.getDimensionPixelSize(R.dimen.keyguard_split_shade_top_margin)
if (ComposeLockscreen.isEnabled) {
@@ -130,4 +152,11 @@
}
return topMargin
}
+
+ enum class ClockLayout {
+ LARGE_CLOCK,
+ SMALL_CLOCK,
+ SPLIT_SHADE_LARGE_CLOCK,
+ SPLIT_SHADE_SMALL_CLOCK,
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
index 23320be..1f80441 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt
@@ -23,7 +23,8 @@
import com.android.systemui.keyguard.domain.interactor.KeyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.res.R
-import com.android.systemui.statusbar.policy.SplitShadeStateController
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
@@ -40,7 +41,7 @@
private val interactor: KeyguardBlueprintInteractor,
private val authController: AuthController,
val longPress: KeyguardLongPressViewModel,
- val splitShadeStateController: SplitShadeStateController,
+ val shadeInteractor: ShadeInteractor,
) {
private val clockSize = clockInteractor.clockSize
@@ -48,10 +49,12 @@
get() = authController.isUdfpsSupported
val isLargeClockVisible: Boolean
get() = clockSize.value == KeyguardClockSwitch.LARGE
- fun areNotificationsVisible(resources: Resources): Boolean {
- return !isLargeClockVisible ||
- splitShadeStateController.shouldUseSplitNotificationShade(resources)
- }
+
+ val areNotificationsVisible: Boolean
+ get() = !isLargeClockVisible || shouldUseSplitNotificationShade
+
+ val shouldUseSplitNotificationShade: Boolean
+ get() = shadeInteractor.shadeMode.value == ShadeMode.Split
fun getSmartSpacePaddingTop(resources: Resources): Int {
return if (isLargeClockVisible) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
index 170d348..b6b4571 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardBlueprintInteractorTest.kt
@@ -17,6 +17,8 @@
package com.android.systemui.keyguard.domain.interactor
+import android.platform.test.annotations.DisableFlags
+import android.platform.test.annotations.EnableFlags
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.Flags
@@ -76,6 +78,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
fun testAppliesSplitShadeBlueprint() {
testScope.runTest {
val blueprint by collectLastValue(underTest.blueprint)
@@ -88,6 +91,7 @@
}
@Test
+ @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
fun fingerprintPropertyInitialized_updatesBlueprint() {
testScope.runTest {
val blueprint by collectLastValue(underTest.blueprint)
@@ -99,9 +103,9 @@
}
@Test
+ @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
fun composeLockscreenOff_DoesAppliesSplitShadeWeatherClockBlueprint() {
testScope.runTest {
- mSetFlagsRule.disableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
val blueprint by collectLastValue(underTest.blueprint)
whenever(clockController.config)
.thenReturn(
@@ -121,9 +125,9 @@
}
@Test
+ @EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
fun testDoesAppliesSplitShadeWeatherClockBlueprint() {
testScope.runTest {
- mSetFlagsRule.enableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
val blueprint by collectLastValue(underTest.blueprint)
whenever(clockController.config)
.thenReturn(
@@ -143,9 +147,9 @@
}
@Test
+ @EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
fun testAppliesWeatherClockBlueprint() {
testScope.runTest {
- mSetFlagsRule.enableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
val blueprint by collectLastValue(underTest.blueprint)
whenever(clockController.config)
.thenReturn(
@@ -163,4 +167,18 @@
assertThat(blueprint?.id).isEqualTo(WEATHER_CLOCK_BLUEPRINT_ID)
}
}
+
+ @Test
+ @EnableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN)
+ fun testDoesNotApplySplitShadeBlueprint() {
+ testScope.runTest {
+ overrideResource(R.bool.config_use_split_notification_shade, true)
+ val blueprint by collectLastValue(underTest.blueprint)
+ clockRepository.setCurrentClock(clockController)
+ configurationRepository.onConfigurationChange()
+ runCurrent()
+
+ assertThat(blueprint?.id).isEqualTo(DefaultKeyguardBlueprint.DEFAULT)
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
index f252163..0322301 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelTest.kt
@@ -33,6 +33,8 @@
import com.android.systemui.plugins.clocks.ClockController
import com.android.systemui.plugins.clocks.ClockFaceConfig
import com.android.systemui.plugins.clocks.ClockFaceController
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.shade.shared.model.ShadeMode
import com.android.systemui.shared.clocks.ClockRegistry
import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor
import com.android.systemui.statusbar.policy.SplitShadeStateController
@@ -42,6 +44,7 @@
import kotlin.test.Test
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.StandardTestDispatcher
import kotlinx.coroutines.test.TestCoroutineScheduler
import kotlinx.coroutines.test.TestScope
@@ -72,6 +75,7 @@
@Mock private lateinit var splitShadeStateController: SplitShadeStateController
@Mock private lateinit var notifsKeyguardInteractor: NotificationsKeyguardInteractor
@Mock private lateinit var areNotificationsFullyHidden: Flow<Boolean>
+ @Mock private lateinit var shadeInteractor: ShadeInteractor
@Before
fun setup() {
@@ -96,13 +100,14 @@
keyguardClockInteractor = KeyguardClockInteractor(keyguardClockRepository)
whenever(notifsKeyguardInteractor.areNotificationsFullyHidden)
.thenReturn(areNotificationsFullyHidden)
+ whenever(shadeInteractor.shadeMode).thenReturn(MutableStateFlow(ShadeMode.Single))
underTest =
KeyguardClockViewModel(
keyguardInteractor,
keyguardClockInteractor,
scope.backgroundScope,
- splitShadeStateController,
- notifsKeyguardInteractor
+ notifsKeyguardInteractor,
+ shadeInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelWithKosmosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelWithKosmosTest.kt
new file mode 100644
index 0000000..e53cd11
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelWithKosmosTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 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.keyguard.ui.viewmodel
+
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardClockSwitch
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.keyguardClockRepository
+import com.android.systemui.keyguard.data.repository.keyguardRepository
+import com.android.systemui.kosmos.testScope
+import com.android.systemui.shade.data.repository.shadeRepository
+import com.android.systemui.shade.shared.model.ShadeMode
+import com.android.systemui.testKosmos
+import com.google.common.truth.Truth.assertThat
+import kotlin.test.Test
+import kotlinx.coroutines.test.runTest
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@SmallTest
+@RunWith(JUnit4::class)
+class KeyguardClockViewModelWithKosmosTest : SysuiTestCase() {
+ private val kosmos = testKosmos()
+ private val underTest = kosmos.keyguardClockViewModel
+ private val testScope = kosmos.testScope
+
+ @Test
+ fun currentClockLayout_splitShadeOn_clockCentered_largeClock() =
+ testScope.runTest {
+ with(kosmos) {
+ shadeRepository.setShadeMode(ShadeMode.Split)
+ keyguardRepository.setClockShouldBeCentered(true)
+ keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+ }
+ val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+ assertThat(currentClockLayout).isEqualTo(KeyguardClockViewModel.ClockLayout.LARGE_CLOCK)
+ }
+
+ @Test
+ fun currentClockLayout_splitShadeOn_clockNotCentered_largeClock_splitShadeLargeClock() =
+ testScope.runTest {
+ with(kosmos) {
+ shadeRepository.setShadeMode(ShadeMode.Split)
+ keyguardRepository.setClockShouldBeCentered(false)
+ keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+ }
+ val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+ assertThat(currentClockLayout)
+ .isEqualTo(KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_LARGE_CLOCK)
+ }
+
+ @Test
+ fun currentClockLayout_splitShadeOn_clockNotCentered_smallClock_splitShadeSmallClock() =
+ testScope.runTest {
+ with(kosmos) {
+ shadeRepository.setShadeMode(ShadeMode.Split)
+ keyguardRepository.setClockShouldBeCentered(false)
+ keyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL)
+ }
+ val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+ assertThat(currentClockLayout)
+ .isEqualTo(KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_SMALL_CLOCK)
+ }
+
+ @Test
+ fun currentClockLayout_singleShade_smallClock_smallClock() =
+ testScope.runTest {
+ with(kosmos) {
+ shadeRepository.setShadeMode(ShadeMode.Single)
+ keyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL)
+ }
+ val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+ assertThat(currentClockLayout).isEqualTo(KeyguardClockViewModel.ClockLayout.SMALL_CLOCK)
+ }
+
+ @Test
+ fun currentClockLayout_singleShade_largeClock_largeClock() =
+ testScope.runTest {
+ with(kosmos) {
+ shadeRepository.setShadeMode(ShadeMode.Single)
+ keyguardClockRepository.setClockSize(KeyguardClockSwitch.LARGE)
+ }
+ val currentClockLayout by collectLastValue(underTest.currentClockLayout)
+ assertThat(currentClockLayout).isEqualTo(KeyguardClockViewModel.ClockLayout.LARGE_CLOCK)
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
index 4a85909..60dd48a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt
@@ -20,8 +20,8 @@
import com.android.systemui.keyguard.domain.interactor.keyguardInteractor
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.kosmos.applicationCoroutineScope
+import com.android.systemui.shade.domain.interactor.shadeInteractor
import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor
-import com.android.systemui.statusbar.policy.splitShadeStateController
val Kosmos.keyguardClockViewModel by
Kosmos.Fixture {
@@ -29,7 +29,7 @@
keyguardInteractor = keyguardInteractor,
keyguardClockInteractor = keyguardClockInteractor,
applicationScope = applicationCoroutineScope,
- splitShadeStateController = splitShadeStateController,
notifsKeyguardInteractor = notificationsKeyguardInteractor,
+ shadeInteractor = shadeInteractor,
)
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
index 8da5dd4..f0fedd2 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt
@@ -20,7 +20,7 @@
import com.android.systemui.keyguard.domain.interactor.keyguardBlueprintInteractor
import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor
import com.android.systemui.kosmos.Kosmos
-import com.android.systemui.statusbar.policy.splitShadeStateController
+import com.android.systemui.shade.domain.interactor.shadeInteractor
val Kosmos.lockscreenContentViewModel by
Kosmos.Fixture {
@@ -29,6 +29,6 @@
interactor = keyguardBlueprintInteractor,
authController = authController,
longPress = keyguardLongPressViewModel,
- splitShadeStateController = splitShadeStateController,
+ shadeInteractor = shadeInteractor,
)
}