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

Commit 552c1df2 authored by Hawkwood Glazier's avatar Hawkwood Glazier
Browse files

Translate digits on AOD to not be quite so wide

This uses two views stacked vertically instead of 4 individual views.
Doing this improves behavior for moving from very wide axes to relative
thin ones on AOD, and provides much better spacing.

I'm hopeful this will also improve our memory usage as it effectively
halves the number of text animators and views we're allocating in
using. We'll see about this when the metrics come in. If it is
significant, I'll probably move to one view with all four digits, but
that layout proved tricker to implement than this for the time being.

Finally, this also breaks the stepping animation. That'll need to be
addressed, but the implementation of that will depend on where we
land for number of child views we're using here. b/393577936
tracks that follow up work.

Bug: 391858439
Bug: 385338722
Test: Manually checked spacing
Flag: com.android.systemui.shared.clock_reactive_variants
Change-Id: Ib6f67763e77a3ceeeea4a5cbf97dcad6307867fe
parent c896855c
Loading
Loading
Loading
Loading
+5 −16
Original line number Diff line number Diff line
@@ -55,31 +55,20 @@ class ComposedDigitalLayerController(private val clockCtx: ClockContext) :
        val layerCfg =
            LayerConfig(
                style = FontTextStyle(lineHeight = 147.25f),
                timespec = DigitalTimespec.DIGIT_PAIR,
                alignment = DigitalAlignment(HorizontalAlignment.CENTER, VerticalAlignment.CENTER),
                aodStyle =
                    FontTextStyle(
                        transitionInterpolator = Interpolators.EMPHASIZED,
                        transitionDuration = 750,
                    ),
                alignment =
                    DigitalAlignment(HorizontalAlignment.CENTER, VerticalAlignment.BASELINE),

                // Placeholders
                timespec = DigitalTimespec.TIME_FULL_FORMAT,
                // Placeholder
                dateTimeFormat = "hh:mm",
            )

        createController(
            layerCfg.copy(timespec = DigitalTimespec.FIRST_DIGIT, dateTimeFormat = "hh")
        )
        createController(
            layerCfg.copy(timespec = DigitalTimespec.SECOND_DIGIT, dateTimeFormat = "hh")
        )
        createController(
            layerCfg.copy(timespec = DigitalTimespec.FIRST_DIGIT, dateTimeFormat = "mm")
        )
        createController(
            layerCfg.copy(timespec = DigitalTimespec.SECOND_DIGIT, dateTimeFormat = "mm")
        )
        createController(layerCfg.copy(dateTimeFormat = "hh"))
        createController(layerCfg.copy(dateTimeFormat = "mm"))
    }

    private fun refreshTime() {
+2 −16
Original line number Diff line number Diff line
@@ -147,21 +147,6 @@ class FlexClockFaceController(clockCtx: ClockContext, private val isLargeClock:
         * keyguard_large_clock_top_margin from default clock
         */
        override fun onTargetRegionChanged(targetRegion: Rect?) {
            // When a clock needs to be aligned with screen, like weather clock
            // it needs to offset back the translation of keyguard_large_clock_top_margin
            if (isLargeClock && (view as FlexClockView).isAlignedWithScreen()) {
                val topMargin = keyguardLargeClockTopMargin
                targetRegion?.let {
                    val (_, yDiff) = computeLayoutDiff(view, it, isLargeClock)
                    // In LS, we use yDiff to counter translate
                    // the translation of KeyguardLargeClockTopMargin
                    // With the targetRegion passed from picker,
                    // we will have yDiff = 0, no translation is needed for weather clock
                    if (yDiff.toInt() != 0) view.translationY = yDiff - topMargin / 2
                }
                return
            }

            var maxWidth = 0f
            var maxHeight = 0f

@@ -230,7 +215,7 @@ class FlexClockFaceController(clockCtx: ClockContext, private val isLargeClock:
            }

            override fun onPickerCarouselSwiping(swipingFraction: Float) {
                if (isLargeClock && !(view as FlexClockView).isAlignedWithScreen()) {
                if (isLargeClock) {
                    view.translationY = keyguardLargeClockTopMargin / 2F * swipingFraction
                }
                layerController.animations.onPickerCarouselSwiping(swipingFraction)
@@ -250,6 +235,7 @@ class FlexClockFaceController(clockCtx: ClockContext, private val isLargeClock:

    companion object {
        val SMALL_CLOCK_MAX_WDTH = 120f

        val SMALL_LAYER_CONFIG =
            LayerConfig(
                timespec = DigitalTimespec.TIME_FULL_FORMAT,
+25 −0
Original line number Diff line number Diff line
@@ -71,6 +71,7 @@ data class FontTextStyle(

enum class DigitalTimespec {
    TIME_FULL_FORMAT,
    DIGIT_PAIR,
    FIRST_DIGIT,
    SECOND_DIGIT,
}
@@ -120,6 +121,28 @@ open class SimpleDigitalHandLayerController(
        }
    }

    private fun applyLayout() {
        // TODO: Remove NO-OP
        if (view.layoutParams is RelativeLayout.LayoutParams) {
            val lp = view.layoutParams as RelativeLayout.LayoutParams
            lp.addRule(RelativeLayout.TEXT_ALIGNMENT_CENTER)
            when (view.id) {
                R.id.HOUR_DIGIT_PAIR -> {
                    lp.addRule(RelativeLayout.CENTER_VERTICAL)
                    lp.addRule(RelativeLayout.ALIGN_PARENT_START)
                }
                R.id.MINUTE_DIGIT_PAIR -> {
                    lp.addRule(RelativeLayout.CENTER_VERTICAL)
                    lp.addRule(RelativeLayout.END_OF, R.id.HOUR_DIGIT_PAIR)
                }
                else -> {
                    throw Exception("cannot apply two pairs layout to view ${view.id}")
                }
            }
            view.layoutParams = lp
        }
    }

    override val events =
        object : ClockEvents {
            override var isReactiveTouchInteractionEnabled = false
@@ -154,6 +177,7 @@ open class SimpleDigitalHandLayerController(
    override val animations =
        object : ClockAnimations {
            override fun enter() {
                applyLayout()
                refreshTime()
            }

@@ -169,6 +193,7 @@ open class SimpleDigitalHandLayerController(
            }

            override fun fold(fraction: Float) {
                applyLayout()
                refreshTime()
            }

+5 −8
Original line number Diff line number Diff line
@@ -106,19 +106,16 @@ class DigitalTimespecHandler(
        )
    }

    private fun getSingleDigit(): String {
        val isFirstDigit = timespec == DigitalTimespec.FIRST_DIGIT
    private fun getSingleDigit(offset: Int): String {
        val text = dateFormat.format(cal.time).toString()
        return text.substring(
            if (isFirstDigit) 0 else text.length - 1,
            if (isFirstDigit) text.length - 1 else text.length,
        )
        return text.substring(offset, offset + 1)
    }

    fun getDigitString(): String {
        return when (timespec) {
            DigitalTimespec.FIRST_DIGIT,
            DigitalTimespec.SECOND_DIGIT -> getSingleDigit()
            DigitalTimespec.FIRST_DIGIT -> getSingleDigit(0)
            DigitalTimespec.SECOND_DIGIT -> getSingleDigit(1)
            DigitalTimespec.DIGIT_PAIR -> dateFormat.format(cal.time).toString()
            DigitalTimespec.TIME_FULL_FORMAT -> dateFormat.format(cal.time).toString()
        }
    }
+34 −25
Original line number Diff line number Diff line
@@ -87,7 +87,7 @@ class FlexClockView(clockCtx: ClockContext) : FrameLayout(clockCtx.context) {

    protected fun calculateSize(widthMeasureSpec: Int, heightMeasureSpec: Int): Point? {
        maxSingleDigitSize = Point(-1, -1)
        val bottomLocation: (textView: SimpleDigitalClockTextView) -> Int = { textView ->
        val viewHeight: (textView: SimpleDigitalClockTextView) -> Int = { textView ->
            if (isMonoVerticalNumericLineSpacing) {
                maxSingleDigitSize.y
            } else {
@@ -98,9 +98,15 @@ class FlexClockView(clockCtx: ClockContext) : FrameLayout(clockCtx.context) {
        digitalClockTextViewMap.forEach { (_, textView) ->
            textView.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED)
            maxSingleDigitSize.x = max(maxSingleDigitSize.x, textView.measuredWidth)
            maxSingleDigitSize.y = max(bottomLocation(textView), textView.measuredHeight)
            maxSingleDigitSize.y = max(viewHeight(textView), textView.measuredHeight)
        }
        aodTranslate = Point(0, 0)
        // TODO(b/364680879): Cleanup
        /*
        aodTranslate = Point(
            (maxSingleDigitSize.x * AOD_HORIZONTAL_TRANSLATE_RATIO).toInt(),
            (maxSingleDigitSize.y * AOD_VERTICAL_TRANSLATE_RATIO).toInt())
        */
        return Point(
            ((maxSingleDigitSize.x + abs(aodTranslate.x)) * 2),
            ((maxSingleDigitSize.y + abs(aodTranslate.y)) * 2),
@@ -112,6 +118,10 @@ class FlexClockView(clockCtx: ClockContext) : FrameLayout(clockCtx.context) {
        digitLeftTopMap[R.id.HOUR_SECOND_DIGIT] = Point(maxSingleDigitSize.x, 0)
        digitLeftTopMap[R.id.MINUTE_FIRST_DIGIT] = Point(0, maxSingleDigitSize.y)
        digitLeftTopMap[R.id.MINUTE_SECOND_DIGIT] = Point(maxSingleDigitSize)
        digitLeftTopMap[R.id.HOUR_DIGIT_PAIR] = Point(maxSingleDigitSize.x / 2, 0)
        // Add a small vertical buffer for the second digit pair
        digitLeftTopMap[R.id.MINUTE_DIGIT_PAIR] =
            Point(maxSingleDigitSize.x / 2, (maxSingleDigitSize.y * 1.05f).toInt())
        digitLeftTopMap.forEach { (_, point) ->
            point.x += abs(aodTranslate.x)
            point.y += abs(aodTranslate.y)
@@ -179,9 +189,9 @@ class FlexClockView(clockCtx: ClockContext) : FrameLayout(clockCtx.context) {
            // save canvas location in anticipation of restoration later
            canvas.save()
            val xTranslateAmount =
                digitOffsets.getOrDefault(id, 0f) + digitLeftTopMap[id]!!.x.toFloat()
                digitOffsets.getOrDefault(id, 0f) + (digitLeftTopMap[id]?.x?.toFloat() ?: 0f)
            // move canvas to location that the textView would like
            canvas.translate(xTranslateAmount, digitLeftTopMap[id]!!.y.toFloat())
            canvas.translate(xTranslateAmount, digitLeftTopMap[id]?.y?.toFloat() ?: 0f)
            // draw the textView at the location of the canvas above
            textView.draw(canvas)
            // reset the canvas location back to 0 without drawing
@@ -189,8 +199,6 @@ class FlexClockView(clockCtx: ClockContext) : FrameLayout(clockCtx.context) {
        }
    }

    fun isAlignedWithScreen(): Boolean = false

    fun onLocaleChanged(locale: Locale) {
        updateLocale(locale)
        requestLayout()
@@ -302,23 +310,17 @@ class FlexClockView(clockCtx: ClockContext) : FrameLayout(clockCtx.context) {
        clockMoveDirection: Int,
        moveFraction: Float,
    ) {
        // TODO(b/393577936): The step animation isn't correct with the two pairs approach
        val isMovingToCenter = if (isLayoutRtl) clockMoveDirection < 0 else clockMoveDirection > 0
        // The sign of moveAmountDeltaForDigit is already set here
        // we can interpret (left - clockStartLeft) as (destinationPosition - originPosition)
        // so we no longer need to multiply direct sign to moveAmountDeltaForDigit
        val currentMoveAmount = left - clockStartLeft
        for (i in 0 until NUM_DIGITS) {
            val mapIndexToId =
                when (i) {
                    0 -> R.id.HOUR_FIRST_DIGIT
                    1 -> R.id.HOUR_SECOND_DIGIT
                    2 -> R.id.MINUTE_FIRST_DIGIT
                    3 -> R.id.MINUTE_SECOND_DIGIT
                    else -> -1
                }
        var index = 0
        digitalClockTextViewMap.forEach { id, _ ->
            val digitFraction =
                getDigitFraction(
                    digit = i,
                    digit = index++,
                    isMovingToCenter = isMovingToCenter,
                    fraction = moveFraction,
                )
@@ -326,7 +328,7 @@ class FlexClockView(clockCtx: ClockContext) : FrameLayout(clockCtx.context) {
            val moveAmountForDigit = currentMoveAmount * digitFraction
            var moveAmountDeltaForDigit = moveAmountForDigit - currentMoveAmount
            if (isMovingToCenter && moveAmountForDigit < 0) moveAmountDeltaForDigit *= -1
            digitOffsets[mapIndexToId] = moveAmountDeltaForDigit
            digitOffsets[id] = moveAmountDeltaForDigit
            invalidate()
        }
    }
@@ -347,7 +349,8 @@ class FlexClockView(clockCtx: ClockContext) : FrameLayout(clockCtx.context) {
                /* rangeMin= */ 0.0f,
                /* rangeMax= */ 1.0f,
                /* valueMin= */ digitInitialDelay,
                /* valueMax= */ digitInitialDelay + AVAILABLE_ANIMATION_TIME,
                /* valueMax= */ digitInitialDelay +
                    availableAnimationTime(digitalClockTextViewMap.size),
                /* value= */ fraction,
            )
        )
@@ -357,12 +360,8 @@ class FlexClockView(clockCtx: ClockContext) : FrameLayout(clockCtx.context) {
        val AOD_TRANSITION_DURATION = 750L
        val CHARGING_TRANSITION_DURATION = 300L

        // Calculate the positions of all of the digits...
        // Offset each digit by, say, 0.1
        // This means that each digit needs to move over a slice of "fractions", i.e. digit 0 should
        // move from 0.0 - 0.7, digit 1 from 0.1 - 0.8, digit 2 from 0.2 - 0.9, and digit 3
        // from 0.3 - 1.0.
        private const val NUM_DIGITS = 4
        val AOD_HORIZONTAL_TRANSLATE_RATIO = -0.15F
        val AOD_VERTICAL_TRANSLATE_RATIO = 0.075F

        // Delays. Each digit's animation should have a slight delay, so we get a nice
        // "stepping" effect. When moving right, the second digit of the hour should move first.
@@ -387,7 +386,9 @@ class FlexClockView(clockCtx: ClockContext) : FrameLayout(clockCtx.context) {

        // Total available transition time for each digit, taking into account the step. If step is
        // 0.1, then digit 0 would animate over 0.0 - 0.7, making availableTime 0.7.
        private const val AVAILABLE_ANIMATION_TIME = 1.0f - MOVE_DIGIT_STEP * (NUM_DIGITS - 1)
        private fun availableAnimationTime(numDigits: Int): Float {
            return 1.0f - MOVE_DIGIT_STEP * (numDigits.toFloat() - 1)
        }

        // Add language tags below that do not have vertically mono spaced numerals
        private val NON_MONO_VERTICAL_NUMERIC_LINE_SPACING_LANGUAGES =
@@ -415,6 +416,14 @@ class FlexClockView(clockCtx: ClockContext) : FrameLayout(clockCtx.context) {
                    outPoint.x *= 1
                    outPoint.y *= 1
                }
                R.id.HOUR_DIGIT_PAIR -> {
                    outPoint.x *= -1
                    outPoint.y *= -1
                }
                R.id.MINUTE_DIGIT_PAIR -> {
                    outPoint.x *= -1
                    outPoint.y *= 1
                }
            }
            return outPoint
        }
Loading