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",