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

Commit 94d09001 authored by Sherry Zhou's avatar Sherry Zhou Committed by Automerger Merge Worker
Browse files

Merge "Support change of several axes in textAnimator" into tm-qpr-dev am:...

Merge "Support change of several axes in textAnimator" into tm-qpr-dev am: 7b9b280f am: 893e3d43 am: ac1655ce

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21802990



Change-Id: I9dee9d661cb63ef0579476c4ae73f99ac48b5d31
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents adf49496 ac1655ce
Loading
Loading
Loading
Loading
+59 −0
Original line number Diff line number Diff line
package com.android.systemui.animation

private const val TAG_WGHT = "wght"
private const val TAG_WDTH = "wdth"
private const val TAG_OPSZ = "opsz"
private const val TAG_ROND = "ROND"

class FontVariationUtils {
    private var mWeight = -1
    private var mWidth = -1
    private var mOpticalSize = -1
    private var mRoundness = -1
    private var isUpdated = false

    /*
     * generate fontVariationSettings string, used for key in typefaceCache in TextAnimator
     * the order of axes should align to the order of parameters
     * if every axis remains unchanged, return ""
     */
    fun updateFontVariation(
        weight: Int = -1,
        width: Int = -1,
        opticalSize: Int = -1,
        roundness: Int = -1
    ): String {
        isUpdated = false
        if (weight >= 0 && mWeight != weight) {
            isUpdated = true
            mWeight = weight
        }
        if (width >= 0 && mWidth != width) {
            isUpdated = true
            mWidth = width
        }
        if (opticalSize >= 0 && mOpticalSize != opticalSize) {
            isUpdated = true
            mOpticalSize = opticalSize
        }

        if (roundness >= 0 && mRoundness != roundness) {
            isUpdated = true
            mRoundness = roundness
        }
        var resultString = ""
        if (mWeight >= 0) {
            resultString += "'$TAG_WGHT' $mWeight"
        }
        if (mWidth >= 0) {
            resultString += (if (resultString.isBlank()) "" else ", ") + "'$TAG_WDTH' $mWidth"
        }
        if (mOpticalSize >= 0) {
            resultString += (if (resultString.isBlank()) "" else ", ") + "'$TAG_OPSZ' $mOpticalSize"
        }
        if (mRoundness >= 0) {
            resultString += (if (resultString.isBlank()) "" else ", ") + "'$TAG_ROND' $mRoundness"
        }
        return if (isUpdated) resultString else ""
    }
}
+64 −48
Original line number Diff line number Diff line
@@ -23,11 +23,8 @@ import android.animation.ValueAnimator
import android.graphics.Canvas
import android.graphics.Typeface
import android.graphics.fonts.Font
import android.graphics.fonts.FontVariationAxis
import android.text.Layout
import android.util.SparseArray

private const val TAG_WGHT = "wght"
private const val DEFAULT_ANIMATION_DURATION: Long = 300

typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit
@@ -51,7 +48,7 @@ typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit
 *
 *         // Change the text size with animation.
 *         fun setTextSize(sizePx: Float, animate: Boolean) {
 *             animator.setTextStyle(-1 /* unchanged weight */, sizePx, animate)
 *             animator.setTextStyle("" /* unchanged fvar... */, sizePx, animate)
 *         }
 *     }
 * ```
@@ -115,7 +112,9 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
            protected set
    }

    private val typefaceCache = SparseArray<Typeface?>()
    private val fontVariationUtils = FontVariationUtils()

    private val typefaceCache = HashMap<String, Typeface?>()

    fun updateLayout(layout: Layout) {
        textInterpolator.layout = layout
@@ -186,7 +185,7 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
     * Bu passing -1 to duration, the default text animation, 1000ms, is used.
     * By passing false to animate, the text will be updated without animation.
     *
     * @param weight an optional text weight.
     * @param fvar an optional text fontVariationSettings.
     * @param textSize an optional font size.
     * @param colors an optional colors array that must be the same size as numLines passed to
     *               the TextInterpolator
@@ -199,7 +198,7 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
     *                     will be used. This is ignored if animate is false.
     */
    fun setTextStyle(
        weight: Int = -1,
        fvar: String? = "",
        textSize: Float = -1f,
        color: Int? = null,
        strokeWidth: Float = -1f,
@@ -217,42 +216,16 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
        if (textSize >= 0) {
            textInterpolator.targetPaint.textSize = textSize
        }
        if (weight >= 0) {
            val fontVariationArray =
                    FontVariationAxis.fromFontVariationSettings(
                        textInterpolator.targetPaint.fontVariationSettings
                    )
            if (fontVariationArray.isNullOrEmpty()) {
                textInterpolator.targetPaint.typeface =
                    typefaceCache.getOrElse(weight) {
                        textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight"
                        textInterpolator.targetPaint.typeface
                    }
            } else {
                val idx = fontVariationArray.indexOfFirst { it.tag == "$TAG_WGHT" }
                if (idx == -1) {
                    val updatedFontVariation =
                        textInterpolator.targetPaint.fontVariationSettings + ",'$TAG_WGHT' $weight"
                    textInterpolator.targetPaint.typeface =
                        typefaceCache.getOrElse(weight) {
                            textInterpolator.targetPaint.fontVariationSettings =
                                    updatedFontVariation
                            textInterpolator.targetPaint.typeface
                        }
                } else {
                    fontVariationArray[idx] = FontVariationAxis(
                            "$TAG_WGHT", weight.toFloat())
                    val updatedFontVariation =
                            FontVariationAxis.toFontVariationSettings(fontVariationArray)

        if (!fvar.isNullOrBlank()) {
            textInterpolator.targetPaint.typeface =
                        typefaceCache.getOrElse(weight) {
                            textInterpolator.targetPaint.fontVariationSettings =
                                    updatedFontVariation
                typefaceCache.getOrElse(fvar) {
                    textInterpolator.targetPaint.fontVariationSettings = fvar
                    typefaceCache.put(fvar, textInterpolator.targetPaint.typeface)
                    textInterpolator.targetPaint.typeface
                }
        }
            }
        }

        if (color != null) {
            textInterpolator.targetPaint.color = color
        }
@@ -291,13 +264,56 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
            invalidateCallback()
        }
    }
}

private fun <V> SparseArray<V>.getOrElse(key: Int, defaultValue: () -> V): V {
    var v = get(key)
    if (v == null) {
        v = defaultValue()
        put(key, v)
    /**
     * Set text style with animation. Similar as
     * fun setTextStyle(
     *      fvar: String? = "",
     *      textSize: Float = -1f,
     *      color: Int? = null,
     *      strokeWidth: Float = -1f,
     *      animate: Boolean = true,
     *      duration: Long = -1L,
     *      interpolator: TimeInterpolator? = null,
     *      delay: Long = 0,
     *      onAnimationEnd: Runnable? = null
     * )
     *
     * @param weight an optional style value for `wght` in fontVariationSettings.
     * @param width an optional style value for `wdth` in fontVariationSettings.
     * @param opticalSize an optional style value for `opsz` in fontVariationSettings.
     * @param roundness an optional style value for `ROND` in fontVariationSettings.
     */
    fun setTextStyle(
        weight: Int = -1,
        width: Int = -1,
        opticalSize: Int = -1,
        roundness: Int = -1,
        textSize: Float = -1f,
        color: Int? = null,
        strokeWidth: Float = -1f,
        animate: Boolean = true,
        duration: Long = -1L,
        interpolator: TimeInterpolator? = null,
        delay: Long = 0,
        onAnimationEnd: Runnable? = null
    ) {
        val fvar = fontVariationUtils.updateFontVariation(
            weight = weight,
            width = width,
            opticalSize = opticalSize,
            roundness = roundness,)
        setTextStyle(
            fvar = fvar,
            textSize = textSize,
            color = color,
            strokeWidth = strokeWidth,
            animate = animate,
            duration = duration,
            interpolator = interpolator,
            delay = delay,
            onAnimationEnd = onAnimationEnd,
        )
    }
    return v
}
+62 −0
Original line number Diff line number Diff line
package com.android.systemui.animation

import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import junit.framework.Assert
import org.junit.Test
import org.junit.runner.RunWith

private const val TAG_WGHT = "wght"
private const val TAG_WDTH = "wdth"
private const val TAG_OPSZ = "opsz"
private const val TAG_ROND = "ROND"

@RunWith(AndroidTestingRunner::class)
@SmallTest
class FontVariationUtilsTest : SysuiTestCase() {
    @Test
    fun testUpdateFontVariation_getCorrectFvarStr() {
        val fontVariationUtils = FontVariationUtils()
        val initFvar =
            fontVariationUtils.updateFontVariation(
                weight = 100,
                width = 100,
                opticalSize = -1,
                roundness = 100
            )
        Assert.assertEquals("'$TAG_WGHT' 100, '$TAG_WDTH' 100, '$TAG_ROND' 100", initFvar)
        val updatedFvar =
            fontVariationUtils.updateFontVariation(
                weight = 200,
                width = 100,
                opticalSize = 0,
                roundness = 100
            )
        Assert.assertEquals(
            "'$TAG_WGHT' 200, '$TAG_WDTH' 100, '$TAG_OPSZ' 0, '$TAG_ROND' 100",
            updatedFvar
        )
    }

    @Test
    fun testStyleValueUnchange_getBlankStr() {
        val fontVariationUtils = FontVariationUtils()
        fontVariationUtils.updateFontVariation(
            weight = 100,
            width = 100,
            opticalSize = 0,
            roundness = 100
        )
        val updatedFvar1 =
            fontVariationUtils.updateFontVariation(
                weight = 100,
                width = 100,
                opticalSize = 0,
                roundness = 100
            )
        Assert.assertEquals("", updatedFvar1)
        val updatedFvar2 = fontVariationUtils.updateFontVariation()
        Assert.assertEquals("", updatedFvar2)
    }
}
+0 −68
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.systemui.animation
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.graphics.Typeface
import android.graphics.fonts.FontVariationAxis
import android.testing.AndroidTestingRunner
import android.text.Layout
import android.text.StaticLayout
@@ -180,71 +179,4 @@ class TextAnimatorTest : SysuiTestCase() {

        assertThat(paint.typeface).isSameInstanceAs(prevTypeface)
    }

    @Test
    fun testSetTextStyle_addWeight() {
        testWeightChange("", 100, FontVariationAxis.fromFontVariationSettings("'wght' 100")!!)
    }

    @Test
    fun testSetTextStyle_changeWeight() {
        testWeightChange(
                "'wght' 500",
                100,
                FontVariationAxis.fromFontVariationSettings("'wght' 100")!!
        )
    }

    @Test
    fun testSetTextStyle_addWeightWithOtherAxis() {
        testWeightChange(
                "'wdth' 100",
                100,
                FontVariationAxis.fromFontVariationSettings("'wght' 100, 'wdth' 100")!!
        )
    }

    @Test
    fun testSetTextStyle_changeWeightWithOtherAxis() {
        testWeightChange(
                "'wght' 500, 'wdth' 100",
                100,
                FontVariationAxis.fromFontVariationSettings("'wght' 100, 'wdth' 100")!!
        )
    }

    private fun testWeightChange(
            initialFontVariationSettings: String,
            weight: Int,
            expectedFontVariationSettings: Array<FontVariationAxis>
    ) {
        val layout = makeLayout("Hello, World", PAINT)
        val valueAnimator = mock(ValueAnimator::class.java)
        val textInterpolator = mock(TextInterpolator::class.java)
        val paint =
                TextPaint().apply {
                    typeface = Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf")
                    fontVariationSettings = initialFontVariationSettings
                }
        `when`(textInterpolator.targetPaint).thenReturn(paint)

        val textAnimator =
                TextAnimator(layout, {}).apply {
                    this.textInterpolator = textInterpolator
                    this.animator = valueAnimator
                }
        textAnimator.setTextStyle(weight = weight, animate = false)

        val resultFontVariationList =
                FontVariationAxis.fromFontVariationSettings(
                        textInterpolator.targetPaint.fontVariationSettings
                )
        expectedFontVariationSettings.forEach { expectedAxis ->
            val resultAxis = resultFontVariationList?.filter { it.tag == expectedAxis.tag }?.get(0)
            assertThat(resultAxis).isNotNull()
            if (resultAxis != null) {
                assertThat(resultAxis.styleValue).isEqualTo(expectedAxis.styleValue)
            }
        }
    }
}