diff options
| author | 2024-03-15 00:39:38 +0000 | |
|---|---|---|
| committer | 2024-03-15 00:39:38 +0000 | |
| commit | 0f6a4de28937bcbeefc545c3f1cb59c20def75d9 (patch) | |
| tree | 0dd2840d69b18352422332009e99a16e12661b11 | |
| parent | dd808f63362f15375fb2267b01ac68953d4bedb7 (diff) | |
| parent | a1cfdf6d47dfc60e351dc573f324730698781eaa (diff) | |
Merge "Add Centering animation for large clock" into main
18 files changed, 525 insertions, 466 deletions
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 53f400fac7e5..55f7f69a08d6 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 @@ package com.android.systemui.keyguard.ui.composable 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 @@ import dagger.Module 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 c5ff859def17..d9ed4976bb34 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 @@ object ClockTransition { 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 ClockTransition { 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 9509fd22534a..320c455a8201 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.foundation.layout.fillMaxWidth 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 @@ class DefaultBlueprint 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 @@ constructor( @Composable override fun SceneScope.Content(modifier: Modifier) { val isUdfpsVisible = viewModel.isUdfpsVisible - val resources = LocalContext.current.resources LockscreenLongPress( viewModel = viewModel.longPress, @@ -77,17 +71,7 @@ constructor( 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 9abfa4233a15..64c2cb3670c8 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.foundation.layout.fillMaxWidth 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 @@ class ShortcutsBesideUdfpsBlueprint 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 @@ constructor( @Composable override fun SceneScope.Content(modifier: Modifier) { val isUdfpsVisible = viewModel.isUdfpsVisible - val resources = LocalContext.current.resources LockscreenLongPress( viewModel = viewModel.longPress, @@ -77,16 +71,7 @@ constructor( 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 652412d13aba..000000000000 --- 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 f86623fe935c..ee4e2d697833 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 @@ constructor( 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 1ab2bc76c1fd..82e19e7c154c 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 @@ class DefaultClockSection @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 @@ constructor( 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 d1cc53e093a5..fc8b3b9009ce 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.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.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 @@ constructor( private val keyguardUnlockAnimationController: KeyguardUnlockAnimationController, private val keyguardSmartspaceViewModel: KeyguardSmartspaceViewModel, private val aodBurnInViewModel: AodBurnInViewModel, + private val lockscreenContentViewModel: LockscreenContentViewModel, ) { @Composable fun SceneScope.SmartSpace( @@ -58,57 +63,71 @@ constructor( onTopChanged: (top: Float?) -> Unit, modifier: Modifier = Modifier, ) { - Column( - modifier = modifier.onTopPlacementChanged(onTopChanged), - ) { - if (!keyguardSmartspaceViewModel.isSmartspaceEnabled) { - return - } + val resources = LocalContext.current.resources + + 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 + } - val paddingBelowClockStart = dimensionResource(R.dimen.below_clock_padding_start) - val paddingBelowClockEnd = dimensionResource(R.dimen.below_clock_padding_end) + 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, + 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( - viewModel = aodBurnInViewModel, - params = burnInParams, - ), - ) - Spacer(modifier = Modifier.width(4.dp)) - Weather( - modifier = - Modifier.burnInAware( + end = paddingBelowClockEnd, + ) + .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 000000000000..763584182c97 --- /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 ad1cef1c1e92..751ac1d8b458 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.flags.fakeFeatureFlagsClassic 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 @@ class LockscreenContentViewModelTest : SysuiTestCase() { 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 @@ class LockscreenContentViewModelTest : SysuiTestCase() { 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 @@ class LockscreenContentViewModelTest : SysuiTestCase() { with(kosmos) { testScope.runTest { kosmos.fakeKeyguardClockRepository.setClockSize(KeyguardClockSwitch.SMALL) - assertThat(underTest.areNotificationsVisible(context.resources)).isTrue() + assertThat(underTest.areNotificationsVisible).isTrue() } } @@ -112,7 +113,34 @@ class LockscreenContentViewModelTest : SysuiTestCase() { 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 c877192cd804..d39bd3d5eb12 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 @@ constructor( 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 58c45c74815c..b6622e5c07b1 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.ComposeLockscreen 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 @@ constructor( 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 @@ constructor( 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 @@ constructor( } 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 23320be129fd..1f80441492bc 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.dagger.SysUISingleton 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 @@ constructor( 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 @@ constructor( 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 170d3486decb..b6b457142a3f 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 @@ class KeyguardBlueprintInteractorTest : SysuiTestCase() { } @Test + @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN) fun testAppliesSplitShadeBlueprint() { testScope.runTest { val blueprint by collectLastValue(underTest.blueprint) @@ -88,6 +91,7 @@ class KeyguardBlueprintInteractorTest : SysuiTestCase() { } @Test + @DisableFlags(Flags.FLAG_COMPOSE_LOCKSCREEN) fun fingerprintPropertyInitialized_updatesBlueprint() { testScope.runTest { val blueprint by collectLastValue(underTest.blueprint) @@ -99,9 +103,9 @@ class KeyguardBlueprintInteractorTest : SysuiTestCase() { } @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 @@ class KeyguardBlueprintInteractorTest : SysuiTestCase() { } @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 @@ class KeyguardBlueprintInteractorTest : SysuiTestCase() { } @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 @@ class KeyguardBlueprintInteractorTest : SysuiTestCase() { 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 f252163ee332..0322301b3d6b 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.keyguard.domain.interactor.KeyguardInteractorFactory 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 com.google.common.truth.Truth.assertThat 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 @@ class KeyguardClockViewModelTest : SysuiTestCase() { @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 @@ class KeyguardClockViewModelTest : SysuiTestCase() { 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 000000000000..e53cd11ebe48 --- /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 4a85909ae996..60dd48aeaf61 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.keyguardClockInteractor 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 @@ val Kosmos.keyguardClockViewModel by 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 8da5dd46b62a..f0fedd2ed479 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.biometrics.authController 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 @@ val Kosmos.lockscreenContentViewModel by interactor = keyguardBlueprintInteractor, authController = authController, longPress = keyguardLongPressViewModel, - splitShadeStateController = splitShadeStateController, + shadeInteractor = shadeInteractor, ) } |