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

Commit 26147d72 authored by Hawkwood's avatar Hawkwood
Browse files

Basic clock centering transition

This animation is not to spec. Instead this change is primarially
intended to establish the core pattern to use for animating lockscreen
elements between positions within the lockscreen.

To do this we use NestedSceneTransitionLayout to establish nested
subscenes and convert some of relevant Elements to MovableElement to
facilitate movement of android views that would be too complex to
duplicate while the animation is in-progress.

Bug: 418824686
Flag: com.android.systemui.scene_container
Test: Checked view translated between positions as expected
Change-Id: Ie981b36e53220e4107d5194e95899e23ce16ffe0
parent c278558c
Loading
Loading
Loading
Loading
+0 −69
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

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

import androidx.compose.animation.core.tween
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.TransitionBuilder
import com.android.compose.animation.scene.transitions
import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.ClockFaceInTransition.Companion.CLOCK_IN_MILLIS
import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.ClockFaceInTransition.Companion.CLOCK_IN_START_DELAY_MILLIS
import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.ClockFaceOutTransition.Companion.CLOCK_OUT_MILLIS
import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.SmartspaceMoveTransition.Companion.STATUS_AREA_MOVE_DOWN_MILLIS
import com.android.systemui.keyguard.ui.view.layout.sections.transitions.ClockSizeTransition.SmartspaceMoveTransition.Companion.STATUS_AREA_MOVE_UP_MILLIS

object ClockTransition {
    // TODO(b/438513876): Rebuild to be dependent on new lockscreen clock element list.
    // Possibly this makes sense to delegate fully to the clock plugins.
    val clockTransitions = transitions {}

    data class ElementKeys(
        val smallClock: List<ElementKey>,
        val largeClock: List<ElementKey>,
        val smartspace: List<ElementKey>,
    )

    private fun TransitionBuilder.transitioningToLargeClock(keys: ElementKeys) {
        spec = tween(durationMillis = STATUS_AREA_MOVE_UP_MILLIS.toInt())
        timestampRange(
            startMillis = CLOCK_IN_START_DELAY_MILLIS.toInt(),
            endMillis = (CLOCK_IN_START_DELAY_MILLIS + CLOCK_IN_MILLIS).toInt(),
        ) {
            keys.largeClock.forEach { fade(it) }
        }

        timestampRange(endMillis = CLOCK_OUT_MILLIS.toInt()) {
            keys.smallClock.forEach { fade(it) }
            keys.smartspace.forEach { fade(it) }
        }
    }

    private fun TransitionBuilder.transitioningToSmallClock(keys: ElementKeys) {
        spec = tween(durationMillis = STATUS_AREA_MOVE_DOWN_MILLIS.toInt())
        timestampRange(
            startMillis = CLOCK_IN_START_DELAY_MILLIS.toInt(),
            endMillis = (CLOCK_IN_START_DELAY_MILLIS + CLOCK_IN_MILLIS).toInt(),
        ) {
            keys.smallClock.forEach { fade(it) }
            keys.smartspace.forEach { fade(it) }
        }

        timestampRange(endMillis = CLOCK_OUT_MILLIS.toInt()) {
            keys.largeClock.forEach { fade(it) }
        }
    }
}
+13 −22
Original line number Diff line number Diff line
@@ -45,7 +45,11 @@ 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.plugins.keyguard.ui.composable.elements.LockscreenElementContext
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.MediaCarousel
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys.SettingsMenu
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys.Shortcuts
import com.android.systemui.plugins.keyguard.ui.composable.elements.LockscreenElementKeys.Smartspace
import javax.inject.Inject

