Loading packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt +4 −2 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.util.MathUtils import androidx.annotation.VisibleForTesting import java.lang.Float.max import java.lang.Float.min import kotlin.math.roundToInt private const val TAG_WGHT = "wght" private const val TAG_ITAL = "ital" Loading Loading @@ -89,7 +90,7 @@ class FontCacheImpl(override val animationFrameCount: Int = DEFAULT_FONT_CACHE_M /** 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 { fun lerp(start: Font, end: Font, progress: Float, linearProgress: Float): Font { if (progress == 0f) { return start } else if (progress == 1f) { Loading @@ -105,7 +106,8 @@ class FontInterpolator(val fontCache: FontCache = FontCacheImpl()) { // Check we already know the result. This is commonly happens since we draws the different // text chunks with the same font. val iKey = InterpKey(start, end, (progress * fontCache.animationFrameCount).toInt()) val iKey = InterpKey(start, end, (linearProgress * fontCache.animationFrameCount).roundToInt()) fontCache.get(iKey)?.let { if (DEBUG) { Log.d(LOG_TAG, "[$progress] Interp. cache hit for $iKey") Loading packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt +84 −58 Original line number Diff line number Diff line Loading @@ -25,9 +25,8 @@ import android.graphics.Typeface import android.graphics.fonts.Font import android.graphics.fonts.FontVariationAxis import android.text.Layout import android.util.LruCache import kotlin.math.roundToInt import android.util.Log import android.util.LruCache private const val DEFAULT_ANIMATION_DURATION: Long = 300 private const val TYPEFACE_CACHE_MAX_ENTRIES = 5 Loading @@ -37,6 +36,7 @@ typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit interface TypefaceVariantCache { val fontCache: FontCache val animationFrameCount: Int fun getTypefaceForVariant(fvar: String?): Typeface? companion object { Loading @@ -45,24 +45,25 @@ interface TypefaceVariantCache { return baseTypeface } val axes = FontVariationAxis.fromFontVariationSettings(fVar) ?.toMutableList() val axes = FontVariationAxis.fromFontVariationSettings(fVar)?.toMutableList() ?: mutableListOf() axes.removeIf { !baseTypeface.isSupportedAxes(it.getOpenTypeTagValue()) } if (axes.isEmpty()) { return baseTypeface } } else { return Typeface.createFromTypefaceWithVariation(baseTypeface, axes) } } } } class TypefaceVariantCacheImpl( var baseTypeface: Typeface, override val animationFrameCount: Int, ) : TypefaceVariantCache { 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 Loading Loading @@ -113,29 +114,20 @@ class TextAnimator( ValueAnimator.ofFloat(1f).apply { duration = DEFAULT_ANIMATION_DURATION addUpdateListener { textInterpolator.progress = calculateProgress(it.animatedValue as Float, typefaceCache.animationFrameCount) textInterpolator.progress = it.animatedValue as Float textInterpolator.linearProgress = it.currentPlayTime.toFloat() / it.duration.toFloat() invalidateCallback() } addListener( object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) = textInterpolator.rebase() override fun onAnimationCancel(animation: Animator) = textInterpolator.rebase() } ) } private fun calculateProgress(animProgress: Float, numberOfAnimationSteps: Int?): Float { if (numberOfAnimationSteps != null) { // This clamps the progress to the nearest value of "numberOfAnimationSteps" // discrete values between 0 and 1f. return (animProgress * numberOfAnimationSteps).roundToInt() / numberOfAnimationSteps.toFloat() } return animProgress } sealed class PositionedGlyph { /** Mutable X coordinate of the glyph position relative from drawing offset. */ var x: Float = 0f Loading Loading @@ -209,12 +201,14 @@ class TextAnimator( * * Here is an example of font runs: "fin. 終わり" * * ``` * Characters : f i n . _ 終 わ り * Code Points: \u0066 \u0069 \u006E \u002E \u0020 \u7D42 \u308F \u308A * Font Runs : <-- Roboto-Regular.ttf --><-- NotoSans-CJK.otf --> * runStart = 0, runLength = 5 runStart = 5, runLength = 3 * Glyph IDs : 194 48 7 8 4367 1039 1002 * Glyph Index: 0 1 2 3 0 1 2 * ``` * * In this example, the "fi" is converted into ligature form, thus the single glyph ID is * assigned for two characters, f and i. Loading Loading @@ -246,15 +240,17 @@ class TextAnimator( /** * Set text style with animation. * * ``` * By passing -1 to weight, the view preserve the current weight. * By passing -1 to textSize, the view preserve the current text size. * Bu passing -1 to duration, the default text animation, 1000ms, is used. * By passing -1 to duration, the default text animation, 1000ms, is used. * By passing false to animate, the text will be updated without animation. * ``` * * @param fvar an optional text fontVariationSettings. * @param textSize an optional font size. * @param colors an optional colors array that must be the same size as numLines passed to * the TextInterpolator * @param colors an optional colors array that must be the same size as numLines passed to the * TextInterpolator * @param strokeWidth an optional paint stroke width * @param animate an optional boolean indicating true for showing style transition as animation, * false for immediate style transition. True by default. Loading @@ -273,8 +269,20 @@ class TextAnimator( interpolator: TimeInterpolator? = null, delay: Long = 0, onAnimationEnd: Runnable? = null, ) = setTextStyleInternal(fvar, textSize, color, strokeWidth, animate, duration, interpolator, delay, onAnimationEnd, updateLayoutOnFailure = true) ) { setTextStyleInternal( fvar, textSize, color, strokeWidth, animate, duration, interpolator, delay, onAnimationEnd, updateLayoutOnFailure = true, ) } private fun setTextStyleInternal( fvar: String?, Loading Loading @@ -310,24 +318,21 @@ class TextAnimator( if (animate) { animator.startDelay = delay animator.duration = if (duration == -1L) { DEFAULT_ANIMATION_DURATION } else { duration } animator.duration = if (duration == -1L) DEFAULT_ANIMATION_DURATION else duration interpolator?.let { animator.interpolator = it } if (onAnimationEnd != null) { val listener = object : AnimatorListenerAdapter() { animator.addListener( object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { onAnimationEnd.run() animator.removeListener(this) } override fun onAnimationCancel(animation: Animator) { animator.removeListener(this) } } animator.addListener(listener) ) } animator.start() } else { Loading @@ -338,11 +343,26 @@ class TextAnimator( } } catch (ex: IllegalArgumentException) { if (updateLayoutOnFailure) { Log.e(TAG, "setTextStyleInternal: Exception caught but retrying. This is usually" + " due to the layout having changed unexpectedly without being notified.", ex) Log.e( TAG, "setTextStyleInternal: Exception caught but retrying. This is usually" + " due to the layout having changed unexpectedly without being notified.", ex, ) updateLayout(textInterpolator.layout) setTextStyleInternal(fvar, textSize, color, strokeWidth, animate, duration, interpolator, delay, onAnimationEnd, updateLayoutOnFailure = false) setTextStyleInternal( fvar, textSize, color, strokeWidth, animate, duration, interpolator, delay, onAnimationEnd, updateLayoutOnFailure = false, ) } else { throw ex } Loading @@ -351,6 +371,8 @@ class TextAnimator( /** * Set text style with animation. Similar as * * ``` * fun setTextStyle( * fvar: String? = "", * textSize: Float = -1f, Loading @@ -362,6 +384,7 @@ class TextAnimator( * delay: Long = 0, * onAnimationEnd: Runnable? = null * ) * ``` * * @param weight an optional style value for `wght` in fontVariationSettings. * @param width an optional style value for `wdth` in fontVariationSettings. Loading @@ -380,9 +403,11 @@ class TextAnimator( duration: Long = -1L, interpolator: TimeInterpolator? = null, delay: Long = 0, onAnimationEnd: Runnable? = null ) = setTextStyleInternal( fvar = fontVariationUtils.updateFontVariation( onAnimationEnd: Runnable? = null, ) { setTextStyleInternal( fvar = fontVariationUtils.updateFontVariation( weight = weight, width = width, opticalSize = opticalSize, Loading @@ -398,6 +423,7 @@ class TextAnimator( onAnimationEnd = onAnimationEnd, updateLayoutOnFailure = true, ) } companion object { private val TAG = TextAnimator::class.simpleName!! Loading packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt +10 −2 Original line number Diff line number Diff line Loading @@ -98,6 +98,9 @@ class TextInterpolator(layout: Layout, var typefaceCache: TypefaceVariantCache) */ var progress: Float = 0f /** Linear progress value (not interpolated) */ var linearProgress: Float = 0f /** * The layout used for drawing text. * Loading Loading @@ -217,7 +220,12 @@ class TextInterpolator(layout: Layout, var typefaceCache: TypefaceVariantCache) } run.fontRuns.forEach { fontRun -> fontRun.baseFont = fontInterpolator.lerp(fontRun.baseFont, fontRun.targetFont, progress) fontInterpolator.lerp( fontRun.baseFont, fontRun.targetFont, progress, linearProgress, ) val fvar = FontVariationAxis.toFontVariationSettings(fontRun.baseFont.axes) basePaint.typeface = typefaceCache.getTypefaceForVariant(fvar) } Loading Loading @@ -358,7 +366,7 @@ class TextInterpolator(layout: Layout, var typefaceCache: TypefaceVariantCache) // Draws single font run. private fun drawFontRun(c: Canvas, line: Run, run: FontRun, lineNo: Int, paint: Paint) { var arrayIndex = 0 val font = fontInterpolator.lerp(run.baseFont, run.targetFont, progress) val font = fontInterpolator.lerp(run.baseFont, run.targetFont, progress, linearProgress) val glyphFilter = glyphFilter if (glyphFilter == null) { Loading packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt +2 −1 Original line number Diff line number Diff line Loading @@ -109,7 +109,8 @@ class DefaultClockProvider( companion object { // 750ms @ 120hz -> 90 frames of animation const val NUM_CLOCK_FONT_ANIMATION_STEPS = 90 // In practice, 45 looks good enough const val NUM_CLOCK_FONT_ANIMATION_STEPS = 45 val FLEX_TYPEFACE by lazy { // TODO(b/364680873): Move constant to config_clockFontFamily when shipping Loading packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt +14 −11 Original line number Diff line number Diff line Loading @@ -58,9 +58,12 @@ class FontInterpolatorTest : SysuiTestCase() { Font.Builder(sFont).setFontVariationSettings("'wght' 900, 'ital' 1, 'GRAD' 700").build() val interp = FontInterpolator() assertSameAxes(startFont, interp.lerp(startFont, endFont, 0f)) assertSameAxes(endFont, interp.lerp(startFont, endFont, 1f)) assertSameAxes("'wght' 500, 'ital' 0.5, 'GRAD' 450", interp.lerp(startFont, endFont, 0.5f)) assertSameAxes(startFont, interp.lerp(startFont, endFont, 0f, 0f)) assertSameAxes(endFont, interp.lerp(startFont, endFont, 1f, 1f)) assertSameAxes( "'wght' 500, 'ital' 0.5, 'GRAD' 450", interp.lerp(startFont, endFont, 0.5f, 0.5f), ) } @Test Loading @@ -69,7 +72,7 @@ class FontInterpolatorTest : SysuiTestCase() { val endFont = Font.Builder(sFont).setFontVariationSettings("'ital' 1").build() val interp = FontInterpolator() assertSameAxes("'wght' 250, 'ital' 0.5", interp.lerp(startFont, endFont, 0.5f)) assertSameAxes("'wght' 250, 'ital' 0.5", interp.lerp(startFont, endFont, 0.5f, 0.5f)) } @Test Loading @@ -78,8 +81,8 @@ class FontInterpolatorTest : SysuiTestCase() { val endFont = Font.Builder(sFont).setFontVariationSettings("'ital' 1").build() val interp = FontInterpolator() val resultFont = interp.lerp(startFont, endFont, 0.5f) val cachedFont = interp.lerp(startFont, endFont, 0.5f) val resultFont = interp.lerp(startFont, endFont, 0.5f, 0.5f) val cachedFont = interp.lerp(startFont, endFont, 0.5f, 0.5f) assertThat(resultFont).isSameInstanceAs(cachedFont) } Loading @@ -89,8 +92,8 @@ class FontInterpolatorTest : SysuiTestCase() { val endFont = Font.Builder(sFont).setFontVariationSettings("'ital' 1").build() val interp = FontInterpolator() val resultFont = interp.lerp(startFont, endFont, 0.5f) val reversedFont = interp.lerp(endFont, startFont, 0.5f) val resultFont = interp.lerp(startFont, endFont, 0.5f, 0.5f) val reversedFont = interp.lerp(endFont, startFont, 0.5f, 0.5f) assertThat(resultFont).isSameInstanceAs(reversedFont) } Loading @@ -100,14 +103,14 @@ class FontInterpolatorTest : SysuiTestCase() { 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) val resultFont = interp.lerp(startFont, endFont, 0.5f, 0.5f) 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) interp.lerp(f1, f2, 0.5f, 0.5f) } val cachedFont = interp.lerp(startFont, endFont, 0.5f) val cachedFont = interp.lerp(startFont, endFont, 0.5f, 0.5f) assertThat(resultFont).isNotSameInstanceAs(cachedFont) } } Loading
packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt +4 −2 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.util.MathUtils import androidx.annotation.VisibleForTesting import java.lang.Float.max import java.lang.Float.min import kotlin.math.roundToInt private const val TAG_WGHT = "wght" private const val TAG_ITAL = "ital" Loading Loading @@ -89,7 +90,7 @@ class FontCacheImpl(override val animationFrameCount: Int = DEFAULT_FONT_CACHE_M /** 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 { fun lerp(start: Font, end: Font, progress: Float, linearProgress: Float): Font { if (progress == 0f) { return start } else if (progress == 1f) { Loading @@ -105,7 +106,8 @@ class FontInterpolator(val fontCache: FontCache = FontCacheImpl()) { // Check we already know the result. This is commonly happens since we draws the different // text chunks with the same font. val iKey = InterpKey(start, end, (progress * fontCache.animationFrameCount).toInt()) val iKey = InterpKey(start, end, (linearProgress * fontCache.animationFrameCount).roundToInt()) fontCache.get(iKey)?.let { if (DEBUG) { Log.d(LOG_TAG, "[$progress] Interp. cache hit for $iKey") Loading
packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt +84 −58 Original line number Diff line number Diff line Loading @@ -25,9 +25,8 @@ import android.graphics.Typeface import android.graphics.fonts.Font import android.graphics.fonts.FontVariationAxis import android.text.Layout import android.util.LruCache import kotlin.math.roundToInt import android.util.Log import android.util.LruCache private const val DEFAULT_ANIMATION_DURATION: Long = 300 private const val TYPEFACE_CACHE_MAX_ENTRIES = 5 Loading @@ -37,6 +36,7 @@ typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit interface TypefaceVariantCache { val fontCache: FontCache val animationFrameCount: Int fun getTypefaceForVariant(fvar: String?): Typeface? companion object { Loading @@ -45,24 +45,25 @@ interface TypefaceVariantCache { return baseTypeface } val axes = FontVariationAxis.fromFontVariationSettings(fVar) ?.toMutableList() val axes = FontVariationAxis.fromFontVariationSettings(fVar)?.toMutableList() ?: mutableListOf() axes.removeIf { !baseTypeface.isSupportedAxes(it.getOpenTypeTagValue()) } if (axes.isEmpty()) { return baseTypeface } } else { return Typeface.createFromTypefaceWithVariation(baseTypeface, axes) } } } } class TypefaceVariantCacheImpl( var baseTypeface: Typeface, override val animationFrameCount: Int, ) : TypefaceVariantCache { 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 Loading Loading @@ -113,29 +114,20 @@ class TextAnimator( ValueAnimator.ofFloat(1f).apply { duration = DEFAULT_ANIMATION_DURATION addUpdateListener { textInterpolator.progress = calculateProgress(it.animatedValue as Float, typefaceCache.animationFrameCount) textInterpolator.progress = it.animatedValue as Float textInterpolator.linearProgress = it.currentPlayTime.toFloat() / it.duration.toFloat() invalidateCallback() } addListener( object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) = textInterpolator.rebase() override fun onAnimationCancel(animation: Animator) = textInterpolator.rebase() } ) } private fun calculateProgress(animProgress: Float, numberOfAnimationSteps: Int?): Float { if (numberOfAnimationSteps != null) { // This clamps the progress to the nearest value of "numberOfAnimationSteps" // discrete values between 0 and 1f. return (animProgress * numberOfAnimationSteps).roundToInt() / numberOfAnimationSteps.toFloat() } return animProgress } sealed class PositionedGlyph { /** Mutable X coordinate of the glyph position relative from drawing offset. */ var x: Float = 0f Loading Loading @@ -209,12 +201,14 @@ class TextAnimator( * * Here is an example of font runs: "fin. 終わり" * * ``` * Characters : f i n . _ 終 わ り * Code Points: \u0066 \u0069 \u006E \u002E \u0020 \u7D42 \u308F \u308A * Font Runs : <-- Roboto-Regular.ttf --><-- NotoSans-CJK.otf --> * runStart = 0, runLength = 5 runStart = 5, runLength = 3 * Glyph IDs : 194 48 7 8 4367 1039 1002 * Glyph Index: 0 1 2 3 0 1 2 * ``` * * In this example, the "fi" is converted into ligature form, thus the single glyph ID is * assigned for two characters, f and i. Loading Loading @@ -246,15 +240,17 @@ class TextAnimator( /** * Set text style with animation. * * ``` * By passing -1 to weight, the view preserve the current weight. * By passing -1 to textSize, the view preserve the current text size. * Bu passing -1 to duration, the default text animation, 1000ms, is used. * By passing -1 to duration, the default text animation, 1000ms, is used. * By passing false to animate, the text will be updated without animation. * ``` * * @param fvar an optional text fontVariationSettings. * @param textSize an optional font size. * @param colors an optional colors array that must be the same size as numLines passed to * the TextInterpolator * @param colors an optional colors array that must be the same size as numLines passed to the * TextInterpolator * @param strokeWidth an optional paint stroke width * @param animate an optional boolean indicating true for showing style transition as animation, * false for immediate style transition. True by default. Loading @@ -273,8 +269,20 @@ class TextAnimator( interpolator: TimeInterpolator? = null, delay: Long = 0, onAnimationEnd: Runnable? = null, ) = setTextStyleInternal(fvar, textSize, color, strokeWidth, animate, duration, interpolator, delay, onAnimationEnd, updateLayoutOnFailure = true) ) { setTextStyleInternal( fvar, textSize, color, strokeWidth, animate, duration, interpolator, delay, onAnimationEnd, updateLayoutOnFailure = true, ) } private fun setTextStyleInternal( fvar: String?, Loading Loading @@ -310,24 +318,21 @@ class TextAnimator( if (animate) { animator.startDelay = delay animator.duration = if (duration == -1L) { DEFAULT_ANIMATION_DURATION } else { duration } animator.duration = if (duration == -1L) DEFAULT_ANIMATION_DURATION else duration interpolator?.let { animator.interpolator = it } if (onAnimationEnd != null) { val listener = object : AnimatorListenerAdapter() { animator.addListener( object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { onAnimationEnd.run() animator.removeListener(this) } override fun onAnimationCancel(animation: Animator) { animator.removeListener(this) } } animator.addListener(listener) ) } animator.start() } else { Loading @@ -338,11 +343,26 @@ class TextAnimator( } } catch (ex: IllegalArgumentException) { if (updateLayoutOnFailure) { Log.e(TAG, "setTextStyleInternal: Exception caught but retrying. This is usually" + " due to the layout having changed unexpectedly without being notified.", ex) Log.e( TAG, "setTextStyleInternal: Exception caught but retrying. This is usually" + " due to the layout having changed unexpectedly without being notified.", ex, ) updateLayout(textInterpolator.layout) setTextStyleInternal(fvar, textSize, color, strokeWidth, animate, duration, interpolator, delay, onAnimationEnd, updateLayoutOnFailure = false) setTextStyleInternal( fvar, textSize, color, strokeWidth, animate, duration, interpolator, delay, onAnimationEnd, updateLayoutOnFailure = false, ) } else { throw ex } Loading @@ -351,6 +371,8 @@ class TextAnimator( /** * Set text style with animation. Similar as * * ``` * fun setTextStyle( * fvar: String? = "", * textSize: Float = -1f, Loading @@ -362,6 +384,7 @@ class TextAnimator( * delay: Long = 0, * onAnimationEnd: Runnable? = null * ) * ``` * * @param weight an optional style value for `wght` in fontVariationSettings. * @param width an optional style value for `wdth` in fontVariationSettings. Loading @@ -380,9 +403,11 @@ class TextAnimator( duration: Long = -1L, interpolator: TimeInterpolator? = null, delay: Long = 0, onAnimationEnd: Runnable? = null ) = setTextStyleInternal( fvar = fontVariationUtils.updateFontVariation( onAnimationEnd: Runnable? = null, ) { setTextStyleInternal( fvar = fontVariationUtils.updateFontVariation( weight = weight, width = width, opticalSize = opticalSize, Loading @@ -398,6 +423,7 @@ class TextAnimator( onAnimationEnd = onAnimationEnd, updateLayoutOnFailure = true, ) } companion object { private val TAG = TextAnimator::class.simpleName!! Loading
packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt +10 −2 Original line number Diff line number Diff line Loading @@ -98,6 +98,9 @@ class TextInterpolator(layout: Layout, var typefaceCache: TypefaceVariantCache) */ var progress: Float = 0f /** Linear progress value (not interpolated) */ var linearProgress: Float = 0f /** * The layout used for drawing text. * Loading Loading @@ -217,7 +220,12 @@ class TextInterpolator(layout: Layout, var typefaceCache: TypefaceVariantCache) } run.fontRuns.forEach { fontRun -> fontRun.baseFont = fontInterpolator.lerp(fontRun.baseFont, fontRun.targetFont, progress) fontInterpolator.lerp( fontRun.baseFont, fontRun.targetFont, progress, linearProgress, ) val fvar = FontVariationAxis.toFontVariationSettings(fontRun.baseFont.axes) basePaint.typeface = typefaceCache.getTypefaceForVariant(fvar) } Loading Loading @@ -358,7 +366,7 @@ class TextInterpolator(layout: Layout, var typefaceCache: TypefaceVariantCache) // Draws single font run. private fun drawFontRun(c: Canvas, line: Run, run: FontRun, lineNo: Int, paint: Paint) { var arrayIndex = 0 val font = fontInterpolator.lerp(run.baseFont, run.targetFont, progress) val font = fontInterpolator.lerp(run.baseFont, run.targetFont, progress, linearProgress) val glyphFilter = glyphFilter if (glyphFilter == null) { Loading
packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockProvider.kt +2 −1 Original line number Diff line number Diff line Loading @@ -109,7 +109,8 @@ class DefaultClockProvider( companion object { // 750ms @ 120hz -> 90 frames of animation const val NUM_CLOCK_FONT_ANIMATION_STEPS = 90 // In practice, 45 looks good enough const val NUM_CLOCK_FONT_ANIMATION_STEPS = 45 val FLEX_TYPEFACE by lazy { // TODO(b/364680873): Move constant to config_clockFontFamily when shipping Loading
packages/SystemUI/tests/src/com/android/systemui/animation/FontInterpolatorTest.kt +14 −11 Original line number Diff line number Diff line Loading @@ -58,9 +58,12 @@ class FontInterpolatorTest : SysuiTestCase() { Font.Builder(sFont).setFontVariationSettings("'wght' 900, 'ital' 1, 'GRAD' 700").build() val interp = FontInterpolator() assertSameAxes(startFont, interp.lerp(startFont, endFont, 0f)) assertSameAxes(endFont, interp.lerp(startFont, endFont, 1f)) assertSameAxes("'wght' 500, 'ital' 0.5, 'GRAD' 450", interp.lerp(startFont, endFont, 0.5f)) assertSameAxes(startFont, interp.lerp(startFont, endFont, 0f, 0f)) assertSameAxes(endFont, interp.lerp(startFont, endFont, 1f, 1f)) assertSameAxes( "'wght' 500, 'ital' 0.5, 'GRAD' 450", interp.lerp(startFont, endFont, 0.5f, 0.5f), ) } @Test Loading @@ -69,7 +72,7 @@ class FontInterpolatorTest : SysuiTestCase() { val endFont = Font.Builder(sFont).setFontVariationSettings("'ital' 1").build() val interp = FontInterpolator() assertSameAxes("'wght' 250, 'ital' 0.5", interp.lerp(startFont, endFont, 0.5f)) assertSameAxes("'wght' 250, 'ital' 0.5", interp.lerp(startFont, endFont, 0.5f, 0.5f)) } @Test Loading @@ -78,8 +81,8 @@ class FontInterpolatorTest : SysuiTestCase() { val endFont = Font.Builder(sFont).setFontVariationSettings("'ital' 1").build() val interp = FontInterpolator() val resultFont = interp.lerp(startFont, endFont, 0.5f) val cachedFont = interp.lerp(startFont, endFont, 0.5f) val resultFont = interp.lerp(startFont, endFont, 0.5f, 0.5f) val cachedFont = interp.lerp(startFont, endFont, 0.5f, 0.5f) assertThat(resultFont).isSameInstanceAs(cachedFont) } Loading @@ -89,8 +92,8 @@ class FontInterpolatorTest : SysuiTestCase() { val endFont = Font.Builder(sFont).setFontVariationSettings("'ital' 1").build() val interp = FontInterpolator() val resultFont = interp.lerp(startFont, endFont, 0.5f) val reversedFont = interp.lerp(endFont, startFont, 0.5f) val resultFont = interp.lerp(startFont, endFont, 0.5f, 0.5f) val reversedFont = interp.lerp(endFont, startFont, 0.5f, 0.5f) assertThat(resultFont).isSameInstanceAs(reversedFont) } Loading @@ -100,14 +103,14 @@ class FontInterpolatorTest : SysuiTestCase() { 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) val resultFont = interp.lerp(startFont, endFont, 0.5f, 0.5f) 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) interp.lerp(f1, f2, 0.5f, 0.5f) } val cachedFont = interp.lerp(startFont, endFont, 0.5f) val cachedFont = interp.lerp(startFont, endFont, 0.5f, 0.5f) assertThat(resultFont).isNotSameInstanceAs(cachedFont) } }