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

Commit 7d9bac1d authored by Hawkwood's avatar Hawkwood
Browse files

Default element provider for clock regions

This creates an element provider for the clock regions. These are
overriden by any region the clock specifies when the map is constructed,
but keeps the code streamlined in the common case.

This change also improves smartspace rendering by removing the need for
a wrapper FrameLayout in those cases, and moves some padding that was
applied in multiple places. Finally, it also re-enables the layout
callbacks that were previously removed and are used to track the
location of certain on-screen elements.

Bug: 432451019
Bug: 437341450
Bug: 437359698
Test: Checked rendering of clock regions in flexiglass
Flag: com.android.systemui.scene_container
Change-Id: Icdb53149e309899cf6dab90c36704caa498cbdd0
parent 51311c5a
Loading
Loading
Loading
Loading
+31 −4
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ 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.AodPromotedNotificationAreaElement
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
@@ -40,8 +41,12 @@ 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.clocks.LockscreenElementContext
import com.android.systemui.plugins.clocks.LockscreenElementFactory
import com.android.systemui.plugins.clocks.LockscreenElementKeys.ClockSmall
import com.android.systemui.plugins.clocks.LockscreenElementKeys.SmartspaceCards
import java.util.Optional
import javax.inject.Inject

@@ -63,7 +68,9 @@ constructor(
    private val notificationsElement: NotificationElement,
    private val aodPromotedNotificationAreaElement: AodPromotedNotificationAreaElement,
    private val smartspaceElementProvider: SmartspaceElementProvider,
    private val clockRegionElementProvider: ClockRegionElementProvider,
    private val mediaCarouselElement: MediaCarouselElement,
    @KeyguardBlueprintLog private val blueprintLog: LogBuffer,
) : ComposableLockscreenSceneBlueprint {

    override val id: String = "default"
@@ -72,12 +79,17 @@ constructor(
    override fun ContentScope.Content(viewModel: LockscreenContentViewModel, modifier: Modifier) {
        val currentClock = keyguardClockViewModel.currentClock.collectAsStateWithLifecycle()
        val elementFactory =
            remember(currentClock, smartspaceElementProvider.elements) {
                LockscreenElementFactory.build { putAll ->
            remember(
                currentClock,
                smartspaceElementProvider.elements,
                clockRegionElementProvider.elements,
            ) {
                LockscreenElementFactory.build(blueprintLog) { putAll ->
                    putAll(smartspaceElementProvider.elements)
                    putAll(clockRegionElementProvider.elements)
                    currentClock.value?.apply {
                        putAll(largeClock.layout.elements)
                        putAll(smallClock.layout.elements)
                        putAll(largeClock.layout.elements)
                    }
                }
            }
@@ -87,7 +99,22 @@ constructor(
            LockscreenElementContext(
                scope = this,
                burnInModifier =
                    Modifier.burnInAware(viewModel = aodBurnInViewModel, params = burnIn.parameters),
                    Modifier.burnInAware(
                        viewModel = aodBurnInViewModel,
                        params = burnIn.parameters,
                    ),
                onElementPositioned = { key, rect ->
                    when (key) {
                        ClockSmall -> {
                            burnIn.onSmallClockTopChanged(rect.top)
                            viewModel.setSmallClockBottom(rect.bottom)
                        }
                        SmartspaceCards -> {
                            burnIn.onSmartspaceTopChanged(rect.top)
                            viewModel.setSmartspaceCardBottom(rect.bottom)
                        }
                    }
                },
            )

        LockscreenTouchHandling(
+144 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 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.element

import android.content.Context
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ContentScope
import com.android.compose.modifiers.padding
import com.android.systemui.customization.clocks.R as clocksR
import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel
import com.android.systemui.plugins.clocks.LockscreenElement
import com.android.systemui.plugins.clocks.LockscreenElementContext
import com.android.systemui.plugins.clocks.LockscreenElementFactory
import com.android.systemui.plugins.clocks.LockscreenElementKeys.ClockLarge
import com.android.systemui.plugins.clocks.LockscreenElementKeys.ClockRegionLarge
import com.android.systemui.plugins.clocks.LockscreenElementKeys.ClockRegionSmall
import com.android.systemui.plugins.clocks.LockscreenElementKeys.ClockSmall
import com.android.systemui.plugins.clocks.LockscreenElementKeys.SmartspaceCards
import com.android.systemui.plugins.clocks.LockscreenElementKeys.SmartspaceDateLargeClock
import com.android.systemui.plugins.clocks.LockscreenElementKeys.SmartspaceDateSmallClock
import com.android.systemui.plugins.clocks.LockscreenElementKeys.SmartspaceWeatherLargeClock
import com.android.systemui.plugins.clocks.LockscreenElementKeys.SmartspaceWeatherSmallClock
import com.android.systemui.plugins.clocks.LockscreenElementProvider
import com.android.systemui.shade.ShadeDisplayAware
import javax.inject.Inject

/** Provides default clock regions, if not overridden by the clock itself */
class ClockRegionElementProvider
@Inject
constructor(
    @ShadeDisplayAware private val context: Context,
    private val keyguardClockViewModel: KeyguardClockViewModel,
) : LockscreenElementProvider {
    override val elements by lazy { listOf(smallClockRegionElement, largeClockRegionElement) }

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

            @Composable
            override fun ContentScope.LockscreenElement(
                factory: LockscreenElementFactory,
                context: LockscreenElementContext,
            ) {
                val shouldDateWeatherBeBelowSmallClock: Boolean by
                    keyguardClockViewModel.shouldDateWeatherBeBelowSmallClock
                        .collectAsStateWithLifecycle()
                val paddingModifier =
                    Modifier.padding(
                        horizontal = dimensionResource(clocksR.dimen.clock_padding_start)
                    )

                Column {
                    Row(
                        horizontalArrangement = Arrangement.spacedBy(16.dp),
                        verticalAlignment = Alignment.CenterVertically,
                        modifier = paddingModifier,
                    ) {
                        factory.lockscreenElement(ClockSmall, context)

                        if (!shouldDateWeatherBeBelowSmallClock) {
                            Column(
                                horizontalAlignment = Alignment.Start,
                                verticalArrangement = Arrangement.spacedBy(4.dp),
                                modifier = context.burnInModifier,
                            ) {
                                factory.lockscreenElement(SmartspaceDateSmallClock, context)
                                factory.lockscreenElement(SmartspaceWeatherSmallClock, context)
                            }
                        }
                    }

                    if (shouldDateWeatherBeBelowSmallClock) {
                        Row(
                            horizontalArrangement = Arrangement.spacedBy(8.dp),
                            verticalAlignment = Alignment.CenterVertically,
                            modifier = paddingModifier.then(context.burnInModifier),
                        ) {
                            factory.lockscreenElement(SmartspaceDateSmallClock, context)
                            factory.lockscreenElement(SmartspaceWeatherSmallClock, context)
                        }
                    }

                    factory.lockscreenElement(SmartspaceCards, context)
                }
            }
        }

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

            @Composable
            override fun ContentScope.LockscreenElement(
                factory: LockscreenElementFactory,
                context: LockscreenElementContext,
            ) {
                Column(
                    horizontalAlignment = Alignment.CenterHorizontally,
                    verticalArrangement = Arrangement.spacedBy(16.dp, Alignment.CenterVertically),
                ) {
                    factory.lockscreenElement(SmartspaceCards, context)
                    factory.lockscreenElement(ClockLarge, 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)
                    }
                }
            }
        }
}
+70 −140
Original line number Diff line number Diff line
@@ -17,21 +17,18 @@
package com.android.systemui.keyguard.ui.composable.element

import android.content.Context
import android.view.View
import android.widget.FrameLayout
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
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.runtime.getValue
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementKey
import com.android.compose.modifiers.padding
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
@@ -39,7 +36,11 @@ import com.android.systemui.keyguard.ui.viewmodel.KeyguardSmartspaceViewModel
import com.android.systemui.plugins.clocks.LockscreenElement
import com.android.systemui.plugins.clocks.LockscreenElementContext
import com.android.systemui.plugins.clocks.LockscreenElementFactory
import com.android.systemui.plugins.clocks.LockscreenElementKeys
import com.android.systemui.plugins.clocks.LockscreenElementKeys.SmartspaceCards
import com.android.systemui.plugins.clocks.LockscreenElementKeys.SmartspaceDateLargeClock
import com.android.systemui.plugins.clocks.LockscreenElementKeys.SmartspaceDateSmallClock
import com.android.systemui.plugins.clocks.LockscreenElementKeys.SmartspaceWeatherLargeClock
import com.android.systemui.plugins.clocks.LockscreenElementKeys.SmartspaceWeatherSmallClock
import com.android.systemui.plugins.clocks.LockscreenElementProvider
import com.android.systemui.res.R
import com.android.systemui.shade.ShadeDisplayAware
@@ -50,24 +51,25 @@ class SmartspaceElementProvider
@Inject
constructor(
    @ShadeDisplayAware private val context: Context,
    private val lockscreenSmartspaceController: LockscreenSmartspaceController,
    private val smartspaceController: LockscreenSmartspaceController,
    private val keyguardUnlockAnimationController: KeyguardUnlockAnimationController,
    private val keyguardSmartspaceViewModel: KeyguardSmartspaceViewModel,
    private val aodBurnInViewModel: AodBurnInViewModel,
) : LockscreenElementProvider {
    override val elements by lazy {
        listOf(
            dateElement,
            weatherElement,
            dateAndWeatherVerticalElement,
            dateAndWeatherHorizontalElement,
            DateElement(SmartspaceDateLargeClock, isLargeClock = true),
            DateElement(SmartspaceDateSmallClock, isLargeClock = false),
            WeatherElement(SmartspaceWeatherLargeClock, isLargeClock = true),
            WeatherElement(SmartspaceWeatherSmallClock, isLargeClock = false),
            cardsElement,
        )
    }

    private val dateElement =
        object : LockscreenElement {
            override val key = LockscreenElementKeys.SmartspaceDate
    private inner class DateElement(
        override val key: ElementKey,
        private val isLargeClock: Boolean,
    ) : LockscreenElement {
        override val context = this@SmartspaceElementProvider.context

        @Composable
@@ -75,25 +77,23 @@ constructor(
            factory: LockscreenElementFactory,
            context: LockscreenElementContext,
        ) {
                val isVisible =
                    keyguardSmartspaceViewModel.isDateVisible.collectAsStateWithLifecycle()
                if (!isVisible.value || !keyguardSmartspaceViewModel.isSmartspaceEnabled) {
            val isDateEnabled = keyguardSmartspaceViewModel.isDateEnabled
            if (!keyguardSmartspaceViewModel.isSmartspaceEnabled || !isDateEnabled) {
                return
            }

            AndroidView(
                    factory = { context ->
                        wrapView(context) { frame ->
                            lockscreenSmartspaceController.buildAndConnectDateView(frame, false)
                        }
                factory = { ctx ->
                    smartspaceController.buildAndConnectDateView(ctx, isLargeClock)!!
                }
            )
        }
    }

    private val weatherElement =
        object : LockscreenElement {
            override val key = LockscreenElementKeys.SmartspaceWeather
    private inner class WeatherElement(
        override val key: ElementKey,
        private val isLargeClock: Boolean,
    ) : LockscreenElement {
        override val context = this@SmartspaceElementProvider.context

        @Composable
@@ -101,65 +101,23 @@ constructor(
            factory: LockscreenElementFactory,
            context: LockscreenElementContext,
        ) {
                val isVisible =
                    keyguardSmartspaceViewModel.isWeatherVisible.collectAsStateWithLifecycle()
                if (!isVisible.value || !keyguardSmartspaceViewModel.isSmartspaceEnabled) {
            val isWeatherEnabled: Boolean by
                keyguardSmartspaceViewModel.isWeatherEnabled.collectAsStateWithLifecycle(false)
            if (!keyguardSmartspaceViewModel.isSmartspaceEnabled || !isWeatherEnabled) {
                return
            }

            AndroidView(
                    factory = { context ->
                        wrapView(context) { frame ->
                            lockscreenSmartspaceController.buildAndConnectWeatherView(frame, false)
                        }
                factory = { ctx ->
                    smartspaceController.buildAndConnectWeatherView(ctx, isLargeClock)!!
                }
            )
        }
    }

    private val dateAndWeatherHorizontalElement =
        object : LockscreenElement {
            override val key = LockscreenElementKeys.SmartspaceDateWeatherHorizontal
            override val context = this@SmartspaceElementProvider.context

            @Composable
            override fun ContentScope.LockscreenElement(
                factory: LockscreenElementFactory,
                context: LockscreenElementContext,
            ) {
                Row(
                    horizontalArrangement = Arrangement.spacedBy(8.dp),
                    verticalAlignment = Alignment.CenterVertically,
                ) {
                    factory.lockscreenElement(LockscreenElementKeys.SmartspaceDate, context)
                    factory.lockscreenElement(LockscreenElementKeys.SmartspaceWeather, context)
                }
            }
        }

    private val dateAndWeatherVerticalElement =
        object : LockscreenElement {
            override val key = LockscreenElementKeys.SmartspaceDateWeatherVertical
            override val context = this@SmartspaceElementProvider.context

            @Composable
            override fun ContentScope.LockscreenElement(
                factory: LockscreenElementFactory,
                context: LockscreenElementContext,
            ) {
                Column(
                    horizontalAlignment = Alignment.Start,
                    verticalArrangement = Arrangement.spacedBy(4.dp),
                ) {
                    factory.lockscreenElement(LockscreenElementKeys.SmartspaceDate, context)
                    factory.lockscreenElement(LockscreenElementKeys.SmartspaceWeather, context)
                }
            }
        }

    private val cardsElement =
        object : LockscreenElement {
            override val key = LockscreenElementKeys.SmartspaceCards
            override val key = SmartspaceCards
            override val context = this@SmartspaceElementProvider.context

            @Composable
@@ -171,55 +129,27 @@ constructor(
                    return
                }

                // TODO(b/432451019): Reserve card space even if no cards are currently visible
                // TODO(b/432451019): Placement/positional modifiers need an implementation
                Column(
                    modifier =
                        Modifier
                            // .onTopPlacementChanged(onTopChanged)
                            .padding(
                                // top = { smartSpacePaddingTop(LocalResources.current) },
                                bottom =
                                    dimensionResource(R.dimen.keyguard_status_view_bottom_margin)
                            )
                ) {
                AndroidView(
                        factory = { context ->
                            wrapView(context) { frame ->
                                val view = lockscreenSmartspaceController.buildAndConnectView(frame)
                    factory = { ctx ->
                        val view = smartspaceController.buildAndConnectView(ctx)!!
                        keyguardUnlockAnimationController.lockscreenSmartspace = view
                        view
                            }
                    },
                        onRelease = {
                    onRelease = { view ->
                        if (keyguardUnlockAnimationController.lockscreenSmartspace == view) {
                            keyguardUnlockAnimationController.lockscreenSmartspace = null
                        }
                    },
                    modifier =
                        Modifier.fillMaxWidth()
                            .padding(
                                    horizontal = dimensionResource(R.dimen.below_clock_padding_end)
                                // Note: smartspace adds 16dp of start padding internally
                                start = 12.dp,
                                bottom =
                                    dimensionResource(R.dimen.keyguard_status_view_bottom_margin),
                            )
                            .then(context.burnInModifier),
                        // .onGloballyPositioned { coordinates ->
                        //    onBottomChanged?.invoke(coordinates.boundsInWindow().bottom)
                        // },
                )
            }
        }
}

    companion object {
        private fun wrapView(context: Context, builder: (FrameLayout) -> View?): View {
            return FrameLayout(context).apply {
                builder(this)?.let { view ->
                    view.layoutParams =
                        FrameLayout.LayoutParams(
                            FrameLayout.LayoutParams.WRAP_CONTENT,
                            FrameLayout.LayoutParams.WRAP_CONTENT,
                        )
                    addView(view)
                }
            }
        }
    }
}
+40 −167

File changed.

Preview size limit exceeded, changes collapsed.

+1 −10
Original line number Diff line number Diff line
@@ -26,7 +26,6 @@ import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
@@ -80,20 +79,12 @@ open class DefaultClockFaceLayout(val view: View) : ClockFaceLayout {
                // }
                val topMargin = 0.dp

                // TODO(b/432451019): Placement/positional modifiers need an implementation
                clockView(
                    view = view,
                    modifier =
                        Modifier.height(dimensionResource(clocksR.dimen.small_clock_height))
                            .padding(
                                horizontal = dimensionResource(clocksR.dimen.clock_padding_start)
                            )
                            .padding(top = topMargin)
                            // .onTopPlacementChanged(onTopChanged)
                            .then(context.burnInModifier)
                            .onGloballyPositioned { coordinates ->
                                // onBottomChanged?.invoke(coordinates.boundsInWindow().bottom)
                            },
                            .then(context.burnInModifier),
                )
            }
        }
Loading