Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 53c2e8ee authored by Hawkwood's avatar Hawkwood
Browse files

Modify 2-column layout to handle both dual and split shade

The two column layout mode for flexiglass lockscreen assumes that the
only supported notification position is start aligned notifications.
This is problematic becuase notification alignment on lockscreen is
aligned the same way as they are in the shade. Since the position of
notifications in the shade is becoming dependent on the dual-shade
user setting, we must support both notification positions seamlessly.

This is slightly tricky because notification alignment (and therefore
shade mode) informs other design decisions like placement of the big
clock and the clock size in certain scenarios. As a result, this change
moves responsibility for layout of lockscreen elements above the lock
icon away from LockscreenSceneLayout and into a new element responsible
for layout of the "UpperRegion". Doing this reduces somewhat the the
flexibility afforded by using a Compose Layout, but ends up drastically
simplifying LockscreenSceneLayout and it's associated ViewModel as well
as making each version of the layout easier to reason about.

To facilitate this shift in responsibilities, this change also migrates
the media and notification elements to use the LockscreenElementFactory.
Further, we are able to simplify LockscreenContentViewModel by moving
some of the hydrated layout properties into a new ViewModel.

Bug: 432451019
Test: Manually checked 2-column layout
Flag: com.android.systemui.scene_container
Change-Id: Ibbc904fb8261f3acb4af17f8564e21e6ba35327a
parent d14ff733
Loading
Loading
Loading
Loading
+34 −60
Original line number Diff line number Diff line
@@ -16,24 +16,25 @@

package com.android.systemui.keyguard.ui.composable.blueprint

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ContentScope
import com.android.systemui.keyguard.ui.composable.LockscreenTouchHandling
import com.android.systemui.keyguard.ui.composable.element.AmbientIndicationElement
import com.android.systemui.keyguard.ui.composable.element.AodNotificationIconsElement
import com.android.systemui.keyguard.ui.composable.element.AodPromotedNotificationAreaElement
import com.android.systemui.keyguard.ui.composable.element.AodNotificationIconsElementProvider
import com.android.systemui.keyguard.ui.composable.element.AodPromotedNotificationAreaElementProvider
import com.android.systemui.keyguard.ui.composable.element.ClockRegionElementProvider
import com.android.systemui.keyguard.ui.composable.element.IndicationAreaElement
import com.android.systemui.keyguard.ui.composable.element.LockElement
import com.android.systemui.keyguard.ui.composable.element.MediaCarouselElement
import com.android.systemui.keyguard.ui.composable.element.NotificationElement
import com.android.systemui.keyguard.ui.composable.element.LockscreenElementFactoryImpl
import com.android.systemui.keyguard.ui.composable.element.LockscreenElementFactoryImpl.Companion.createRemembered
import com.android.systemui.keyguard.ui.composable.element.LockscreenUpperRegionElementProvider
import com.android.systemui.keyguard.ui.composable.element.MediaElementProvider
import com.android.systemui.keyguard.ui.composable.element.NotificationStackElementProvider
import com.android.systemui.keyguard.ui.composable.element.SettingsMenuElement
import com.android.systemui.keyguard.ui.composable.element.ShortcutElement
import com.android.systemui.keyguard.ui.composable.element.SmartspaceElementProvider
@@ -43,59 +44,50 @@ import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
import com.android.systemui.log.LogBuffer
import com.android.systemui.log.dagger.KeyguardBlueprintLog
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementContext
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementFactory
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys.ClockSmall
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys.SmartspaceCards
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys
import java.util.Optional
import javax.inject.Inject

/**
 * Renders the lockscreen scene when showing with the default layout (e.g. vertical phone form
 * factor).
 */
