Loading packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt 0 → 100644 +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 "" } } packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt +64 −48 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) * } * } * ``` Loading Loading @@ -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 Loading Loading @@ -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 Loading @@ -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, Loading @@ -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 } Loading Loading @@ -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 } packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt 0 → 100644 +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) } } packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt +0 −68 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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<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) } } } } Loading
packages/SystemUI/animation/src/com/android/systemui/animation/FontVariationUtils.kt 0 → 100644 +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 "" } }
packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt +64 −48 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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) * } * } * ``` Loading Loading @@ -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 Loading Loading @@ -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 Loading @@ -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, Loading @@ -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 } Loading Loading @@ -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 }
packages/SystemUI/tests/src/com/android/systemui/animation/FontVariationUtilsTest.kt 0 → 100644 +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) } }
packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt +0 −68 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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<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) } } } }