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

Commit e8d755c4 authored by Hawkwood's avatar Hawkwood
Browse files

Centering Animation with glyph stepping

This is a basic implementation that is functional and good enough for
now, but there seems to be some inconsistency being introduced from
deriving the end position from the start position, current position,
and interpolatin progress. While the animation works, it's not quite to
spec, and we'll likely need to do another pass once some infrastructure
STL work like I9b8498972b57b35c7066c95c1825daa499efacd5 is complete.

Bug: 418824686
Test: Checked clock centering animation
Flag: com.android.systemui.scene_container
Change-Id: Iacf9195001291f85093b2ac0de58ac380c81b01b
parent 26147d72
Loading
Loading
Loading
Loading
+9 −3
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import com.android.compose.animation.Easings
import com.android.compose.animation.scene.ContentScope
import com.android.compose.animation.scene.ElementContentScope
import com.android.compose.windowsizeclass.LocalWindowSizeClass
@@ -153,9 +154,12 @@ constructor(
            NestedScenes(
                sceneKey = if (isTwoColumn) TwoColumnScene else CenteredClockScene,
                transitions = {
                    // TODO(b/418824686): Real transition timings and implementation
                    from(from = CenteredClockScene, to = TwoColumnScene) { spec = tween(1000) }
                    from(from = TwoColumnScene, to = CenteredClockScene) { spec = tween(1000) }
                    from(from = CenteredClockScene, to = TwoColumnScene) {
                        spec = tween(ClockCenteringDurationMS, easing = Easings.Emphasized)
                    }
                    from(from = TwoColumnScene, to = CenteredClockScene) {
                        spec = tween(ClockCenteringDurationMS, easing = Easings.Emphasized)
                    }
                },
                modifier = modifier,
            ) {
@@ -285,6 +289,8 @@ constructor(
    }

    companion object {
        val ClockCenteringDurationMS = 1000

        enum class LayoutType {
            WIDE,
            NARROW,
+7 −6
Original line number Diff line number Diff line
@@ -51,6 +51,8 @@ import kotlin.collections.List
open class DefaultClockFaceLayout(val view: View) : ClockFaceLayout {
    override val views = listOf(view)

    var smallClockModifier: MovableElementContentScope.() -> Modifier = { Modifier }
    var largeClockModifier: MovableElementContentScope.() -> Modifier = { Modifier }
    override val elements: List<MovableLockscreenElement> by lazy {
        if (view.id == ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE) {
            listOf(LargeClockElement())
@@ -69,9 +71,9 @@ open class DefaultClockFaceLayout(val view: View) : ClockFaceLayout {
            context: LockscreenElementContext,
        ) {
            clockView(
                view = view,
                modifier =
                    Modifier.height(dimensionResource(clocksR.dimen.small_clock_height))
                view,
                smallClockModifier()
                    .height(dimensionResource(clocksR.dimen.small_clock_height))
                    .then(context.burnInModifier),
            )
        }
@@ -86,8 +88,7 @@ open class DefaultClockFaceLayout(val view: View) : ClockFaceLayout {
            factory: LockscreenElementFactory,
            context: LockscreenElementContext,
        ) {
            // TODO(b/418824686): Migrate stepping animation to compose
            clockView(view, Modifier.wrapContentSize().then(context.burnInModifier))
            clockView(view, largeClockModifier().wrapContentSize().then(context.burnInModifier))
        }
    }

+28 −1
Original line number Diff line number Diff line
@@ -22,7 +22,11 @@ import android.view.Gravity
import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.widget.FrameLayout
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.layout.positionInWindow
import com.android.app.animation.Interpolators
import com.android.compose.animation.scene.content.state.TransitionState
import com.android.systemui.animation.GSFAxes
import com.android.systemui.customization.clocks.ClockContext
import com.android.systemui.customization.clocks.ClockLogger
@@ -105,7 +109,30 @@ class FlexClockFaceController(

    override val layout: ClockFaceLayout =
        DefaultClockFaceLayout(view).apply {
            views[0].id =
            (view as? FlexClockViewGroup)?.let { view ->
                var startX = 0f
                largeClockModifier = {
                    Modifier.onGloballyPositioned {
                        val currentX = it.positionInWindow().x
                        when (val state = layoutState.transitionState) {
                            is TransitionState.Transition -> {
                                view.offsetGlyphsForStepClockAnimation(
                                    startX = startX,
                                    currentX = currentX,
                                    // TODO(b/441506692): Acquire endX from the state
                                    endX = (currentX - startX) / state.progress + startX,
                                    progress = state.progress,
                                )
                            }
                            else -> {
                                startX = currentX
                                view.resetGlyphsOffsets()
                            }
                        }
                    }
                }
            }
            view.id =
                if (isLargeClock) ClockViewIds.LOCKSCREEN_CLOCK_VIEW_LARGE
                else ClockViewIds.LOCKSCREEN_CLOCK_VIEW_SMALL
        }
+39 −1
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@ import com.android.systemui.plugins.keyguard.ui.clocks.ClockViewIds
import java.util.Locale
import kotlin.collections.filterNotNull
import kotlin.collections.map
import kotlin.collections.max
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.min
@@ -420,6 +421,43 @@ class FlexClockViewGroup(clockCtx: ClockContext) :
        invalidate()
    }

    fun resetGlyphsOffsets() {
        if (digitOffsets.size <= 0) return
        digitOffsets.clear()
        invalidate()
    }

    /** Offsets the textViews of the clock for the compose version of the step clock animation. */
    fun offsetGlyphsForStepClockAnimation(
        startX: Float,
        currentX: Float,
        endX: Float,
        progress: Float,
    ) {
        if (progress <= 0f || progress >= 1f) {
            resetGlyphsOffsets()
            return
        }

        val translation = endX - startX
        // A map of the delays for a given digit, keyed by digit index
        val delays = if (translation > 0) STEP_RIGHT_DELAYS else STEP_LEFT_DELAYS
        childViews.forEachIndexed { index, child ->
            val digitFraction =
                constrainedMap(
                    /* rangeMin= */ 0.0f,
                    /* rangeMax= */ 1.0f,
                    /* valueMin= */ delays[index],
                    /* valueMax= */ delays[index] + STEP_ANIMATION_TIME,
                    /* value= */ progress,
                )

            val digitX = translation * digitFraction + startX
            digitOffsets[child.id] = (digitX - currentX)
        }
        invalidate()
    }

    companion object {
        val FORMAT_NUMBER = 1234567890
        val AOD_TRANSITION_DURATION = 800L
@@ -432,7 +470,7 @@ class FlexClockViewGroup(clockCtx: ClockContext) :
        private val STEP_DIGIT_DELAY = 0.033f // Measured as fraction of total animation duration
        private val STEP_LEFT_DELAYS = listOf(0, 1, 2, 3).map { it * STEP_DIGIT_DELAY }
        private val STEP_RIGHT_DELAYS = listOf(1, 0, 3, 2).map { it * STEP_DIGIT_DELAY }
        private val STEP_ANIMATION_TIME = 1.0f - STEP_DIGIT_DELAY * (STEP_LEFT_DELAYS.size - 1)
        private val STEP_ANIMATION_TIME = 1.0f - STEP_LEFT_DELAYS.max()

        /** Languages that do not have vertically mono spaced numerals */
        private val NON_MONO_VERTICAL_NUMERIC_LINE_SPACING_LANGUAGES = setOf("my" /* Burmese */)
+2 −2
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.transition.Transition
import android.transition.TransitionValues
import android.view.ViewGroup
import com.android.app.animation.Interpolators
import com.android.systemui.keyguard.ui.composable.elements.LockscreenUpperRegionElementProvider
import com.android.systemui.plugins.keyguard.ui.clocks.ClockController
import com.android.systemui.plugins.keyguard.ui.clocks.ClockPositionAnimationArgs
import com.android.systemui.shared.R as sharedR
@@ -29,7 +30,7 @@ import com.android.systemui.shared.R as sharedR
class DefaultClockSteppingTransition(private val clock: ClockController) : Transition() {
    init {
        interpolator = Interpolators.LINEAR
        duration = KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION_MS
        duration = LockscreenUpperRegionElementProvider.ClockCenteringDurationMS.toLong()
        addTarget(clock.largeClock.view)
        if (com.android.systemui.shared.Flags.clockReactiveSmartspaceLayout()) {
            addTarget(sharedR.id.date_smartspace_view_large)
@@ -82,6 +83,5 @@ class DefaultClockSteppingTransition(private val clock: ClockController) : Trans
        private const val PROP_BOUNDS_LEFT = "DefaultClockSteppingTransition:boundsLeft"
        private const val PROP_X_IN_WINDOW = "DefaultClockSteppingTransition:xInWindow"
        private val TRANSITION_PROPERTIES = arrayOf(PROP_BOUNDS_LEFT, PROP_X_IN_WINDOW)
        private const val KEYGUARD_STATUS_VIEW_CUSTOM_CLOCK_MOVE_DURATION_MS = 1000L
    }
}