Loading packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt +33 −4 Original line number Diff line number Diff line Loading @@ -26,9 +26,11 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.layout.Layout import androidx.compose.ui.unit.IntRect import com.android.compose.animation.scene.SceneScope import com.android.compose.modifiers.padding 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 Loading @@ -43,6 +45,7 @@ import dagger.Module import dagger.multibindings.IntoSet import java.util.Optional import javax.inject.Inject import kotlin.math.roundToInt /** * Renders the lockscreen scene when showing with the default layout (e.g. vertical phone form Loading @@ -68,6 +71,7 @@ constructor( val isUdfpsVisible = viewModel.isUdfpsVisible val shouldUseSplitNotificationShade by viewModel.shouldUseSplitNotificationShade.collectAsState() val unfoldTranslations by viewModel.unfoldTranslations.collectAsState() LockscreenLongPress( viewModel = viewModel.longPress, Loading @@ -79,10 +83,25 @@ constructor( Column( modifier = Modifier.fillMaxSize(), ) { with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) } with(statusBarSection) { StatusBar( modifier = Modifier.fillMaxWidth() .padding( horizontal = { unfoldTranslations.start.roundToInt() }, ) ) } Box { with(topAreaSection) { DefaultClockLayout() } with(topAreaSection) { DefaultClockLayout( modifier = Modifier.graphicsLayer { translationX = unfoldTranslations.start } ) } if (shouldUseSplitNotificationShade) { with(notificationSection) { Notifications( Loading Loading @@ -127,8 +146,18 @@ constructor( // Aligned to bottom and NOT constrained by the lock icon. with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = true) Shortcut(isStart = false, applyPadding = true) Shortcut( isStart = true, applyPadding = true, modifier = Modifier.graphicsLayer { translationX = unfoldTranslations.start }, ) Shortcut( isStart = false, applyPadding = true, modifier = Modifier.graphicsLayer { translationX = unfoldTranslations.end }, ) } with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) } }, Loading packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt +37 −4 Original line number Diff line number Diff line Loading @@ -26,9 +26,11 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.layout.Layout import androidx.compose.ui.unit.IntRect import com.android.compose.animation.scene.SceneScope import com.android.compose.modifiers.padding 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 Loading @@ -43,6 +45,7 @@ import dagger.Module import dagger.multibindings.IntoSet import java.util.Optional import javax.inject.Inject import kotlin.math.roundToInt /** * Renders the lockscreen scene when showing with the default layout (e.g. vertical phone form Loading @@ -68,6 +71,7 @@ constructor( val isUdfpsVisible = viewModel.isUdfpsVisible val shouldUseSplitNotificationShade by viewModel.shouldUseSplitNotificationShade.collectAsState() val unfoldTranslations by viewModel.unfoldTranslations.collectAsState() LockscreenLongPress( viewModel = viewModel.longPress, Loading @@ -79,10 +83,25 @@ constructor( Column( modifier = Modifier.fillMaxSize(), ) { with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) } with(statusBarSection) { StatusBar( modifier = Modifier.fillMaxWidth() .padding( horizontal = { unfoldTranslations.start.roundToInt() }, ) ) } Box { with(topAreaSection) { DefaultClockLayout() } with(topAreaSection) { DefaultClockLayout( modifier = Modifier.graphicsLayer { translationX = unfoldTranslations.start }, ) } if (shouldUseSplitNotificationShade) { with(notificationSection) { Notifications( Loading Loading @@ -111,12 +130,26 @@ constructor( } // Constrained to the left of the lock icon (in left-to-right layouts). with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = false) } with(bottomAreaSection) { Shortcut( isStart = true, applyPadding = false, modifier = Modifier.graphicsLayer { translationX = unfoldTranslations.start }, ) } with(lockSection) { LockIcon() } // Constrained to the right of the lock icon (in left-to-right layouts). with(bottomAreaSection) { Shortcut(isStart = false, applyPadding = false) } with(bottomAreaSection) { Shortcut( isStart = false, applyPadding = false, modifier = Modifier.graphicsLayer { translationX = unfoldTranslations.end }, ) } // Aligned to bottom and constrained to below the lock icon. Column(modifier = Modifier.fillMaxWidth()) { Loading packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt +47 −0 Original line number Diff line number Diff line Loading @@ -21,17 +21,21 @@ import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardClockSwitch import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.authController import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.Flags 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.unfold.fakeUnfoldTransitionProgressProvider import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import java.util.Locale import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test Loading Loading @@ -137,4 +141,47 @@ class LockscreenContentViewModelTest : SysuiTestCase() { .isFalse() } } @Test fun unfoldTranslations() = with(kosmos) { testScope.runTest { val maxTranslation = prepareConfiguration() val translations by collectLastValue(underTest.unfoldTranslations) val unfoldProvider = fakeUnfoldTransitionProgressProvider unfoldProvider.onTransitionStarted() assertThat(translations?.start).isEqualTo(0f) assertThat(translations?.end).isEqualTo(-0f) repeat(10) { repetition -> val transitionProgress = 0.1f * (repetition + 1) unfoldProvider.onTransitionProgress(transitionProgress) assertThat(translations?.start) .isEqualTo((1 - transitionProgress) * maxTranslation) assertThat(translations?.end) .isEqualTo(-(1 - transitionProgress) * maxTranslation) } unfoldProvider.onTransitionFinishing() assertThat(translations?.start).isEqualTo(0f) assertThat(translations?.end).isEqualTo(-0f) unfoldProvider.onTransitionFinished() assertThat(translations?.start).isEqualTo(0f) assertThat(translations?.end).isEqualTo(-0f) } } private fun prepareConfiguration(): Int { val configuration = context.resources.configuration configuration.setLayoutDirection(Locale.US) kosmos.fakeConfigurationRepository.onConfigurationChange(configuration) val maxTranslation = 10 kosmos.fakeConfigurationRepository.setDimensionPixelSize( R.dimen.notification_side_paddings, maxTranslation, ) return maxTranslation } } packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt +35 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor 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.unfold.domain.interactor.UnfoldTransitionInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted Loading @@ -46,6 +47,7 @@ constructor( val longPress: KeyguardLongPressViewModel, val shadeInteractor: ShadeInteractor, @Application private val applicationScope: CoroutineScope, private val unfoldTransitionInteractor: UnfoldTransitionInteractor, ) { private val clockSize = clockInteractor.clockSize Loading Loading @@ -75,6 +77,23 @@ constructor( initialValue = false, ) /** Amount of horizontal translation that should be applied to elements in the scene. */ val unfoldTranslations: StateFlow<UnfoldTranslations> = combine( unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide = true), unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide = false), ) { start, end -> UnfoldTranslations( start = start, end = end, ) } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = UnfoldTranslations(), ) fun getSmartSpacePaddingTop(resources: Resources): Int { return if (isLargeClockVisible) { resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) + Loading @@ -94,4 +113,20 @@ constructor( initialValue = interactor.getCurrentBlueprint().id, ) } data class UnfoldTranslations( /** * Amount of horizontal translation to apply to elements that are aligned to the start side * (left in left-to-right layouts). Can also be used as horizontal padding for elements that * need horizontal padding on both side. In pixels. */ val start: Float = 0f, /** * Amount of horizontal translation to apply to elements that are aligned to the end side * (right in left-to-right layouts). In pixels. */ val end: Float = 0f, ) } packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt +2 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor val Kosmos.lockscreenContentViewModel by Kosmos.Fixture { Loading @@ -32,5 +33,6 @@ val Kosmos.lockscreenContentViewModel by longPress = keyguardLongPressViewModel, shadeInteractor = shadeInteractor, applicationScope = applicationCoroutineScope, unfoldTransitionInteractor = unfoldTransitionInteractor, ) } Loading
packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/DefaultBlueprint.kt +33 −4 Original line number Diff line number Diff line Loading @@ -26,9 +26,11 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.layout.Layout import androidx.compose.ui.unit.IntRect import com.android.compose.animation.scene.SceneScope import com.android.compose.modifiers.padding 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 Loading @@ -43,6 +45,7 @@ import dagger.Module import dagger.multibindings.IntoSet import java.util.Optional import javax.inject.Inject import kotlin.math.roundToInt /** * Renders the lockscreen scene when showing with the default layout (e.g. vertical phone form Loading @@ -68,6 +71,7 @@ constructor( val isUdfpsVisible = viewModel.isUdfpsVisible val shouldUseSplitNotificationShade by viewModel.shouldUseSplitNotificationShade.collectAsState() val unfoldTranslations by viewModel.unfoldTranslations.collectAsState() LockscreenLongPress( viewModel = viewModel.longPress, Loading @@ -79,10 +83,25 @@ constructor( Column( modifier = Modifier.fillMaxSize(), ) { with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) } with(statusBarSection) { StatusBar( modifier = Modifier.fillMaxWidth() .padding( horizontal = { unfoldTranslations.start.roundToInt() }, ) ) } Box { with(topAreaSection) { DefaultClockLayout() } with(topAreaSection) { DefaultClockLayout( modifier = Modifier.graphicsLayer { translationX = unfoldTranslations.start } ) } if (shouldUseSplitNotificationShade) { with(notificationSection) { Notifications( Loading Loading @@ -127,8 +146,18 @@ constructor( // Aligned to bottom and NOT constrained by the lock icon. with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = true) Shortcut(isStart = false, applyPadding = true) Shortcut( isStart = true, applyPadding = true, modifier = Modifier.graphicsLayer { translationX = unfoldTranslations.start }, ) Shortcut( isStart = false, applyPadding = true, modifier = Modifier.graphicsLayer { translationX = unfoldTranslations.end }, ) } with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) } }, Loading
packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/blueprint/ShortcutsBesideUdfpsBlueprint.kt +37 −4 Original line number Diff line number Diff line Loading @@ -26,9 +26,11 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.layout.Layout import androidx.compose.ui.unit.IntRect import com.android.compose.animation.scene.SceneScope import com.android.compose.modifiers.padding 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 Loading @@ -43,6 +45,7 @@ import dagger.Module import dagger.multibindings.IntoSet import java.util.Optional import javax.inject.Inject import kotlin.math.roundToInt /** * Renders the lockscreen scene when showing with the default layout (e.g. vertical phone form Loading @@ -68,6 +71,7 @@ constructor( val isUdfpsVisible = viewModel.isUdfpsVisible val shouldUseSplitNotificationShade by viewModel.shouldUseSplitNotificationShade.collectAsState() val unfoldTranslations by viewModel.unfoldTranslations.collectAsState() LockscreenLongPress( viewModel = viewModel.longPress, Loading @@ -79,10 +83,25 @@ constructor( Column( modifier = Modifier.fillMaxSize(), ) { with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) } with(statusBarSection) { StatusBar( modifier = Modifier.fillMaxWidth() .padding( horizontal = { unfoldTranslations.start.roundToInt() }, ) ) } Box { with(topAreaSection) { DefaultClockLayout() } with(topAreaSection) { DefaultClockLayout( modifier = Modifier.graphicsLayer { translationX = unfoldTranslations.start }, ) } if (shouldUseSplitNotificationShade) { with(notificationSection) { Notifications( Loading Loading @@ -111,12 +130,26 @@ constructor( } // Constrained to the left of the lock icon (in left-to-right layouts). with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = false) } with(bottomAreaSection) { Shortcut( isStart = true, applyPadding = false, modifier = Modifier.graphicsLayer { translationX = unfoldTranslations.start }, ) } with(lockSection) { LockIcon() } // Constrained to the right of the lock icon (in left-to-right layouts). with(bottomAreaSection) { Shortcut(isStart = false, applyPadding = false) } with(bottomAreaSection) { Shortcut( isStart = false, applyPadding = false, modifier = Modifier.graphicsLayer { translationX = unfoldTranslations.end }, ) } // Aligned to bottom and constrained to below the lock icon. Column(modifier = Modifier.fillMaxWidth()) { Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelTest.kt +47 −0 Original line number Diff line number Diff line Loading @@ -21,17 +21,21 @@ import androidx.test.filters.SmallTest import com.android.keyguard.KeyguardClockSwitch import com.android.systemui.SysuiTestCase import com.android.systemui.biometrics.authController import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.Flags 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.unfold.fakeUnfoldTransitionProgressProvider import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import java.util.Locale import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test Loading Loading @@ -137,4 +141,47 @@ class LockscreenContentViewModelTest : SysuiTestCase() { .isFalse() } } @Test fun unfoldTranslations() = with(kosmos) { testScope.runTest { val maxTranslation = prepareConfiguration() val translations by collectLastValue(underTest.unfoldTranslations) val unfoldProvider = fakeUnfoldTransitionProgressProvider unfoldProvider.onTransitionStarted() assertThat(translations?.start).isEqualTo(0f) assertThat(translations?.end).isEqualTo(-0f) repeat(10) { repetition -> val transitionProgress = 0.1f * (repetition + 1) unfoldProvider.onTransitionProgress(transitionProgress) assertThat(translations?.start) .isEqualTo((1 - transitionProgress) * maxTranslation) assertThat(translations?.end) .isEqualTo(-(1 - transitionProgress) * maxTranslation) } unfoldProvider.onTransitionFinishing() assertThat(translations?.start).isEqualTo(0f) assertThat(translations?.end).isEqualTo(-0f) unfoldProvider.onTransitionFinished() assertThat(translations?.start).isEqualTo(0f) assertThat(translations?.end).isEqualTo(-0f) } } private fun prepareConfiguration(): Int { val configuration = context.resources.configuration configuration.setLayoutDirection(Locale.US) kosmos.fakeConfigurationRepository.onConfigurationChange(configuration) val maxTranslation = 10 kosmos.fakeConfigurationRepository.setDimensionPixelSize( R.dimen.notification_side_paddings, maxTranslation, ) return maxTranslation } }
packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModel.kt +35 −0 Original line number Diff line number Diff line Loading @@ -27,6 +27,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor 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.unfold.domain.interactor.UnfoldTransitionInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.SharingStarted Loading @@ -46,6 +47,7 @@ constructor( val longPress: KeyguardLongPressViewModel, val shadeInteractor: ShadeInteractor, @Application private val applicationScope: CoroutineScope, private val unfoldTransitionInteractor: UnfoldTransitionInteractor, ) { private val clockSize = clockInteractor.clockSize Loading Loading @@ -75,6 +77,23 @@ constructor( initialValue = false, ) /** Amount of horizontal translation that should be applied to elements in the scene. */ val unfoldTranslations: StateFlow<UnfoldTranslations> = combine( unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide = true), unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide = false), ) { start, end -> UnfoldTranslations( start = start, end = end, ) } .stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = UnfoldTranslations(), ) fun getSmartSpacePaddingTop(resources: Resources): Int { return if (isLargeClockVisible) { resources.getDimensionPixelSize(R.dimen.keyguard_smartspace_top_offset) + Loading @@ -94,4 +113,20 @@ constructor( initialValue = interactor.getCurrentBlueprint().id, ) } data class UnfoldTranslations( /** * Amount of horizontal translation to apply to elements that are aligned to the start side * (left in left-to-right layouts). Can also be used as horizontal padding for elements that * need horizontal padding on both side. In pixels. */ val start: Float = 0f, /** * Amount of horizontal translation to apply to elements that are aligned to the end side * (right in left-to-right layouts). In pixels. */ val end: Float = 0f, ) }
packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenContentViewModelKosmos.kt +2 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor val Kosmos.lockscreenContentViewModel by Kosmos.Fixture { Loading @@ -32,5 +33,6 @@ val Kosmos.lockscreenContentViewModel by longPress = keyguardLongPressViewModel, shadeInteractor = shadeInteractor, applicationScope = applicationCoroutineScope, unfoldTransitionInteractor = unfoldTransitionInteractor, ) }