/** Renders the lockscreen scene when showing a standard phone or tablet layout */
@@ -97,14 +101,12 @@ constructor(
            )

        val burnIn = rememberBurnIn(keyguardClockViewModel)

        LockscreenTouchHandling(
            viewModelFactory = viewModel.touchHandlingFactory,
            modifier = modifier,
        ) { onSettingsMenuPlaced ->
            val elementContext =
                LockscreenElementContext(
                    scope = this@Content,
                    burnInModifier =
                        Modifier.burnInAware(
                            viewModel = aodBurnInViewModel,
@@ -112,35 +114,24 @@ constructor(
                        ),
                    onElementPositioned = { key, rect ->
                        when (key) {
                            LockscreenElementKeys.Clock.Small -> {
                            Clock.Small -> {
                                burnIn.onSmallClockTopChanged(rect.top)
                                viewModel.setSmallClockBottom(rect.bottom)
                            }
                            LockscreenElementKeys.Smartspace.Cards -> {
                            Smartspace.Cards -> {
                                burnIn.onSmartspaceTopChanged(rect.top)
                                viewModel.setSmartspaceCardBottom(rect.bottom)
                            }
                            LockscreenElementKeys.MediaCarousel -> {
                                viewModel.setMediaPlayerBottom(rect.bottom)
                            }
                            LockscreenElementKeys.Shortcuts.Start -> {
                                viewModel.setShortcutTop(rect.top)
                            }
                            LockscreenElementKeys.Shortcuts.End -> {
                                viewModel.setShortcutTop(rect.top)
                            }
                            LockscreenElementKeys.SettingsMenu -> {
                                onSettingsMenuPlaced(rect)
                            }
                            MediaCarousel -> viewModel.setMediaPlayerBottom(rect.bottom)
                            Shortcuts.Start -> viewModel.setShortcutTop(rect.top)
                            Shortcuts.End -> viewModel.setShortcutTop(rect.top)
                            SettingsMenu -> onSettingsMenuPlaced(rect)
                            else -> {}
                        }
                    },
                )

            LockscreenSceneLayout(
                viewModel = viewModel,
                factory = elementFactory,
                context = elementContext,
            )
            LockscreenSceneLayout(viewModel, elementFactory, elementContext)
        }
    }
}
+12 −12
Original line number Diff line number Diff line
@@ -34,6 +34,7 @@ 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.animation.scene.ElementContentScope
import com.android.compose.modifiers.padding
import com.android.systemui.common.ui.ConfigurationState
import com.android.systemui.dagger.SysUISingleton
@@ -70,15 +71,14 @@ constructor(
    private val nicAodIconViewStore: AlwaysOnDisplayNotificationIconViewStore,
    @ShadeDisplayAware private val systemBarUtilsState: SystemBarUtilsState,
) : LockscreenElementProvider {
    override val elements: List<LockscreenElement> by lazy { listOf(aodNotificationElement) }
    override val elements: List<LockscreenElement> by lazy { listOf(AodNotificationElement()) }

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

        @Composable
            override fun ContentScope.LockscreenElement(
        override fun ElementContentScope.LockscreenElement(
            factory: LockscreenElementFactory,
            context: LockscreenElementContext,
        ) {
+13 −13
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ 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.compose.animation.scene.ElementContentScope
import com.android.systemui.compose.modifiers.sysuiResTag
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.ui.viewmodel.KeyguardRootViewModel
@@ -57,15 +58,14 @@ constructor(
    private val keyguardRootViewModel: KeyguardRootViewModel,
    private val aodPromotedNotificationViewModelFactory: AODPromotedNotificationViewModel.Factory,
) : LockscreenElementProvider {
    override val elements: List<LockscreenElement> by lazy { listOf(promotedNotificationElement) }
    override val elements: List<LockscreenElement> by lazy { listOf(PromotedNotificationElement()) }

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

        @Composable
            override fun ContentScope.LockscreenElement(
        override fun ElementContentScope.LockscreenElement(
            factory: LockscreenElementFactory,
            context: LockscreenElementContext,
        ) {
+83 −79
Original line number Diff line number Diff line
@@ -31,14 +31,13 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementContentScope
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.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.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
@@ -64,10 +63,11 @@ constructor(
        override val context = this@ClockRegionElementProvider.context

        @Composable
        override fun ContentScope.LockscreenElement(
        override fun ElementContentScope.LockscreenElement(
            factory: LockscreenElementFactory,
            context: LockscreenElementContext,
        ) {
            with(factory) {
                val shouldDateWeatherBeBelowSmallClock: Boolean by
                    keyguardClockViewModel.shouldDateWeatherBeBelowSmallClock
                        .collectAsStateWithLifecycle()
@@ -83,10 +83,10 @@ constructor(
                            Modifier.padding(horizontal = xPadding)
                                .padding(top = dimensionResource(R.dimen.keyguard_clock_top_margin)),
                    ) {
                    factory.lockscreenElement(Clock.Small, context)
                        LockscreenElement(Clock.Small, context, Modifier)

                        if (!shouldDateWeatherBeBelowSmallClock) {
                        factory.lockscreenElement(
                            LockscreenElement(
                                Smartspace.DWA.SmallClock.Column,
                                context,
                                Modifier.padding(
@@ -98,14 +98,15 @@ constructor(
                    }

                    if (shouldDateWeatherBeBelowSmallClock) {
                    factory.lockscreenElement(
                        LockscreenElement(
                            Smartspace.DWA.SmallClock.Row,
                            context,
                            Modifier.padding(horizontal = xPadding),
                        )
                    }

                factory.lockscreenElement(Smartspace.Cards, context)
                    LockscreenElement(Smartspace.Cards, context, Modifier)
                }
            }
        }
    }
@@ -115,10 +116,11 @@ constructor(
        override val context = this@ClockRegionElementProvider.context

        @Composable
        override fun ContentScope.LockscreenElement(
        override fun ElementContentScope.LockscreenElement(
            factory: LockscreenElementFactory,
            context: LockscreenElementContext,
        ) {
            with(factory) {
                val shouldDateWeatherBeBelowLargeClock: Boolean by
                    keyguardClockViewModel.shouldDateWeatherBeBelowLargeClock
                        .collectAsStateWithLifecycle()
@@ -140,18 +142,19 @@ constructor(
                        modifier = Modifier.fillMaxWidth(),
                    ) {
                        if (!shouldDateWeatherBeBelowLargeClock) {
                        factory.lockscreenElement(
                            LockscreenElement(
                                Smartspace.DWA.LargeClock.Above,
                                context,
                                Modifier.padding(horizontal = xPadding),
                            )
                        }

                    factory.lockscreenElement(
                        LockscreenElement(
                            Smartspace.Cards,
                            context,
                        // Always reserve space for smartspace cards, even if they're not visible.
                        // This keeps the clock position stable when smartspace enters/exits.
                            // Always reserve space for smartspace cards, even if they're not
                            // visible. This keeps the clock position stable when smartspace
                            // enters/exits.
                            Modifier.heightIn(
                                min = dimensionResource(clocksR.dimen.enhanced_smartspace_height)
                            ),
@@ -167,9 +170,10 @@ constructor(
                            ),
                        modifier = Modifier.padding(horizontal = xPadding).fillMaxWidth().weight(1f),
                    ) {
                    factory.lockscreenElement(Clock.Large, context)
                        LockscreenElement(Clock.Large, context, Modifier)
                        if (shouldDateWeatherBeBelowLargeClock) {
                        factory.lockscreenElement(Smartspace.DWA.LargeClock.Below, context)
                            LockscreenElement(Smartspace.DWA.LargeClock.Below, context, Modifier)
                        }
                    }
                }
            }
Loading