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