/** Renders the lockscreen scene when showing a standard phone or tablet layout */
class DefaultBlueprint
@Inject
constructor(
    private val keyguardClockViewModel: KeyguardClockViewModel,
    private val aodBurnInViewModel: AodBurnInViewModel,
    private val statusBarElement: StatusBarElement,
    private val upperRegionElementProvider: LockscreenUpperRegionElementProvider,
    private val lockElement: LockElement,
    private val ambientIndicationElementOptional: Optional<AmbientIndicationElement>,
    private val shortcutElement: ShortcutElement,
    private val indicationAreaElement: IndicationAreaElement,
    private val settingsMenuElement: SettingsMenuElement,
    private val notificationsElement: NotificationElement,
    private val aodPromotedNotificationAreaElement: AodPromotedNotificationAreaElement,
    private val aodNotificationIconsElement: AodNotificationIconsElement,
    private val notificationStackElementProvider: NotificationStackElementProvider,
    private val aodNotificationIconElementProvider: AodNotificationIconsElementProvider,
    private val aodPromotedNotificationElementProvider: AodPromotedNotificationAreaElementProvider,
    private val smartspaceElementProvider: SmartspaceElementProvider,
    private val clockRegionElementProvider: ClockRegionElementProvider,
    private val mediaCarouselElement: MediaCarouselElement,
    @KeyguardBlueprintLog private val blueprintLog: LogBuffer,
    private val mediaElementProvider: MediaElementProvider,
    private val elementFactoryBuilder: LockscreenElementFactoryImpl.Builder,
) : ComposableLockscreenSceneBlueprint {

    override val id: String = "default"

    @Composable
    override fun ContentScope.Content(viewModel: LockscreenContentViewModel, modifier: Modifier) {
        val currentClock = keyguardClockViewModel.currentClock.collectAsStateWithLifecycle()
        val currentClock by keyguardClockViewModel.currentClock.collectAsStateWithLifecycle()
        val elementFactory =
            remember(
                currentClock,
                smartspaceElementProvider.elements,
                clockRegionElementProvider.elements,
            ) {
                LockscreenElementFactory.build(blueprintLog) { putAll ->
                    putAll(smartspaceElementProvider.elements)
                    putAll(clockRegionElementProvider.elements)
                    currentClock.value?.apply {
                        putAll(smallClock.layout.elements)
                        putAll(largeClock.layout.elements)
                    }
                }
            }
            elementFactoryBuilder.createRemembered(
                upperRegionElementProvider,
                mediaElementProvider,
                smartspaceElementProvider,
                clockRegionElementProvider,
                notificationStackElementProvider,
                aodNotificationIconElementProvider,
                aodPromotedNotificationElementProvider,
                currentClock?.smallClock?.layout,
                currentClock?.largeClock?.layout,
            )

        val burnIn = rememberBurnIn(keyguardClockViewModel)
        val elementContext =
@@ -108,14 +100,17 @@ constructor(
                    ),
                onElementPositioned = { key, rect ->
                    when (key) {
                        ClockSmall -> {
                        LockscreenElementKeys.Clock.Small -> {
                            burnIn.onSmallClockTopChanged(rect.top)
                            viewModel.setSmallClockBottom(rect.bottom)
                        }
                        SmartspaceCards -> {
                        LockscreenElementKeys.Smartspace.Cards -> {
                            burnIn.onSmartspaceTopChanged(rect.top)
                            viewModel.setSmartspaceCardBottom(rect.bottom)
                        }
                        LockscreenElementKeys.MediaCarousel -> {
                            viewModel.setMediaPlayerBottom(rect.bottom)
                        }
                    }
                },
            )
@@ -131,27 +126,6 @@ constructor(
                statusBar = {
                    with(statusBarElement) { StatusBar(modifier = Modifier.fillMaxWidth()) }
                },
                media = {
                    with(mediaCarouselElement) {
                        KeyguardMediaCarousel(
                            isFullWidthShade = viewModel.isFullWidthShade,
                            onBottomChanged = viewModel::setMediaPlayerBottom,
                        )
                    }
                },
                notifications = {
                    Box(modifier = Modifier.fillMaxHeight()) {
                        Column {
                            with(aodPromotedNotificationAreaElement) {
                                AodPromotedNotificationArea()
                            }
                            with(aodNotificationIconsElement) { AodNotificationIcons() }
                        }
                        with(notificationsElement) {
                            Notifications(areNotificationsVisible = true, burnInParams = null)
                        }
                    }
                },
                lockIcon = { with(lockElement) { LockIcon() } },
                startShortcut = {
                    with(shortcutElement) {
+35 −11
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.keyguard.ui.composable.element

import android.content.Context
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.fadeIn
@@ -27,17 +28,21 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ContentScope
import com.android.compose.modifiers.padding
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn
import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElement
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementContext
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementFactory
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementProvider
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.icon.ui.viewbinder.AlwaysOnDisplayNotificationIconViewStore
@@ -50,24 +55,43 @@ import com.android.systemui.util.ui.isAnimating
import com.android.systemui.util.ui.stopAnimating
import com.android.systemui.util.ui.value
import javax.inject.Inject
import kotlin.collections.List
import kotlinx.coroutines.launch

@SysUISingleton
class AodNotificationIconsElement
class AodNotificationIconsElementProvider
@Inject
constructor(
    private val aodBurnInViewModel: AodBurnInViewModel,
    @ShadeDisplayAware private val context: Context,
    private val keyguardRootViewModel: KeyguardRootViewModel,
    @ShadeDisplayAware private val configurationState: ConfigurationState,
    private val iconBindingFailureTracker: StatusBarIconViewBindingFailureTracker,
    private val nicAodViewModel: NotificationIconContainerAlwaysOnDisplayViewModel,
    private val nicAodIconViewStore: AlwaysOnDisplayNotificationIconViewStore,
    @ShadeDisplayAware private val systemBarUtilsState: SystemBarUtilsState,
    private val keyguardClockViewModel: KeyguardClockViewModel,
) : LockscreenElementProvider {
    override val elements: List<LockscreenElement> by lazy { listOf(aodNotificationElement) }

    private val aodNotificationElement =
        object : LockscreenElement {
            override val key = LockscreenElementKeys.Notifications.AOD.IconShelf
            override val context = this@AodNotificationIconsElementProvider.context

            @Composable
            override fun ContentScope.LockscreenElement(
                factory: LockscreenElementFactory,
                context: LockscreenElementContext,
            ) {
                AodNotificationIcons(factory, context)
            }
        }

    @Composable
    fun AodNotificationIcons(modifier: Modifier = Modifier) {
    private fun ContentScope.AodNotificationIcons(
        factory: LockscreenElementFactory,
        context: LockscreenElementContext,
        modifier: Modifier = Modifier,
    ) {
        val isVisible by
            keyguardRootViewModel.isNotifIconContainerVisible.collectAsStateWithLifecycle()
        val transitionState = remember { MutableTransitionState(isVisible.value) }
@@ -77,7 +101,7 @@ constructor(
                isVisible.stopAnimating()
            }
        }
        val burnIn = rememberBurnIn(keyguardClockViewModel)

        AnimatedVisibility(
            visibleState = transitionState,
            enter = fadeIn(),
@@ -89,7 +113,7 @@ constructor(
                        start = dimensionResource(R.dimen.below_clock_padding_start_icons),
                        end = dimensionResource(R.dimen.shelf_icon_container_padding),
                    )
                    .burnInAware(aodBurnInViewModel, burnIn.parameters),
                    .then(context.burnInModifier),
        ) {
            val scope = rememberCoroutineScope()
            AndroidView(
+35 −14
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.keyguard.ui.composable.element

import android.content.Context
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
@@ -26,15 +27,19 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ContentScope
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.ui.composable.blueprint.rememberBurnIn
import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElement
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementContext
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementFactory
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementProvider
import com.android.systemui.shade.ShadeDisplayAware
import com.android.systemui.statusbar.notification.promoted.AODPromotedNotification
import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi
import com.android.systemui.statusbar.notification.promoted.ui.viewmodel.AODPromotedNotificationViewModel
@@ -42,23 +47,40 @@ import com.android.systemui.util.ui.isAnimating
import com.android.systemui.util.ui.stopAnimating
import com.android.systemui.util.ui.value
import javax.inject.Inject
import kotlin.collections.List

@SysUISingleton
class AodPromotedNotificationAreaElement
class AodPromotedNotificationAreaElementProvider
@Inject
constructor(
    private val aodBurnInViewModel: AodBurnInViewModel,
    @ShadeDisplayAware private val context: Context,
    private val keyguardRootViewModel: KeyguardRootViewModel,
    private val aodPromotedNotificationViewModelFactory: AODPromotedNotificationViewModel.Factory,
    private val keyguardClockViewModel: KeyguardClockViewModel,
) {
) : LockscreenElementProvider {
    override val elements: List<LockscreenElement> by lazy { listOf(promotedNotificationElement) }

    private val promotedNotificationElement =
        object : LockscreenElement {
            override val key = LockscreenElementKeys.Notifications.AOD.Promoted
            override val context = this@AodPromotedNotificationAreaElementProvider.context

            @Composable
    fun AodPromotedNotificationArea(modifier: Modifier = Modifier) {
        if (!PromotedNotificationUi.isEnabled) {
            return
            override fun ContentScope.LockscreenElement(
                factory: LockscreenElementFactory,
                context: LockscreenElementContext,
            ) {
                if (PromotedNotificationUi.isEnabled) {
                    AodPromotedNotificationArea(factory, context)
                }
            }
        }

    @Composable
    private fun ContentScope.AodPromotedNotificationArea(
        factory: LockscreenElementFactory,
        context: LockscreenElementContext,
        modifier: Modifier = Modifier,
    ) {
        val isVisible by
            keyguardRootViewModel.isAodPromotedNotifVisible.collectAsStateWithLifecycle()
        val transitionState = remember { MutableTransitionState(isVisible.value) }
@@ -68,13 +90,12 @@ constructor(
                isVisible.stopAnimating()
            }
        }
        val burnIn = rememberBurnIn(keyguardClockViewModel)

        AnimatedVisibility(
            visibleState = transitionState,
            enter = if (isVisible.isAnimating) fadeIn() else EnterTransition.None,
            exit = if (isVisible.isAnimating) fadeOut() else ExitTransition.None,
            modifier = modifier.burnInAware(aodBurnInViewModel, burnIn.parameters),
            modifier = modifier.then(context.burnInModifier),
        ) {
            AODPromotedNotification(
                viewModelFactory = aodPromotedNotificationViewModelFactory,
+20 −23
Original line number Diff line number Diff line
@@ -36,18 +36,14 @@ import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElement
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementContext
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementFactory
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys.ClockLarge
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys.ClockRegionLarge
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys.ClockRegionSmall
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys.ClockSmall
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys.SmartspaceCards
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys.SmartspaceDateLargeClock
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys.SmartspaceDateSmallClock
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys.SmartspaceWeatherLargeClock
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys.SmartspaceWeatherSmallClock
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementFactory.Companion.lockscreenElement
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys.Clock
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys.Smartspace
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementProvider
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject
import kotlin.collections.List

/** Provides default clock regions, if not overridden by the clock itself */
class ClockRegionElementProvider
@@ -56,11 +52,13 @@ constructor(
    @ShadeDisplayAware private val context: Context,
    private val keyguardClockViewModel: KeyguardClockViewModel,
) : LockscreenElementProvider {
    override val elements by lazy { listOf(smallClockRegionElement, largeClockRegionElement) }
    override val elements: List<LockscreenElement> by lazy {
        listOf(smallClockRegionElement, largeClockRegionElement)
    }

    private val smallClockRegionElement =
        object : LockscreenElement {
            override val key = ClockRegionSmall
            override val key = LockscreenElementKeys.Clock.Region.Small
            override val context = this@ClockRegionElementProvider.context

            @Composable
@@ -82,7 +80,7 @@ constructor(
                        verticalAlignment = Alignment.CenterVertically,
                        modifier = paddingModifier,
                    ) {
                        factory.lockscreenElement(ClockSmall, context)
                        factory.lockscreenElement(Clock.Small, context)

                        if (!shouldDateWeatherBeBelowSmallClock) {
                            Column(
@@ -90,8 +88,8 @@ constructor(
                                verticalArrangement = Arrangement.spacedBy(4.dp),
                                modifier = context.burnInModifier,
                            ) {
                                factory.lockscreenElement(SmartspaceDateSmallClock, context)
                                factory.lockscreenElement(SmartspaceWeatherSmallClock, context)
                                factory.lockscreenElement(Smartspace.Date.SmallClock, context)
                                factory.lockscreenElement(Smartspace.Weather.SmallClock, context)
                            }
                        }
                    }
@@ -102,19 +100,19 @@ constructor(
                            verticalAlignment = Alignment.CenterVertically,
                            modifier = paddingModifier.then(context.burnInModifier),
                        ) {
                            factory.lockscreenElement(SmartspaceDateSmallClock, context)
                            factory.lockscreenElement(SmartspaceWeatherSmallClock, context)
                            factory.lockscreenElement(Smartspace.Date.SmallClock, context)
                            factory.lockscreenElement(Smartspace.Weather.SmallClock, context)
                        }
                    }

                    factory.lockscreenElement(SmartspaceCards, context)
                    factory.lockscreenElement(Smartspace.Cards, context)
                }
            }
        }

    private val largeClockRegionElement =
        object : LockscreenElement {
            override val key = ClockRegionLarge
            override val key = LockscreenElementKeys.Clock.Region.Large
            override val context = this@ClockRegionElementProvider.context

            @Composable
@@ -126,17 +124,16 @@ constructor(
                    horizontalAlignment = Alignment.CenterHorizontally,
                    verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterVertically),
                ) {
                    factory.lockscreenElement(SmartspaceCards, context)
                    factory.lockscreenElement(ClockLarge, context)
                    factory.lockscreenElement(Smartspace.Cards, context)
                    factory.lockscreenElement(Clock.Large, context)

                    // TODO(b/432451019): This ends up with 0 height and doesn't render
                    Row(
                        horizontalArrangement = Arrangement.spacedBy(8.dp),
                        verticalAlignment = Alignment.CenterVertically,
                        modifier = context.burnInModifier,
                    ) {
                        factory.lockscreenElement(SmartspaceDateLargeClock, context)
                        factory.lockscreenElement(SmartspaceWeatherLargeClock, context)
                        factory.lockscreenElement(Smartspace.Date.LargeClock, context)
                        factory.lockscreenElement(Smartspace.Weather.LargeClock, context)
                    }
                }
            }
+98 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading