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

Commit 6b4be2fa authored by Aaron Liu's avatar Aaron Liu Committed by Android (Google) Code Review
Browse files

Merge "Add stepping animation to clock." into main

parents c5be730a 4f47e3bb
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

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

import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween
import com.android.compose.animation.scene.ElementKey
import com.android.compose.animation.scene.SceneKey
@@ -39,7 +40,7 @@ object ClockTransition {
            transitioningToSmallClock()
        }
        from(ClockScenes.splitShadeLargeClockScene, to = ClockScenes.largeClockScene) {
            spec = tween(1000)
            spec = tween(1000, easing = LinearEasing)
        }
    }

+38 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

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

import android.content.res.Resources
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
@@ -23,6 +24,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
@@ -36,6 +38,8 @@ import com.android.systemui.customization.R as customizationR
import com.android.systemui.customization.R
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.largeClockElementKey
import com.android.systemui.keyguard.ui.composable.blueprint.ClockElementKeys.smallClockElementKey
import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.largeClockScene
import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.splitShadeLargeClockScene
import com.android.systemui.keyguard.ui.composable.modifier.burnInAware
import com.android.systemui.keyguard.ui.composable.modifier.onTopPlacementChanged
import com.android.systemui.keyguard.ui.viewmodel.AodBurnInViewModel
@@ -95,6 +99,36 @@ constructor(
        if (currentClock?.largeClock?.view == null) {
            return
        }

        // Centering animation for clocks that have custom position animations.
        LaunchedEffect(layoutState.currentTransition?.progress) {
            val transition = layoutState.currentTransition ?: return@LaunchedEffect
            if (currentClock?.largeClock?.config?.hasCustomPositionUpdatedAnimation != true) {
                return@LaunchedEffect
            }

            // If we are not doing the centering animation, do not animate.
            val progress =
                if (transition.isTransitioningBetween(largeClockScene, splitShadeLargeClockScene)) {
                    transition.progress
                } else {
                    1f
                }

            val distance =
                if (transition.toScene == splitShadeLargeClockScene) {
                        -getClockCenteringDistance()
                    } else {
                        getClockCenteringDistance()
                    }
                    .toFloat()
            val largeClock = checkNotNull(currentClock).largeClock
            largeClock.animations.onPositionUpdated(
                distance = distance,
                fraction = progress,
            )
        }

        MovableElement(key = largeClockElementKey, modifier = modifier) {
            content {
                AndroidView(
@@ -120,4 +154,8 @@ constructor(
        (clockView.parent as? ViewGroup)?.removeView(clockView)
        addView(clockView)
    }

    fun getClockCenteringDistance(): Float {
        return Resources.getSystem().displayMetrics.widthPixels / 4f
    }
}
+46 −17
Original line number Diff line number Diff line
@@ -16,12 +16,16 @@

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

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
@@ -31,11 +35,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
import com.android.compose.animation.scene.SceneTransitionLayout
import com.android.compose.modifiers.thenIf
import com.android.systemui.Flags
import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor
import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes
import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.largeClockScene
import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.smallClockScene
import com.android.systemui.keyguard.ui.composable.blueprint.ClockScenes.splitShadeLargeClockScene
@@ -63,6 +68,9 @@ constructor(
    ) {
        val isLargeClockVisible by clockViewModel.isLargeClockVisible.collectAsState()
        val currentClockLayout by clockViewModel.currentClockLayout.collectAsState()
        val hasCustomPositionUpdatedAnimation by
            clockViewModel.hasCustomPositionUpdatedAnimation.collectAsState()

        val currentScene =
            when (currentClockLayout) {
                KeyguardClockViewModel.ClockLayout.SPLIT_SHADE_LARGE_CLOCK ->
@@ -94,12 +102,10 @@ constructor(
            transitions = ClockTransition.defaultClockTransitions,
            enableInterruptions = false,
        ) {
            scene(ClockScenes.splitShadeLargeClockScene) {
                Row(
                    modifier = Modifier.fillMaxSize(),
                ) {
            scene(splitShadeLargeClockScene) {
                Box(modifier = Modifier.fillMaxSize()) {
                    Column(
                        modifier = Modifier.fillMaxHeight().weight(weight = 1f),
                        modifier = Modifier.fillMaxSize(),
                        horizontalAlignment = Alignment.CenterHorizontally,
                    ) {
                        with(smartSpaceSection) {
@@ -108,8 +114,34 @@ constructor(
                                onTopChanged = burnIn.onSmartspaceTopChanged,
                            )
                        }
                        with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }

                        with(clockSection) {
                            LargeClock(
                                modifier =
                                    Modifier.fillMaxSize().thenIf(
                                        !hasCustomPositionUpdatedAnimation
                                    ) {
                                        // If we do not have a custom position animation, we want
                                        // the clock to be on one half of the screen.
                                        Modifier.offset {
                                            IntOffset(
                                                x =
                                                    -clockSection
                                                        .getClockCenteringDistance()
                                                        .toInt(),
                                                y = 0,
                                            )
                                        }
                                    }
                            )
                        }
                    }
                }

                Row(
                    modifier = Modifier.fillMaxSize(),
                ) {
                    Spacer(modifier = Modifier.weight(weight = 1f))
                    with(notificationSection) {
                        Notifications(
                            modifier =
@@ -121,7 +153,7 @@ constructor(
                }
            }

            scene(ClockScenes.splitShadeSmallClockScene) {
            scene(splitShadeSmallClockScene) {
                Row(
                    modifier = Modifier.fillMaxSize(),
                ) {
@@ -133,7 +165,7 @@ constructor(
                            SmallClock(
                                burnInParams = burnIn.parameters,
                                onTopChanged = burnIn.onSmallClockTopChanged,
                                modifier = Modifier.fillMaxWidth()
                                modifier = Modifier.wrapContentSize()
                            )
                        }
                        with(smartSpaceSection) {
@@ -155,13 +187,13 @@ constructor(
                }
            }

            scene(ClockScenes.smallClockScene) {
            scene(smallClockScene) {
                Column {
                    with(clockSection) {
                        SmallClock(
                            burnInParams = burnIn.parameters,
                            onTopChanged = burnIn.onSmallClockTopChanged,
                            modifier = Modifier.fillMaxWidth()
                            modifier = Modifier.wrapContentSize()
                        )
                    }
                    with(smartSpaceSection) {
@@ -172,15 +204,12 @@ constructor(
                    }
                    with(mediaCarouselSection) { MediaCarousel() }
                    with(notificationSection) {
                        Notifications(
                            modifier =
                                androidx.compose.ui.Modifier.fillMaxWidth().weight(weight = 1f)
                        )
                        Notifications(modifier = Modifier.fillMaxWidth().weight(weight = 1f))
                    }
                }
            }

            scene(ClockScenes.largeClockScene) {
            scene(largeClockScene) {
                Column {
                    with(smartSpaceSection) {
                        SmartSpace(
@@ -188,7 +217,7 @@ constructor(
                            onTopChanged = burnIn.onSmartspaceTopChanged,
                        )
                    }
                    with(clockSection) { LargeClock(modifier = Modifier.fillMaxWidth()) }
                    with(clockSection) { LargeClock(modifier = Modifier.fillMaxSize()) }
                }
            }
        }
+56 −17
Original line number Diff line number Diff line
@@ -517,29 +517,68 @@ class AnimatableClockView @JvmOverloads constructor(
        val currentMoveAmount = left - clockStartLeft
        val digitOffsetDirection = if (isLayoutRtl) -1 else 1
        for (i in 0 until NUM_DIGITS) {
            val digitFraction =
                getDigitFraction(
                    digit = i,
                    isMovingToCenter = isMovingToCenter,
                    fraction = moveFraction,
                )
            val moveAmountForDigit = currentMoveAmount * digitFraction
            val moveAmountDeltaForDigit = moveAmountForDigit - currentMoveAmount
            glyphOffsets[i] = digitOffsetDirection * moveAmountDeltaForDigit
        }
        invalidate()
    }

    /**
     * Offsets the glyphs of the clock for the step clock animation.
     *
     * The animation makes the glyphs of the clock move at different speeds, when the clock is
     * moving horizontally. This method uses direction, distance, and fraction to determine offset.
     *
     * @param distance is the total distance in pixels to offset the glyphs when animation
     *   completes. Negative distance means we are animating the position towards the center.
     * @param fraction fraction of the clock movement. 0 means it is at the beginning, and 1
     *   means it finished moving.
     */
    fun offsetGlyphsForStepClockAnimation(
        distance: Float,
        fraction: Float,
    ) {
        for (i in 0 until NUM_DIGITS) {
            val dir = if (isLayoutRtl) -1 else 1
            val digitFraction =
                getDigitFraction(digit = i, isMovingToCenter = distance > 0, fraction = fraction)
            val moveAmountForDigit = dir * distance * digitFraction
            glyphOffsets[i] = moveAmountForDigit

            if (distance > 0) {
                // If distance > 0 then we are moving from the left towards the center.
                // We need ensure that the glyphs are offset to the initial position.
                glyphOffsets -= dir * distance
            }
        }
        invalidate()
    }

    private fun getDigitFraction(digit: Int, isMovingToCenter: Boolean, fraction: Float): Float {
        // The delay for the digit, in terms of fraction (i.e. the digit should not move
        // during 0.0 - 0.1).
        val digitInitialDelay =
            if (isMovingToCenter) {
                        moveToCenterDelays[i] * MOVE_DIGIT_STEP
                moveToCenterDelays[digit] * MOVE_DIGIT_STEP
            } else {
                        moveToSideDelays[i] * MOVE_DIGIT_STEP
                moveToSideDelays[digit] * MOVE_DIGIT_STEP
            }
            val digitFraction =
                    MOVE_INTERPOLATOR.getInterpolation(
        return MOVE_INTERPOLATOR.getInterpolation(
                constrainedMap(
                    0.0f,
                    1.0f,
                    digitInitialDelay,
                    digitInitialDelay + AVAILABLE_ANIMATION_TIME,
                                    moveFraction
                    fraction,
                )
            )
            val moveAmountForDigit = currentMoveAmount * digitFraction
            val moveAmountDeltaForDigit = moveAmountForDigit - currentMoveAmount
            glyphOffsets[i] = digitOffsetDirection * moveAmountDeltaForDigit
        }
        invalidate()
    }

    // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
+10 −0
Original line number Diff line number Diff line
@@ -232,6 +232,10 @@ class DefaultClockController(
        fun offsetGlyphsForStepClockAnimation(fromLeft: Int, direction: Int, fraction: Float) {
            view.offsetGlyphsForStepClockAnimation(fromLeft, direction, fraction)
        }

        fun offsetGlyphsForStepClockAnimation(distance: Float, fraction: Float) {
            view.offsetGlyphsForStepClockAnimation(distance, fraction)
        }
    }

    inner class DefaultClockEvents : ClockEvents {
@@ -316,6 +320,8 @@ class DefaultClockController(
        }

        override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {}

        override fun onPositionUpdated(distance: Float, fraction: Float) {}
    }

    inner class LargeClockAnimations(
@@ -326,6 +332,10 @@ class DefaultClockController(
        override fun onPositionUpdated(fromLeft: Int, direction: Int, fraction: Float) {
            largeClock.offsetGlyphsForStepClockAnimation(fromLeft, direction, fraction)
        }

        override fun onPositionUpdated(distance: Float, fraction: Float) {
            largeClock.offsetGlyphsForStepClockAnimation(distance, fraction)
        }
    }

    class AnimationState(
Loading