Merge "Allow TextAnimator TypefaceCaches to be shared" into udc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
46df96f6cc
@@ -24,12 +24,30 @@ import android.graphics.Canvas
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.fonts.Font
|
||||
import android.text.Layout
|
||||
import android.text.TextPaint
|
||||
import android.util.LruCache
|
||||
|
||||
private const val DEFAULT_ANIMATION_DURATION: Long = 300
|
||||
private const val TYPEFACE_CACHE_MAX_ENTRIES = 5
|
||||
|
||||
typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit
|
||||
|
||||
interface TypefaceVariantCache {
|
||||
fun getTypefaceForVariant(fvar: String, targetPaint: TextPaint): Typeface?
|
||||
}
|
||||
|
||||
class TypefaceVariantCacheImpl() : TypefaceVariantCache {
|
||||
private val cache = LruCache<String, Typeface>(TYPEFACE_CACHE_MAX_ENTRIES)
|
||||
override fun getTypefaceForVariant(fvar: String, targetPaint: TextPaint): Typeface? {
|
||||
cache.get(fvar)?.let {
|
||||
return it
|
||||
}
|
||||
|
||||
targetPaint.fontVariationSettings = fvar
|
||||
return targetPaint.typeface?.also { cache.put(fvar, it) }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class provides text animation between two styles.
|
||||
*
|
||||
@@ -56,9 +74,19 @@ typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit
|
||||
* ```
|
||||
* </code> </pre>
|
||||
*/
|
||||
class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
|
||||
class TextAnimator(
|
||||
layout: Layout,
|
||||
private val invalidateCallback: () -> Unit,
|
||||
) {
|
||||
var typefaceCache: TypefaceVariantCache = TypefaceVariantCacheImpl()
|
||||
get() = field
|
||||
set(value) {
|
||||
field = value
|
||||
textInterpolator.typefaceCache = value
|
||||
}
|
||||
|
||||
// Following two members are for mutable for testing purposes.
|
||||
public var textInterpolator: TextInterpolator = TextInterpolator(layout)
|
||||
public var textInterpolator: TextInterpolator = TextInterpolator(layout, typefaceCache)
|
||||
public var animator: ValueAnimator =
|
||||
ValueAnimator.ofFloat(1f).apply {
|
||||
duration = DEFAULT_ANIMATION_DURATION
|
||||
@@ -68,9 +96,7 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
|
||||
}
|
||||
addListener(
|
||||
object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator?) {
|
||||
textInterpolator.rebase()
|
||||
}
|
||||
override fun onAnimationEnd(animation: Animator?) = textInterpolator.rebase()
|
||||
override fun onAnimationCancel(animation: Animator?) = textInterpolator.rebase()
|
||||
}
|
||||
)
|
||||
@@ -116,8 +142,6 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
|
||||
|
||||
private val fontVariationUtils = FontVariationUtils()
|
||||
|
||||
private val typefaceCache = LruCache<String, Typeface>(TYPEFACE_CACHE_MAX_ENTRIES)
|
||||
|
||||
fun updateLayout(layout: Layout) {
|
||||
textInterpolator.layout = layout
|
||||
}
|
||||
@@ -220,12 +244,8 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
|
||||
}
|
||||
|
||||
if (!fvar.isNullOrBlank()) {
|
||||
textInterpolator.targetPaint.typeface = typefaceCache.get(fvar) ?: run {
|
||||
textInterpolator.targetPaint.fontVariationSettings = fvar
|
||||
textInterpolator.targetPaint.typeface?.also {
|
||||
typefaceCache.put(fvar, textInterpolator.targetPaint.typeface)
|
||||
}
|
||||
}
|
||||
textInterpolator.targetPaint.typeface =
|
||||
typefaceCache.getTypefaceForVariant(fvar, textInterpolator.targetPaint)
|
||||
}
|
||||
|
||||
if (color != null) {
|
||||
@@ -304,7 +324,8 @@ class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) {
|
||||
weight = weight,
|
||||
width = width,
|
||||
opticalSize = opticalSize,
|
||||
roundness = roundness,)
|
||||
roundness = roundness,
|
||||
)
|
||||
setTextStyle(
|
||||
fvar = fvar,
|
||||
textSize = textSize,
|
||||
|
||||
@@ -28,8 +28,10 @@ import com.android.internal.graphics.ColorUtils
|
||||
import java.lang.Math.max
|
||||
|
||||
/** Provide text style linear interpolation for plain text. */
|
||||
class TextInterpolator(layout: Layout) {
|
||||
|
||||
class TextInterpolator(
|
||||
layout: Layout,
|
||||
var typefaceCache: TypefaceVariantCache,
|
||||
) {
|
||||
/**
|
||||
* Returns base paint used for interpolation.
|
||||
*
|
||||
|
||||
@@ -31,6 +31,7 @@ import android.text.TextPaint
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.File
|
||||
@@ -64,6 +65,7 @@ private val END_PAINT = TextPaint(PAINT).apply {
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@SmallTest
|
||||
class TextInterpolatorTest : SysuiTestCase() {
|
||||
lateinit var typefaceCache: TypefaceVariantCache
|
||||
|
||||
private fun makeLayout(
|
||||
text: String,
|
||||
@@ -75,11 +77,16 @@ class TextInterpolatorTest : SysuiTestCase() {
|
||||
.setTextDirection(dir).build()
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
typefaceCache = TypefaceVariantCacheImpl()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testStartState() {
|
||||
val layout = makeLayout(TEXT, PAINT)
|
||||
|
||||
val interp = TextInterpolator(layout)
|
||||
val interp = TextInterpolator(layout, typefaceCache)
|
||||
interp.basePaint.set(START_PAINT)
|
||||
interp.onBasePaintModified()
|
||||
|
||||
@@ -98,7 +105,7 @@ class TextInterpolatorTest : SysuiTestCase() {
|
||||
fun testEndState() {
|
||||
val layout = makeLayout(TEXT, PAINT)
|
||||
|
||||
val interp = TextInterpolator(layout)
|
||||
val interp = TextInterpolator(layout, typefaceCache)
|
||||
interp.basePaint.set(START_PAINT)
|
||||
interp.onBasePaintModified()
|
||||
|
||||
@@ -116,7 +123,7 @@ class TextInterpolatorTest : SysuiTestCase() {
|
||||
fun testMiddleState() {
|
||||
val layout = makeLayout(TEXT, PAINT)
|
||||
|
||||
val interp = TextInterpolator(layout)
|
||||
val interp = TextInterpolator(layout, typefaceCache)
|
||||
interp.basePaint.set(START_PAINT)
|
||||
interp.onBasePaintModified()
|
||||
|
||||
@@ -138,7 +145,7 @@ class TextInterpolatorTest : SysuiTestCase() {
|
||||
fun testRebase() {
|
||||
val layout = makeLayout(TEXT, PAINT)
|
||||
|
||||
val interp = TextInterpolator(layout)
|
||||
val interp = TextInterpolator(layout, typefaceCache)
|
||||
interp.basePaint.set(START_PAINT)
|
||||
interp.onBasePaintModified()
|
||||
|
||||
@@ -160,7 +167,7 @@ class TextInterpolatorTest : SysuiTestCase() {
|
||||
fun testBidi_LTR() {
|
||||
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.LTR)
|
||||
|
||||
val interp = TextInterpolator(layout)
|
||||
val interp = TextInterpolator(layout, typefaceCache)
|
||||
interp.basePaint.set(START_PAINT)
|
||||
interp.onBasePaintModified()
|
||||
|
||||
@@ -180,7 +187,7 @@ class TextInterpolatorTest : SysuiTestCase() {
|
||||
fun testBidi_RTL() {
|
||||
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
|
||||
|
||||
val interp = TextInterpolator(layout)
|
||||
val interp = TextInterpolator(layout, typefaceCache)
|
||||
interp.basePaint.set(START_PAINT)
|
||||
interp.onBasePaintModified()
|
||||
|
||||
@@ -200,7 +207,7 @@ class TextInterpolatorTest : SysuiTestCase() {
|
||||
fun testGlyphCallback_Empty() {
|
||||
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
|
||||
|
||||
val interp = TextInterpolator(layout).apply {
|
||||
val interp = TextInterpolator(layout, typefaceCache).apply {
|
||||
glyphFilter = { glyph, progress ->
|
||||
}
|
||||
}
|
||||
@@ -222,7 +229,7 @@ class TextInterpolatorTest : SysuiTestCase() {
|
||||
fun testGlyphCallback_Xcoordinate() {
|
||||
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
|
||||
|
||||
val interp = TextInterpolator(layout).apply {
|
||||
val interp = TextInterpolator(layout, typefaceCache).apply {
|
||||
glyphFilter = { glyph, progress ->
|
||||
glyph.x += 30f
|
||||
}
|
||||
@@ -247,7 +254,7 @@ class TextInterpolatorTest : SysuiTestCase() {
|
||||
fun testGlyphCallback_Ycoordinate() {
|
||||
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
|
||||
|
||||
val interp = TextInterpolator(layout).apply {
|
||||
val interp = TextInterpolator(layout, typefaceCache).apply {
|
||||
glyphFilter = { glyph, progress ->
|
||||
glyph.y += 30f
|
||||
}
|
||||
@@ -272,7 +279,7 @@ class TextInterpolatorTest : SysuiTestCase() {
|
||||
fun testGlyphCallback_TextSize() {
|
||||
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
|
||||
|
||||
val interp = TextInterpolator(layout).apply {
|
||||
val interp = TextInterpolator(layout, typefaceCache).apply {
|
||||
glyphFilter = { glyph, progress ->
|
||||
glyph.textSize += 10f
|
||||
}
|
||||
@@ -297,7 +304,7 @@ class TextInterpolatorTest : SysuiTestCase() {
|
||||
fun testGlyphCallback_Color() {
|
||||
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
|
||||
|
||||
val interp = TextInterpolator(layout).apply {
|
||||
val interp = TextInterpolator(layout, typefaceCache).apply {
|
||||
glyphFilter = { glyph, progress ->
|
||||
glyph.color = Color.RED
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user