diff --git a/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt b/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt index cdb39ef216bcf..5e874ae6f44eb 100644 --- a/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt +++ b/packages/SystemUI/src/com/android/keyguard/TextAnimator.kt @@ -21,7 +21,9 @@ import android.animation.AnimatorListenerAdapter import android.animation.TimeInterpolator import android.animation.ValueAnimator import android.graphics.Canvas +import android.graphics.Typeface import android.text.Layout +import android.util.SparseArray private const val TAG_WGHT = "wght" private const val DEFAULT_ANIMATION_DURATION: Long = 300 @@ -72,6 +74,8 @@ class TextAnimator( }) } + private val typefaceCache = SparseArray() + fun updateLayout(layout: Layout) { textInterpolator.layout = layout } @@ -120,7 +124,12 @@ class TextAnimator( textInterpolator.targetPaint.textSize = textSize } if (weight >= 0) { - textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight" + // Paint#setFontVariationSettings creates Typeface instance from scratch. To reduce the + // memory impact, cache the typeface result. + textInterpolator.targetPaint.typeface = typefaceCache.getOrElse(weight) { + textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight" + textInterpolator.targetPaint.typeface + } } if (color != null) { textInterpolator.targetPaint.color = color @@ -155,3 +164,12 @@ class TextAnimator( } } } + +private fun SparseArray.getOrElse(key: Int, defaultValue: () -> V): V { + var v = get(key) + if (v == null) { + v = defaultValue() + put(key, v) + } + return v +} \ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt index ad7f0cbe1e3ad..3322414e9e7ad 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/TextAnimatorTest.kt @@ -18,12 +18,14 @@ package com.android.keyguard import android.animation.AnimatorListenerAdapter import android.animation.ValueAnimator +import android.graphics.Typeface import android.testing.AndroidTestingRunner import android.text.Layout import android.text.StaticLayout import android.text.TextPaint import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor @@ -140,4 +142,41 @@ class TextAnimatorTest : SysuiTestCase() { verify(animationEndCallback).run() verify(valueAnimator).removeListener(eq(captor.value)) } + + @Test + fun testCacheTypeface() { + 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") + } + `when`(textInterpolator.targetPaint).thenReturn(paint) + + val textAnimator = TextAnimator(layout, {}).apply { + this.textInterpolator = textInterpolator + this.animator = valueAnimator + } + + textAnimator.setTextStyle( + weight = 400, + animate = true + ) + + val prevTypeface = paint.typeface + + textAnimator.setTextStyle( + weight = 700, + animate = true + ) + + assertThat(paint.typeface).isNotSameInstanceAs(prevTypeface) + + textAnimator.setTextStyle( + weight = 400, + animate = true + ) + + assertThat(paint.typeface).isSameInstanceAs(prevTypeface) + } }