From 523c1ce50086d0d7f69840ed4c8f588525563f57 Mon Sep 17 00:00:00 2001 From: Sherry Zhou Date: Mon, 6 Mar 2023 22:20:27 +0000 Subject: [PATCH] Support change of several axes in textAnimator Test: atest TextAnimatorTest, AnimatableClockViewTest, FontVariationUtilsTest Bug: 268067751 Change-Id: I3253e3b6ce58b38527ee8a47c0cbc77cac33e890 --- .../systemui/animation/FontVariationUtils.kt | 59 +++++++++ .../systemui/animation/TextAnimator.kt | 114 ++++++++++-------- .../animation/FontVariationUtilsTest.kt | 62 ++++++++++ .../systemui/animation/TextAnimatorTest.kt | 68 ----------- 4 files changed, 186 insertions(+), 117 deletions(-) create mode 100644 packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt new file mode 100644 index 0000000000000..78ae4af258fcd --- /dev/null +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt @@ -0,0 +1,59 @@ +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 "" + } +} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt index 7fe94d349c428..9e9929e79d47d 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt @@ -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() + private val fontVariationUtils = FontVariationUtils() + + private val typefaceCache = HashMap() 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) - textInterpolator.targetPaint.typeface = - typefaceCache.getOrElse(weight) { - textInterpolator.targetPaint.fontVariationSettings = - updatedFontVariation - textInterpolator.targetPaint.typeface - } + + if (!fvar.isNullOrBlank()) { + textInterpolator.targetPaint.typeface = + 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() } } + + /** + * 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, + ) + } } -private fun SparseArray.getOrElse(key: Int, defaultValue: () -> V): V { - var v = get(key) - if (v == null) { - v = defaultValue() - put(key, v) - } - return v -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt new file mode 100644 index 0000000000000..070cad7302b23 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt @@ -0,0 +1,62 @@ +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) + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt index 31d0d1292fe7c..ed0cd7ed9b24a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt @@ -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 @@ -179,71 +178,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 - ) { - 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) - } - } - } }