Merge "Reduce the number of Font instance creation" into main
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
index addabcc..a137891 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt
@@ -34,66 +34,60 @@
private const val FONT_ITALIC_ANIMATION_STEP = 0.1f
private const val FONT_ITALIC_DEFAULT_VALUE = 0f
-// Benchmarked via Perfetto, difference between 10 and 50 entries is about 0.3ms in
-// frame draw time on a Pixel 6.
-@VisibleForTesting const val DEFAULT_FONT_CACHE_MAX_ENTRIES = 10
+/** Caches for font interpolation */
+interface FontCache {
+ val animationFrameCount: Int
-/** Provide interpolation of two fonts by adjusting font variation settings. */
-class FontInterpolator(
- numberOfAnimationSteps: Int? = null,
-) {
- /**
- * Cache key for the interpolated font.
- *
- * This class is mutable for recycling.
- */
- private data class InterpKey(var l: Font?, var r: Font?, var progress: Float) {
- fun set(l: Font, r: Font, progress: Float) {
- this.l = l
- this.r = r
- this.progress = progress
- }
- }
+ fun get(key: InterpKey): Font?
- /**
- * Cache key for the font that has variable font.
- *
- * This class is mutable for recycling.
- */
- private data class VarFontKey(
- var sourceId: Int,
- var index: Int,
- val sortedAxes: MutableList<FontVariationAxis>
- ) {
- constructor(
- font: Font,
- axes: List<FontVariationAxis>
- ) : this(
- font.sourceIdentifier,
- font.ttcIndex,
- axes.toMutableList().apply { sortBy { it.tag } }
- )
+ fun get(key: VarFontKey): Font?
- fun set(font: Font, axes: List<FontVariationAxis>) {
- sourceId = font.sourceIdentifier
- index = font.ttcIndex
- sortedAxes.clear()
- sortedAxes.addAll(axes)
- sortedAxes.sortBy { it.tag }
- }
- }
+ fun put(key: InterpKey, font: Font)
+ fun put(key: VarFontKey, font: Font)
+}
+
+/** Cache key for the interpolated font. */
+data class InterpKey(val start: Font?, val end: Font?, val frame: Int)
+
+/** Cache key for the font that has variable font. */
+data class VarFontKey(val sourceId: Int, val index: Int, val sortedAxes: List<FontVariationAxis>) {
+ constructor(
+ font: Font,
+ axes: List<FontVariationAxis>,
+ ) : this(font.sourceIdentifier, font.ttcIndex, axes.sortedBy { it.tag })
+}
+
+class FontCacheImpl(override val animationFrameCount: Int = DEFAULT_FONT_CACHE_MAX_ENTRIES / 2) :
+ FontCache {
// Font interpolator has two level caches: one for input and one for font with different
// variation settings. No synchronization is needed since FontInterpolator is not designed to be
// thread-safe and can be used only on UI thread.
- val cacheMaxEntries = numberOfAnimationSteps?.let { it * 2 } ?: DEFAULT_FONT_CACHE_MAX_ENTRIES
+ val cacheMaxEntries = animationFrameCount * 2
private val interpCache = LruCache<InterpKey, Font>(cacheMaxEntries)
private val verFontCache = LruCache<VarFontKey, Font>(cacheMaxEntries)
- // Mutable keys for recycling.
- private val tmpInterpKey = InterpKey(null, null, 0f)
- private val tmpVarFontKey = VarFontKey(0, 0, mutableListOf())
+ override fun get(key: InterpKey): Font? = interpCache[key]
+ override fun get(key: VarFontKey): Font? = verFontCache[key]
+
+ override fun put(key: InterpKey, font: Font) {
+ interpCache.put(key, font)
+ }
+
+ override fun put(key: VarFontKey, font: Font) {
+ verFontCache.put(key, font)
+ }
+
+ companion object {
+ // Benchmarked via Perfetto, difference between 10 and 50 entries is about 0.3ms in frame
+ // draw time on a Pixel 6.
+ @VisibleForTesting const val DEFAULT_FONT_CACHE_MAX_ENTRIES = 10
+ }
+}
+
+/** Provide interpolation of two fonts by adjusting font variation settings. */
+class FontInterpolator(val fontCache: FontCache = FontCacheImpl()) {
/** Linear interpolate the font variation settings. */
fun lerp(start: Font, end: Font, progress: Float): Font {
if (progress == 0f) {
@@ -111,13 +105,12 @@
// Check we already know the result. This is commonly happens since we draws the different
// text chunks with the same font.
- tmpInterpKey.set(start, end, progress)
- val cachedFont = interpCache[tmpInterpKey]
- if (cachedFont != null) {
+ val iKey = InterpKey(start, end, (progress * fontCache.animationFrameCount).toInt())
+ fontCache.get(iKey)?.let {
if (DEBUG) {
- Log.d(LOG_TAG, "[$progress] Interp. cache hit for $tmpInterpKey")
+ Log.d(LOG_TAG, "[$progress] Interp. cache hit for $iKey")
}
- return cachedFont
+ return it
}
// General axes interpolation takes O(N log N), this is came from sorting the axes. Usually
@@ -131,14 +124,14 @@
MathUtils.lerp(
startValue ?: FONT_WEIGHT_DEFAULT_VALUE,
endValue ?: FONT_WEIGHT_DEFAULT_VALUE,
- progress
+ progress,
)
TAG_ITAL ->
adjustItalic(
MathUtils.lerp(
startValue ?: FONT_ITALIC_DEFAULT_VALUE,
endValue ?: FONT_ITALIC_DEFAULT_VALUE,
- progress
+ progress,
)
)
else -> {
@@ -152,32 +145,31 @@
// Check if we already make font for this axes. This is typically happens if the animation
// happens backward.
- tmpVarFontKey.set(start, newAxes)
- val axesCachedFont = verFontCache[tmpVarFontKey]
- if (axesCachedFont != null) {
- interpCache.put(InterpKey(start, end, progress), axesCachedFont)
+ val vKey = VarFontKey(start, newAxes)
+ fontCache.get(vKey)?.let {
+ fontCache.put(iKey, it)
if (DEBUG) {
- Log.d(LOG_TAG, "[$progress] Axis cache hit for $tmpVarFontKey")
+ Log.d(LOG_TAG, "[$progress] Axis cache hit for $vKey")
}
- return axesCachedFont
+ return it
}
// This is the first time to make the font for the axes. Build and store it to the cache.
// Font.Builder#build won't throw IOException since creating fonts from existing fonts will
// not do any IO work.
val newFont = Font.Builder(start).setFontVariationSettings(newAxes.toTypedArray()).build()
- interpCache.put(InterpKey(start, end, progress), newFont)
- verFontCache.put(VarFontKey(start, newAxes), newFont)
+ fontCache.put(iKey, newFont)
+ fontCache.put(vKey, newFont)
// Cache misses are likely to create memory leaks, so this is logged at error level.
- Log.e(LOG_TAG, "[$progress] Cache MISS for $tmpInterpKey / $tmpVarFontKey")
+ Log.e(LOG_TAG, "[$progress] Cache MISS for $iKey / $vKey")
return newFont
}
private fun lerp(
start: Array<FontVariationAxis>,
end: Array<FontVariationAxis>,
- filter: (tag: String, left: Float?, right: Float?) -> Float
+ filter: (tag: String, left: Float?, right: Float?) -> Float,
): List<FontVariationAxis> {
// Safe to modify result of Font#getAxes since it returns cloned object.
start.sortBy { axis -> axis.tag }
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 978943ae..eef26b6 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt
@@ -35,6 +35,8 @@
typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit
interface TypefaceVariantCache {
+ val fontCache: FontCache
+ val animationFrameCount: Int
fun getTypefaceForVariant(fvar: String?): Typeface?
companion object {
@@ -57,8 +59,10 @@
class TypefaceVariantCacheImpl(
var baseTypeface: Typeface,
+ override val animationFrameCount: Int,
) : TypefaceVariantCache {
private val cache = LruCache<String, Typeface>(TYPEFACE_CACHE_MAX_ENTRIES)
+ override val fontCache = FontCacheImpl(animationFrameCount)
override fun getTypefaceForVariant(fvar: String?): Typeface? {
if (fvar == null) {
return baseTypeface
@@ -100,25 +104,17 @@
*/
class TextAnimator(
layout: Layout,
- numberOfAnimationSteps: Int? = null, // Only do this number of discrete animation steps.
- private val invalidateCallback: () -> Unit,
+ private val typefaceCache: TypefaceVariantCache,
+ private val invalidateCallback: () -> Unit = {},
) {
- var typefaceCache: TypefaceVariantCache = TypefaceVariantCacheImpl(layout.paint.typeface)
- get() = field
- set(value) {
- field = value
- textInterpolator.typefaceCache = value
- }
-
// Following two members are for mutable for testing purposes.
- public var textInterpolator: TextInterpolator =
- TextInterpolator(layout, typefaceCache, numberOfAnimationSteps)
- public var animator: ValueAnimator =
+ public var textInterpolator = TextInterpolator(layout, typefaceCache)
+ public var animator =
ValueAnimator.ofFloat(1f).apply {
duration = DEFAULT_ANIMATION_DURATION
addUpdateListener {
textInterpolator.progress =
- calculateProgress(it.animatedValue as Float, numberOfAnimationSteps)
+ calculateProgress(it.animatedValue as Float, typefaceCache.animationFrameCount)
invalidateCallback()
}
addListener(
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
index 02caeed..9c0c0ff 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt
@@ -28,11 +28,7 @@
import java.lang.Math.max
/** Provide text style linear interpolation for plain text. */
-class TextInterpolator(
- layout: Layout,
- var typefaceCache: TypefaceVariantCache,
- numberOfAnimationSteps: Int? = null,
-) {
+class TextInterpolator(layout: Layout, var typefaceCache: TypefaceVariantCache) {
/**
* Returns base paint used for interpolation.
*
@@ -66,7 +62,7 @@
val start: Int, // inclusive
val end: Int, // exclusive
var baseFont: Font,
- var targetFont: Font
+ var targetFont: Font,
) {
val length: Int
get() = end - start
@@ -79,14 +75,14 @@
val baseY: FloatArray, // same length as glyphIds
val targetX: FloatArray, // same length as glyphIds
val targetY: FloatArray, // same length as glyphIds
- val fontRuns: List<FontRun>
+ val fontRuns: List<FontRun>,
)
/** A class represents text layout of a single line. */
private class Line(val runs: List<Run>)
private var lines = listOf<Line>()
- private val fontInterpolator = FontInterpolator(numberOfAnimationSteps)
+ private val fontInterpolator = FontInterpolator(typefaceCache.fontCache)
// Recycling object for glyph drawing and tweaking.
private val tmpPaint = TextPaint()
@@ -343,12 +339,16 @@
private class MutablePositionedGlyph : TextAnimator.PositionedGlyph() {
override var runStart: Int = 0
public set
+
override var runLength: Int = 0
public set
+
override var glyphIndex: Int = 0
public set
+
override lateinit var font: Font
public set
+
override var glyphId: Int = 0
public set
}
@@ -401,7 +401,7 @@
0,
i - prevStart,
font,
- tmpPaintForGlyph
+ tmpPaintForGlyph,
)
prevStart = i
arrayIndex = 0
@@ -418,13 +418,13 @@
0,
run.end - prevStart,
font,
- tmpPaintForGlyph
+ tmpPaintForGlyph,
)
}
private fun updatePositionsAndFonts(
layoutResult: List<List<PositionedGlyphs>>,
- updateBase: Boolean
+ updateBase: Boolean,
) {
// Update target positions with newly calculated text layout.
check(layoutResult.size == lines.size) { "The new layout result has different line count." }
@@ -507,7 +507,7 @@
lineStart,
count,
layout.textDirectionHeuristic,
- paint
+ paint,
) { _, _, glyphs, _ ->
runs.add(glyphs)
}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index 9877406..801a2d6 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -34,6 +34,7 @@
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.animation.GlyphCallback
import com.android.systemui.animation.TextAnimator
+import com.android.systemui.animation.TypefaceVariantCacheImpl
import com.android.systemui.customization.R
import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.core.LogcatOnlyMessageBuffer
@@ -98,7 +99,8 @@
@VisibleForTesting
var textAnimatorFactory: (Layout, () -> Unit) -> TextAnimator = { layout, invalidateCb ->
- TextAnimator(layout, NUM_CLOCK_FONT_ANIMATION_STEPS, invalidateCb)
+ val cache = TypefaceVariantCacheImpl(layout.paint.typeface, NUM_CLOCK_FONT_ANIMATION_STEPS)
+ TextAnimator(layout, cache, invalidateCb)
}
// Used by screenshot tests to provide stability
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockAnimation.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockAnimation.kt
deleted file mode 100644
index 5a04169..0000000
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockAnimation.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2024 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.shared.clocks
-
-object ClockAnimation {
- const val NUM_CLOCK_FONT_ANIMATION_STEPS = 30
-}
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
index 9bb92bc..e898725 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt
@@ -67,7 +67,10 @@
val buffers = messageBuffers ?: ClockMessageBuffers(LogUtil.DEFAULT_MESSAGE_BUFFER)
val fontAxes = ClockFontAxis.merge(FlexClockController.FONT_AXES, settings.axes)
val clockSettings = settings.copy(axes = fontAxes.map { it.toSetting() })
- val typefaceCache = TypefaceCache(buffers.infraMessageBuffer) { FLEX_TYPEFACE }
+ val typefaceCache =
+ TypefaceCache(buffers.infraMessageBuffer, NUM_CLOCK_FONT_ANIMATION_STEPS) {
+ FLEX_TYPEFACE
+ }
FlexClockController(
ClockContext(
ctx,
@@ -110,6 +113,8 @@
}
companion object {
+ const val NUM_CLOCK_FONT_ANIMATION_STEPS = 30
+
// TODO(b/364681643): Variations for retargetted DIGITAL_CLOCK_FLEX
val LEGACY_FLEX_LS_VARIATION =
listOf(
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/TypefaceCache.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/TypefaceCache.kt
index f5a9375..9e3f6d9 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/TypefaceCache.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/TypefaceCache.kt
@@ -17,13 +17,18 @@
package com.android.systemui.shared.clocks
import android.graphics.Typeface
+import com.android.systemui.animation.FontCacheImpl
import com.android.systemui.animation.TypefaceVariantCache
import com.android.systemui.log.core.Logger
import com.android.systemui.log.core.MessageBuffer
import java.lang.ref.ReferenceQueue
import java.lang.ref.WeakReference
-class TypefaceCache(messageBuffer: MessageBuffer, val typefaceFactory: (String) -> Typeface) {
+class TypefaceCache(
+ messageBuffer: MessageBuffer,
+ val animationFrameCount: Int,
+ val typefaceFactory: (String) -> Typeface,
+) {
private val logger = Logger(messageBuffer, this::class.simpleName!!)
private data class CacheKey(val res: String, val fvar: String?)
@@ -44,6 +49,7 @@
// result, once a typeface is no longer being used, it is unlikely to be recreated immediately.
private val cache = mutableMapOf<CacheKey, WeakTypefaceRef>()
private val queue = ReferenceQueue<Typeface>()
+ private val fontCache = FontCacheImpl(animationFrameCount)
fun getTypeface(res: String): Typeface {
checkQueue()
@@ -62,6 +68,9 @@
fun getVariantCache(res: String): TypefaceVariantCache {
val baseTypeface = getTypeface(res)
return object : TypefaceVariantCache {
+ override val fontCache = this@TypefaceCache.fontCache
+ override val animationFrameCount = this@TypefaceCache.animationFrameCount
+
override fun getTypefaceForVariant(fvar: String?): Typeface? {
checkQueue()
val key = CacheKey(res, fvar)
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
index 48761c0..cef24e9 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextView.kt
@@ -39,7 +39,6 @@
import com.android.systemui.customization.R
import com.android.systemui.log.core.Logger
import com.android.systemui.plugins.clocks.ClockFontAxisSetting
-import com.android.systemui.shared.clocks.ClockAnimation
import com.android.systemui.shared.clocks.ClockContext
import com.android.systemui.shared.clocks.DigitTranslateAnimator
import com.android.systemui.shared.clocks.DimensionParser
@@ -96,9 +95,7 @@
@VisibleForTesting
var textAnimatorFactory: (Layout, () -> Unit) -> TextAnimator = { layout, invalidateCb ->
- TextAnimator(layout, ClockAnimation.NUM_CLOCK_FONT_ANIMATION_STEPS, invalidateCb).also {
- it.typefaceCache = typefaceCache
- }
+ TextAnimator(layout, typefaceCache, invalidateCb)
}
override var verticalAlignment: VerticalAlignment = VerticalAlignment.BASELINE
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
index ec42b7f..97abba7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt
@@ -31,8 +31,8 @@
@SmallTest
class FontInterpolatorTest : SysuiTestCase() {
- private val sFont = TextRunShaper.shapeTextRun("A", 0, 1, 0, 1, 0f, 0f, false, Paint())
- .getFont(0)
+ private val sFont =
+ TextRunShaper.shapeTextRun("A", 0, 1, 0, 1, 0f, 0f, false, Paint()).getFont(0)
private fun assertSameAxes(expect: Font, actual: Font) {
val expectAxes = expect.axes?.also { it.sortBy { axis -> axis.tag } }
@@ -42,21 +42,20 @@
private fun assertSameAxes(expectVarSettings: String, actual: Font) {
- val expectAxes = FontVariationAxis.fromFontVariationSettings(expectVarSettings)?.also {
- it.sortBy { axis -> axis.tag }
- }
+ val expectAxes =
+ FontVariationAxis.fromFontVariationSettings(expectVarSettings)?.also {
+ it.sortBy { axis -> axis.tag }
+ }
val actualAxes = actual.axes?.also { it.sortBy { axis -> axis.tag } }
assertThat(actualAxes).isEqualTo(expectAxes)
}
@Test
fun textInterpolation() {
- val startFont = Font.Builder(sFont)
- .setFontVariationSettings("'wght' 100, 'ital' 0, 'GRAD' 200")
- .build()
- val endFont = Font.Builder(sFont)
- .setFontVariationSettings("'wght' 900, 'ital' 1, 'GRAD' 700")
- .build()
+ val startFont =
+ Font.Builder(sFont).setFontVariationSettings("'wght' 100, 'ital' 0, 'GRAD' 200").build()
+ val endFont =
+ Font.Builder(sFont).setFontVariationSettings("'wght' 900, 'ital' 1, 'GRAD' 700").build()
val interp = FontInterpolator()
assertSameAxes(startFont, interp.lerp(startFont, endFont, 0f))
@@ -66,12 +65,8 @@
@Test
fun textInterpolation_DefaultValue() {
- val startFont = Font.Builder(sFont)
- .setFontVariationSettings("'wght' 100")
- .build()
- val endFont = Font.Builder(sFont)
- .setFontVariationSettings("'ital' 1")
- .build()
+ val startFont = Font.Builder(sFont).setFontVariationSettings("'wght' 100").build()
+ val endFont = Font.Builder(sFont).setFontVariationSettings("'ital' 1").build()
val interp = FontInterpolator()
assertSameAxes("'wght' 250, 'ital' 0.5", interp.lerp(startFont, endFont, 0.5f))
@@ -79,12 +74,8 @@
@Test
fun testInterpCache() {
- val startFont = Font.Builder(sFont)
- .setFontVariationSettings("'wght' 100")
- .build()
- val endFont = Font.Builder(sFont)
- .setFontVariationSettings("'ital' 1")
- .build()
+ val startFont = Font.Builder(sFont).setFontVariationSettings("'wght' 100").build()
+ val endFont = Font.Builder(sFont).setFontVariationSettings("'ital' 1").build()
val interp = FontInterpolator()
val resultFont = interp.lerp(startFont, endFont, 0.5f)
@@ -94,12 +85,8 @@
@Test
fun testAxesCache() {
- val startFont = Font.Builder(sFont)
- .setFontVariationSettings("'wght' 100")
- .build()
- val endFont = Font.Builder(sFont)
- .setFontVariationSettings("'ital' 1")
- .build()
+ val startFont = Font.Builder(sFont).setFontVariationSettings("'wght' 100").build()
+ val endFont = Font.Builder(sFont).setFontVariationSettings("'ital' 1").build()
val interp = FontInterpolator()
val resultFont = interp.lerp(startFont, endFont, 0.5f)
@@ -111,20 +98,12 @@
fun testCacheMaxSize() {
val interp = FontInterpolator()
- val startFont = Font.Builder(sFont)
- .setFontVariationSettings("'wght' 100")
- .build()
- val endFont = Font.Builder(sFont)
- .setFontVariationSettings("'wght' 1")
- .build()
+ val startFont = Font.Builder(sFont).setFontVariationSettings("'wght' 100").build()
+ val endFont = Font.Builder(sFont).setFontVariationSettings("'wght' 1").build()
val resultFont = interp.lerp(startFont, endFont, 0.5f)
- for (i in 0..interp.cacheMaxEntries + 1) {
- val f1 = Font.Builder(sFont)
- .setFontVariationSettings("'wght' ${i * 100}")
- .build()
- val f2 = Font.Builder(sFont)
- .setFontVariationSettings("'wght' $i")
- .build()
+ for (i in 0..(interp.fontCache as FontCacheImpl).cacheMaxEntries + 1) {
+ val f1 = Font.Builder(sFont).setFontVariationSettings("'wght' ${i * 100}").build()
+ val f2 = Font.Builder(sFont).setFontVariationSettings("'wght' $i").build()
interp.lerp(f1, f2, 0.5f)
}
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 6ba1715..dcf38800 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt
@@ -42,6 +42,8 @@
@SmallTest
class TextAnimatorTest : SysuiTestCase() {
+ private val typeface = Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf")
+
private fun makeLayout(text: String, paint: TextPaint): Layout {
val width = ceil(Layout.getDesiredWidth(text, 0, text.length, paint)).toInt()
return StaticLayout.Builder.obtain(text, 0, text.length, paint, width).build()
@@ -56,7 +58,7 @@
`when`(textInterpolator.targetPaint).thenReturn(paint)
val textAnimator =
- TextAnimator(layout, null, {}).apply {
+ TextAnimator(layout, TypefaceVariantCacheImpl(typeface, 20)).apply {
this.textInterpolator = textInterpolator
this.animator = valueAnimator
}
@@ -86,7 +88,7 @@
`when`(textInterpolator.targetPaint).thenReturn(paint)
val textAnimator =
- TextAnimator(layout, null, {}).apply {
+ TextAnimator(layout, TypefaceVariantCacheImpl(typeface, 20)).apply {
this.textInterpolator = textInterpolator
this.animator = valueAnimator
}
@@ -114,7 +116,7 @@
val animationEndCallback = mock(Runnable::class.java)
val textAnimator =
- TextAnimator(layout, null, {}).apply {
+ TextAnimator(layout, TypefaceVariantCacheImpl(typeface, 20)).apply {
this.textInterpolator = textInterpolator
this.animator = valueAnimator
}
@@ -122,7 +124,7 @@
textAnimator.setTextStyle(
weight = 400,
animate = true,
- onAnimationEnd = animationEndCallback
+ onAnimationEnd = animationEndCallback,
)
// Verify animationEnd callback has been added.
@@ -140,14 +142,11 @@
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")
- }
+ val paint = TextPaint().apply { this.typeface = typeface }
`when`(textInterpolator.targetPaint).thenReturn(paint)
val textAnimator =
- TextAnimator(layout, null, {}).apply {
+ TextAnimator(layout, TypefaceVariantCacheImpl(typeface, 20)).apply {
this.textInterpolator = textInterpolator
this.animator = valueAnimator
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt
index cca5f35..c6fbe3f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/TextInterpolatorTest.kt
@@ -31,11 +31,11 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.google.common.truth.Truth.assertThat
+import java.io.File
+import kotlin.math.ceil
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import java.io.File
-import kotlin.math.ceil
private const val TEXT = "Hello, World."
private const val BIDI_TEXT = "abc\u05D0\u05D1\u05D2"
@@ -47,20 +47,23 @@
private val VF_FONT = Font.Builder(File("/system/fonts/Roboto-Regular.ttf")).build()
private fun Font.toTypeface() =
- Typeface.CustomFallbackBuilder(FontFamily.Builder(this).build()).build()
+ Typeface.CustomFallbackBuilder(FontFamily.Builder(this).build()).build()
-internal val PAINT = TextPaint().apply {
- typeface = Font.Builder(VF_FONT).setFontVariationSettings("'wght' 400").build().toTypeface()
- textSize = 32f
-}
+internal val PAINT =
+ TextPaint().apply {
+ typeface = Font.Builder(VF_FONT).setFontVariationSettings("'wght' 400").build().toTypeface()
+ textSize = 32f
+ }
-private val START_PAINT = TextPaint(PAINT).apply {
- typeface = Font.Builder(VF_FONT).setFontVariationSettings("'wght' 400").build().toTypeface()
-}
+private val START_PAINT =
+ TextPaint(PAINT).apply {
+ typeface = Font.Builder(VF_FONT).setFontVariationSettings("'wght' 400").build().toTypeface()
+ }
-private val END_PAINT = TextPaint(PAINT).apply {
- typeface = Font.Builder(VF_FONT).setFontVariationSettings("'wght' 700").build().toTypeface()
-}
+private val END_PAINT =
+ TextPaint(PAINT).apply {
+ typeface = Font.Builder(VF_FONT).setFontVariationSettings("'wght' 700").build().toTypeface()
+ }
@RunWith(AndroidJUnit4::class)
@SmallTest
@@ -70,16 +73,17 @@
private fun makeLayout(
text: String,
paint: TextPaint,
- dir: TextDirectionHeuristic = TextDirectionHeuristics.LTR
+ dir: TextDirectionHeuristic = TextDirectionHeuristics.LTR,
): Layout {
val width = ceil(Layout.getDesiredWidth(text, 0, text.length, paint)).toInt()
return StaticLayout.Builder.obtain(text, 0, text.length, paint, width)
- .setTextDirection(dir).build()
+ .setTextDirection(dir)
+ .build()
}
@Before
fun setup() {
- typefaceCache = TypefaceVariantCacheImpl(PAINT.typeface)
+ typefaceCache = TypefaceVariantCacheImpl(PAINT.typeface, 10)
}
@Test
@@ -135,10 +139,10 @@
// end state.
interp.progress = 0.5f
val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
- assertThat(actual.sameAs(makeLayout(TEXT, START_PAINT)
- .toBitmap(BMP_WIDTH, BMP_HEIGHT))).isFalse()
- assertThat(actual.sameAs(makeLayout(TEXT, END_PAINT)
- .toBitmap(BMP_WIDTH, BMP_HEIGHT))).isFalse()
+ assertThat(actual.sameAs(makeLayout(TEXT, START_PAINT).toBitmap(BMP_WIDTH, BMP_HEIGHT)))
+ .isFalse()
+ assertThat(actual.sameAs(makeLayout(TEXT, END_PAINT).toBitmap(BMP_WIDTH, BMP_HEIGHT)))
+ .isFalse()
}
@Test
@@ -177,7 +181,8 @@
// Just after created TextInterpolator, it should have 0 progress.
assertThat(interp.progress).isEqualTo(0f)
val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
- val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.LTR)
+ val expected =
+ makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.LTR)
.toBitmap(BMP_WIDTH, BMP_HEIGHT)
assertThat(expected.sameAs(actual)).isTrue()
@@ -197,7 +202,8 @@
// Just after created TextInterpolator, it should have 0 progress.
assertThat(interp.progress).isEqualTo(0f)
val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
- val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
+ val expected =
+ makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
.toBitmap(BMP_WIDTH, BMP_HEIGHT)
assertThat(expected.sameAs(actual)).isTrue()
@@ -207,10 +213,8 @@
fun testGlyphCallback_Empty() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
- val interp = TextInterpolator(layout, typefaceCache).apply {
- glyphFilter = { glyph, progress ->
- }
- }
+ val interp =
+ TextInterpolator(layout, typefaceCache).apply { glyphFilter = { glyph, progress -> } }
interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
@@ -219,7 +223,8 @@
// Just after created TextInterpolator, it should have 0 progress.
val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
- val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
+ val expected =
+ makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
.toBitmap(BMP_WIDTH, BMP_HEIGHT)
assertThat(expected.sameAs(actual)).isTrue()
@@ -229,11 +234,10 @@
fun testGlyphCallback_Xcoordinate() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
- val interp = TextInterpolator(layout, typefaceCache).apply {
- glyphFilter = { glyph, progress ->
- glyph.x += 30f
+ val interp =
+ TextInterpolator(layout, typefaceCache).apply {
+ glyphFilter = { glyph, progress -> glyph.x += 30f }
}
- }
interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
@@ -242,7 +246,8 @@
// Just after created TextInterpolator, it should have 0 progress.
val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
- val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
+ val expected =
+ makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
.toBitmap(BMP_WIDTH, BMP_HEIGHT)
// The glyph position was modified by callback, so the bitmap should not be the same.
@@ -254,11 +259,10 @@
fun testGlyphCallback_Ycoordinate() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
- val interp = TextInterpolator(layout, typefaceCache).apply {
- glyphFilter = { glyph, progress ->
- glyph.y += 30f
+ val interp =
+ TextInterpolator(layout, typefaceCache).apply {
+ glyphFilter = { glyph, progress -> glyph.y += 30f }
}
- }
interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
@@ -267,7 +271,8 @@
// Just after created TextInterpolator, it should have 0 progress.
val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
- val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
+ val expected =
+ makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
.toBitmap(BMP_WIDTH, BMP_HEIGHT)
// The glyph position was modified by callback, so the bitmap should not be the same.
@@ -279,11 +284,10 @@
fun testGlyphCallback_TextSize() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
- val interp = TextInterpolator(layout, typefaceCache).apply {
- glyphFilter = { glyph, progress ->
- glyph.textSize += 10f
+ val interp =
+ TextInterpolator(layout, typefaceCache).apply {
+ glyphFilter = { glyph, progress -> glyph.textSize += 10f }
}
- }
interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
@@ -292,7 +296,8 @@
// Just after created TextInterpolator, it should have 0 progress.
val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
- val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
+ val expected =
+ makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
.toBitmap(BMP_WIDTH, BMP_HEIGHT)
// The glyph position was modified by callback, so the bitmap should not be the same.
@@ -304,11 +309,10 @@
fun testGlyphCallback_Color() {
val layout = makeLayout(BIDI_TEXT, PAINT, TextDirectionHeuristics.RTL)
- val interp = TextInterpolator(layout, typefaceCache).apply {
- glyphFilter = { glyph, progress ->
- glyph.color = Color.RED
+ val interp =
+ TextInterpolator(layout, typefaceCache).apply {
+ glyphFilter = { glyph, progress -> glyph.color = Color.RED }
}
- }
interp.basePaint.set(START_PAINT)
interp.onBasePaintModified()
@@ -317,7 +321,8 @@
// Just after created TextInterpolator, it should have 0 progress.
val actual = interp.toBitmap(BMP_WIDTH, BMP_HEIGHT)
- val expected = makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
+ val expected =
+ makeLayout(BIDI_TEXT, START_PAINT, TextDirectionHeuristics.RTL)
.toBitmap(BMP_WIDTH, BMP_HEIGHT)
// The glyph position was modified by callback, so the bitmap should not be the same.
@@ -327,7 +332,7 @@
}
private fun Layout.toBitmap(width: Int, height: Int) =
- Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }!!
+ Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }!!
private fun TextInterpolator.toBitmap(width: Int, height: Int) =
- Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }
+ Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { draw(Canvas(it)) }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt
index 2812bd3..b3a5872 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shared/clocks/view/SimpleDigitalClockTextViewTest.kt
@@ -49,7 +49,7 @@
context,
context.resources,
ClockSettings(),
- TypefaceCache(messageBuffer) {
+ TypefaceCache(messageBuffer, 20) {
// TODO(b/364680873): Move constant to config_clockFontFamily when shipping
return@TypefaceCache Typeface.create(
"google-sans-flex-clock",