Merge "Support change of several axes in textAnimator" into tm-qpr-dev
This commit is contained in:
@@ -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 ""
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -23,11 +23,8 @@ import android.animation.ValueAnimator
|
|||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
import android.graphics.fonts.Font
|
import android.graphics.fonts.Font
|
||||||
import android.graphics.fonts.FontVariationAxis
|
|
||||||
import android.text.Layout
|
import android.text.Layout
|
||||||
import android.util.SparseArray
|
|
||||||
|
|
||||||
private const val TAG_WGHT = "wght"
|
|
||||||
private const val DEFAULT_ANIMATION_DURATION: Long = 300
|
private const val DEFAULT_ANIMATION_DURATION: Long = 300
|
||||||
|
|
||||||
typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit
|
typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit
|
||||||
@@ -51,7 +48,7 @@ typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit
|
|||||||
*
|
*
|
||||||
* // Change the text size with animation.
|
* // Change the text size with animation.
|
||||||
* fun setTextSize(sizePx: Float, animate: Boolean) {
|
* 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
|
protected set
|
||||||
}
|
}
|
||||||
|
|
||||||
private val typefaceCache = SparseArray<Typeface?>()
|
private val fontVariationUtils = FontVariationUtils()
|
||||||
|
|
||||||
|
private val typefaceCache = HashMap<String, Typeface?>()
|
||||||
|
|
||||||
fun updateLayout(layout: Layout) {
|
fun updateLayout(layout: Layout) {
|
||||||
textInterpolator.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.
|
* Bu passing -1 to duration, the default text animation, 1000ms, is used.
|
||||||
* By passing false to animate, the text will be updated without animation.
|
* 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 textSize an optional font size.
|
||||||
* @param colors an optional colors array that must be the same size as numLines passed to
|
* @param colors an optional colors array that must be the same size as numLines passed to
|
||||||
* the TextInterpolator
|
* the TextInterpolator
|
||||||
@@ -199,7 +198,7 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
|
|||||||
* will be used. This is ignored if animate is false.
|
* will be used. This is ignored if animate is false.
|
||||||
*/
|
*/
|
||||||
fun setTextStyle(
|
fun setTextStyle(
|
||||||
weight: Int = -1,
|
fvar: String? = "",
|
||||||
textSize: Float = -1f,
|
textSize: Float = -1f,
|
||||||
color: Int? = null,
|
color: Int? = null,
|
||||||
strokeWidth: Float = -1f,
|
strokeWidth: Float = -1f,
|
||||||
@@ -217,42 +216,16 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
|
|||||||
if (textSize >= 0) {
|
if (textSize >= 0) {
|
||||||
textInterpolator.targetPaint.textSize = textSize
|
textInterpolator.targetPaint.textSize = textSize
|
||||||
}
|
}
|
||||||
if (weight >= 0) {
|
|
||||||
val fontVariationArray =
|
if (!fvar.isNullOrBlank()) {
|
||||||
FontVariationAxis.fromFontVariationSettings(
|
textInterpolator.targetPaint.typeface =
|
||||||
textInterpolator.targetPaint.fontVariationSettings
|
typefaceCache.getOrElse(fvar) {
|
||||||
)
|
textInterpolator.targetPaint.fontVariationSettings = fvar
|
||||||
if (fontVariationArray.isNullOrEmpty()) {
|
typefaceCache.put(fvar, textInterpolator.targetPaint.typeface)
|
||||||
textInterpolator.targetPaint.typeface =
|
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 (color != null) {
|
if (color != null) {
|
||||||
textInterpolator.targetPaint.color = color
|
textInterpolator.targetPaint.color = color
|
||||||
}
|
}
|
||||||
@@ -291,13 +264,56 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
|
|||||||
invalidateCallback()
|
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 <V> SparseArray<V>.getOrElse(key: Int, defaultValue: () -> V): V {
|
|
||||||
var v = get(key)
|
|
||||||
if (v == null) {
|
|
||||||
v = defaultValue()
|
|
||||||
put(key, v)
|
|
||||||
}
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,6 @@ package com.android.systemui.animation
|
|||||||
import android.animation.AnimatorListenerAdapter
|
import android.animation.AnimatorListenerAdapter
|
||||||
import android.animation.ValueAnimator
|
import android.animation.ValueAnimator
|
||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
import android.graphics.fonts.FontVariationAxis
|
|
||||||
import android.testing.AndroidTestingRunner
|
import android.testing.AndroidTestingRunner
|
||||||
import android.text.Layout
|
import android.text.Layout
|
||||||
import android.text.StaticLayout
|
import android.text.StaticLayout
|
||||||
@@ -179,71 +178,4 @@ class TextAnimatorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
assertThat(paint.typeface).isSameInstanceAs(prevTypeface)
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user