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

Commit f4b87662 authored by Ale Nijamkin's avatar Ale Nijamkin
Browse files

[flexiglass] Rewrites lockscreen scene's DefaultBlueprint

Using the new LockscreenSceneLayout so it can lay itself out the right
way for all form factors.

Bug: 395060439
Bug: 405383415
Bug: 396218588
Bug: 396202921
Bug: 384077475
Bug: 394875986
Bug: 405366494
Bug: 390988286
Test: manually verified on a foldable device when folded and unfolded.
Also verified manually on a tablet in both orientations and on a device
with UDFPS
Test: unit/integration tests modified and added for view-model changes
Flag: com.android.systemui.scene_container

Change-Id: I40d0dc8d8dd4d25976127ed5b7a2ee0205e11976
parent 8c0e33d5
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -20,11 +20,13 @@ import android.content.res.Configuration
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntRect
@@ -42,6 +44,7 @@ import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.keyguard.ui.composable.layout.LockIconAlignmentLines
import com.android.systemui.keyguard.ui.composable.section.BottomAreaSection
import com.android.systemui.keyguard.ui.composable.section.LockSection
import com.android.systemui.res.R
import com.android.systemui.statusbar.phone.SystemUIDialogFactory
import javax.inject.Inject
import kotlin.math.min
@@ -97,7 +100,12 @@ constructor(
                    }
                    with(bottomAreaSection) {
                        IndicationArea(
                            Modifier.element(Communal.Elements.IndicationArea).fillMaxWidth()
                            Modifier.element(Communal.Elements.IndicationArea)
                                .fillMaxWidth()
                                .padding(
                                    bottom =
                                        dimensionResource(R.dimen.keyguard_indication_margin_bottom)
                                )
                        )
                    }
                },
+67 −208
Original line number Diff line number Diff line
@@ -17,39 +17,26 @@
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.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.graphics.graphicsLayer
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.ContentScope
import com.android.compose.modifiers.padding
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.keyguard.ui.composable.LockscreenTouchHandling
import com.android.systemui.keyguard.ui.composable.layout.LockIconAlignmentLines
import com.android.systemui.keyguard.ui.composable.layout.LockscreenSceneLayout
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.SmartSpaceSection
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.KeyguardClockViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel.NotificationsPlacement.BelowClock
import com.android.systemui.keyguard.ui.viewmodel.LockscreenContentViewModel.NotificationsPlacement.BesideClock
import com.android.systemui.res.R
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
@@ -63,17 +50,18 @@ constructor(
    private val ambientIndicationSectionOptional: Optional<AmbientIndicationSection>,
    private val bottomAreaSection: BottomAreaSection,
    private val settingsMenuSection: SettingsMenuSection,
    private val topAreaSection: TopAreaSection,
    private val notificationSection: NotificationSection,
    private val clockSection: DefaultClockSection,
    private val keyguardClockViewModel: KeyguardClockViewModel,
    private val smartSpaceSection: SmartSpaceSection,
    private val mediaSection: MediaCarouselSection,
) : ComposableLockscreenSceneBlueprint {

    override val id: String = "default"

    @Composable
    override fun ContentScope.Content(viewModel: LockscreenContentViewModel, modifier: Modifier) {
        val isUdfpsVisible = viewModel.isUdfpsVisible
        val isBypassEnabled = viewModel.isBypassEnabled
        val notificationsPlacement = viewModel.notificationsPlacement

        if (isBypassEnabled) {
            with(notificationSection) { HeadsUpNotifications() }
@@ -83,199 +71,70 @@ constructor(
            viewModelFactory = viewModel.touchHandlingFactory,
            modifier = modifier,
        ) { onSettingsMenuPlaced ->
            Layout(
                content = {
                    // Constrained to above the lock icon.
                    Column(modifier = Modifier.fillMaxSize()) {
                        with(statusBarSection) {
                            StatusBar(
                                modifier =
                                    Modifier.fillMaxWidth()
                                        .padding(
                                            horizontal = {
                                                viewModel.unfoldTranslations.start.roundToInt()
                                            }
                                        )
                            )
                        }
            val burnIn = rememberBurnIn(keyguardClockViewModel)

                        Box(modifier = Modifier.fillMaxWidth()) {
                            with(topAreaSection) {
                                DefaultClockLayout(
                                    smartSpacePaddingTop = viewModel::getSmartSpacePaddingTop,
                                    modifier =
                                        Modifier.fillMaxWidth().graphicsLayer {
                                            translationX = viewModel.unfoldTranslations.start
            LockscreenSceneLayout(
                viewModel = viewModel.layout,
                statusBar = {
                    with(statusBarSection) { StatusBar(modifier = Modifier.fillMaxWidth()) }
                },
                smallClock = {
                    with(clockSection) {
                        SmallClock(
                            burnInParams = burnIn.parameters,
                            onTopChanged = burnIn.onSmallClockTopChanged,
                        )
                    }
                            if (notificationsPlacement is BesideClock && !isBypassEnabled) {
                                with(notificationSection) {
                                    Box(modifier = Modifier.fillMaxHeight()) {
                                        AodPromotedNotificationArea(
                                            modifier =
                                                Modifier.fillMaxWidth(0.5f)
                                                    .align(notificationsPlacement.alignment)
                                        )
                                        Notifications(
                                            areNotificationsVisible =
                                                viewModel.areNotificationsVisible,
                                            burnInParams = null,
                                            modifier =
                                                Modifier.fillMaxWidth(0.5f)
                                                    .fillMaxHeight()
                                                    .align(notificationsPlacement.alignment)
                                                    .padding(top = 12.dp),
                },
                largeClock = {
                    with(clockSection) { LargeClock(burnInParams = burnIn.parameters) }
                },
                dateAndWeather = { orientation ->
                    with(smartSpaceSection) { DateAndWeather(orientation) }
                },
                smartSpace = {
                    with(smartSpaceSection) {
                        SmartSpace(
                            burnInParams = burnIn.parameters,
                            onTopChanged = burnIn.onSmartspaceTopChanged,
                            smartSpacePaddingTop = { 0 },
                        )
                    }
                },
                media = {
                    with(mediaSection) {
                        KeyguardMediaCarousel(isShadeLayoutWide = viewModel.isShadeLayoutWide)
                    }
                            }
                        }

                        // Not a mistake; reusing below_clock_padding_start_icons as AOD RON top
                        // padding for now.
                        val aodPromotedNotifTopPadding: Dp =
                            dimensionResource(R.dimen.below_clock_padding_start_icons)
                        val aodIconPadding: Dp =
                            dimensionResource(R.dimen.below_clock_padding_start_icons)

                },
                notifications = {
                    with(notificationSection) {
                            if (notificationsPlacement is BelowClock && !isBypassEnabled) {
                                Box(modifier = Modifier.weight(weight = 1f)) {
                                    Column(Modifier.align(alignment = Alignment.TopStart)) {
                                        AodPromotedNotificationArea(
                                            modifier =
                                                Modifier.padding(top = aodPromotedNotifTopPadding)
                                        )
                                        AodNotificationIcons(
                                            modifier = Modifier.padding(start = aodIconPadding)
                                        )
                                    }
                                    Notifications(
                                        areNotificationsVisible = viewModel.areNotificationsVisible,
                                        burnInParams = null,
                                    )
                                }
                            } else {
                                Column {
                                    if (viewModel.notificationsPlacement is BelowClock) {
                                        AodPromotedNotificationArea(
                                            modifier =
                                                Modifier.padding(top = aodPromotedNotifTopPadding)
                                        )
                                    }
                                    AodNotificationIcons(
                                        modifier =
                                            Modifier.padding(
                                                top =
                                                    dimensionResource(
                                                        R.dimen.keyguard_status_view_bottom_margin
                                                    ),
                                                start = aodIconPadding,
                                            )
                                    )
                                }
                            }
                        }
                        if (!isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
                            with(ambientIndicationSectionOptional.get()) {
                                AmbientIndication(modifier = Modifier.fillMaxWidth())
                            }
                        Box(modifier = Modifier.fillMaxHeight()) {
                            AodPromotedNotificationArea()
                            Notifications(areNotificationsVisible = true, burnInParams = null)
                        }
                    }

                    with(lockSection) { LockIcon() }

                    // Aligned to bottom and constrained to below the lock icon.
                    Column(modifier = Modifier.fillMaxWidth().sysuiResTag("keyguard_root_view")) {
                        if (isUdfpsVisible && ambientIndicationSectionOptional.isPresent) {
                },
                lockIcon = { with(lockSection) { LockIcon() } },
                startShortcut = {
                    with(bottomAreaSection) { Shortcut(isStart = true, applyPadding = false) }
                },
                ambientIndication = {
                    if (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,
                            modifier =
                                Modifier.graphicsLayer {
                                    translationX = viewModel.unfoldTranslations.start
                },
                        )
                        Shortcut(
                            isStart = false,
                            applyPadding = true,
                            modifier =
                                Modifier.graphicsLayer {
                                    translationX = viewModel.unfoldTranslations.end
                bottomIndication = {
                    with(bottomAreaSection) { IndicationArea(modifier = Modifier.fillMaxWidth()) }
                },
                        )
                    }
                    with(settingsMenuSection) { SettingsMenu(onSettingsMenuPlaced) }
                endShortcut = {
                    with(bottomAreaSection) { Shortcut(isStart = false, applyPadding = false) }
                },
                settingsMenu = {
                    with(settingsMenuSection) { SettingsMenu(onPlaced = onSettingsMenuPlaced) }
                },
                modifier = Modifier.fillMaxSize(),
            ) { measurables, constraints ->
                check(measurables.size == 6) { "Expected 6 measurables, got: ${measurables.size}" }
                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[LockIconAlignmentLines.Left],
                        top = lockIconPlaceable[LockIconAlignmentLines.Top],
                        right = lockIconPlaceable[LockIconAlignmentLines.Right],
                        bottom = lockIconPlaceable[LockIconAlignmentLines.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,
            )
        }
    }
}
    }
}
+1 −6
Original line number Diff line number Diff line
@@ -88,7 +88,7 @@ constructor(

    @Composable
    fun ContentScope.IndicationArea(modifier: Modifier = Modifier) {
        Element(key = IndicationAreaElementKey, modifier = modifier.indicationAreaPadding()) {
        Element(key = IndicationAreaElementKey, modifier = modifier) {
            IndicationArea(
                indicationAreaViewModel = indicationAreaViewModel,
                indicationController = indicationController,
@@ -189,11 +189,6 @@ constructor(
            )
            .padding(bottom = dimensionResource(R.dimen.keyguard_affordance_vertical_offset))
    }

    @Composable
    private fun Modifier.indicationAreaPadding(): Modifier {
        return this.padding(bottom = dimensionResource(R.dimen.keyguard_indication_margin_bottom))
    }
}

private val StartButtonElementKey = ElementKey("StartButton")
+6 −10
Original line number Diff line number Diff line
@@ -22,8 +22,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import com.android.compose.animation.scene.ContentScope
import com.android.systemui.keyguard.ui.viewmodel.KeyguardMediaViewModel
import com.android.systemui.lifecycle.rememberViewModel
import com.android.systemui.media.controls.ui.composable.MediaCarousel
import com.android.systemui.media.controls.ui.controller.MediaCarouselController
import com.android.systemui.media.controls.ui.view.MediaHost
@@ -37,24 +35,22 @@ class MediaCarouselSection
constructor(
    private val mediaCarouselController: MediaCarouselController,
    @param:Named(MediaModule.KEYGUARD) private val mediaHost: MediaHost,
    private val keyguardMediaViewModelFactory: KeyguardMediaViewModel.Factory,
) {

    @Composable
    fun ContentScope.KeyguardMediaCarousel(modifier: Modifier = Modifier) {
        val viewModel =
            rememberViewModel(traceName = "KeyguardMediaCarousel") {
                keyguardMediaViewModelFactory.create()
            }
    fun ContentScope.KeyguardMediaCarousel(
        isShadeLayoutWide: Boolean,
        modifier: Modifier = Modifier,
    ) {
        val horizontalPadding =
            if (viewModel.isShadeLayoutWide) {
            if (isShadeLayoutWide) {
                dimensionResource(id = R.dimen.notification_side_paddings)
            } else {
                dimensionResource(id = R.dimen.notification_side_paddings) +
                    dimensionResource(id = R.dimen.notification_panel_margin_horizontal)
            }
        MediaCarousel(
            isVisible = viewModel.isMediaVisible,
            isVisible = true,
            mediaHost = mediaHost,
            modifier = modifier.fillMaxWidth().padding(horizontal = horizontalPadding),
            carouselController = mediaCarouselController,
+32 −7
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.systemui.keyguard.ui.composable.section

import android.content.res.Resources
import android.widget.FrameLayout
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
@@ -125,6 +127,27 @@ constructor(
        }
    }

    @Composable
    fun DateAndWeather(orientation: Orientation, modifier: Modifier = Modifier) {
        when (orientation) {
            Orientation.Horizontal ->
                Row(
                    horizontalArrangement = Arrangement.spacedBy(8.dp),
                    verticalAlignment = Alignment.CenterVertically,
                    modifier = modifier,
                ) {
                    Date()
                    Weather()
                }

            Orientation.Vertical ->
                Column(verticalArrangement = Arrangement.spacedBy(4.dp), modifier = modifier) {
                    Date()
                    Weather()
                }
        }
    }

    @Composable
    private fun Card(modifier: Modifier = Modifier) {
        AndroidView(
@@ -159,7 +182,9 @@ constructor(
            factory = { context ->
                FrameLayout(context).apply {
                    addView(
                        lockscreenSmartspaceController.buildAndConnectWeatherView(this, false).apply {
                        lockscreenSmartspaceController
                            .buildAndConnectWeatherView(this, false)
                            .apply {
                                layoutParams =
                                    FrameLayout.LayoutParams(
                                        FrameLayout.LayoutParams.WRAP_CONTENT,
Loading