Loading packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt +56 −50 Original line number Diff line number Diff line Loading @@ -33,9 +33,7 @@ private const val FONT_ITALIC_MIN = 0f private const val FONT_ITALIC_ANIMATION_STEP = 0.1f private const val FONT_ITALIC_DEFAULT_VALUE = 0f /** * Provide interpolation of two fonts by adjusting font variation settings. */ /** Provide interpolation of two fonts by adjusting font variation settings. */ class FontInterpolator { /** Loading @@ -61,8 +59,11 @@ class FontInterpolator { var index: Int, val sortedAxes: MutableList<FontVariationAxis> ) { constructor(font: Font, axes: List<FontVariationAxis>) : this(font.sourceIdentifier, constructor( font: Font, axes: List<FontVariationAxis> ) : this( font.sourceIdentifier, font.ttcIndex, axes.toMutableList().apply { sortBy { it.tag } } ) Loading @@ -86,9 +87,7 @@ class FontInterpolator { private val tmpInterpKey = InterpKey(null, null, 0f) private val tmpVarFontKey = VarFontKey(0, 0, mutableListOf()) /** * Linear interpolate the font variation settings. */ /** Linear interpolate the font variation settings. */ fun lerp(start: Font, end: Font, progress: Float): Font { if (progress == 0f) { return start Loading @@ -115,19 +114,26 @@ class FontInterpolator { // this doesn't take much time since the variation axes is usually up to 5. If we need to // support more number of axes, we may want to preprocess the font and store the sorted axes // and also pre-fill the missing axes value with default value from 'fvar' table. val newAxes = lerp(startAxes, endAxes) { tag, startValue, endValue -> val newAxes = lerp(startAxes, endAxes) { tag, startValue, endValue -> when (tag) { // TODO: Good to parse 'fvar' table for retrieving default value. TAG_WGHT -> adjustWeight( TAG_WGHT -> adjustWeight( MathUtils.lerp( startValue ?: FONT_WEIGHT_DEFAULT_VALUE, endValue ?: FONT_WEIGHT_DEFAULT_VALUE, progress)) TAG_ITAL -> adjustItalic( progress ) ) TAG_ITAL -> adjustItalic( MathUtils.lerp( startValue ?: FONT_ITALIC_DEFAULT_VALUE, endValue ?: FONT_ITALIC_DEFAULT_VALUE, progress)) progress ) ) else -> { require(startValue != null && endValue != null) { "Unable to interpolate due to unknown default axes value : $tag" Loading @@ -149,9 +155,7 @@ class FontInterpolator { // 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() val newFont = Font.Builder(start).setFontVariationSettings(newAxes.toTypedArray()).build() interpCache[InterpKey(start, end, progress)] = newFont verFontCache[VarFontKey(start, newAxes)] = newFont return newFont Loading @@ -173,13 +177,15 @@ class FontInterpolator { val tagA = if (i < start.size) start[i].tag else null val tagB = if (j < end.size) end[j].tag else null val comp = when { val comp = when { tagA == null -> 1 tagB == null -> -1 else -> tagA.compareTo(tagB) } val axis = when { val axis = when { comp == 0 -> { val v = filter(tagA!!, start[i++].styleValue, end[j++].styleValue) FontVariationAxis(tagA, v) Loading packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt +65 −82 Original line number Diff line number Diff line Loading @@ -36,8 +36,8 @@ typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit * Currently this class can provide text style animation for text weight and text size. For example * the simple view that draws text with animating text size is like as follows: * * <pre> * <code> * <pre> <code> * ``` * class SimpleTextAnimation : View { * @JvmOverloads constructor(...) * Loading @@ -53,83 +53,63 @@ typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit * animator.setTextStyle(-1 /* unchanged weight */, sizePx, animate) * } * } * </code> * </pre> * ``` * </code> </pre> */ class TextAnimator( layout: Layout, private val invalidateCallback: () -> Unit ) { class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) { // Following two members are for mutable for testing purposes. public var textInterpolator: TextInterpolator = TextInterpolator(layout) public var animator: ValueAnimator = ValueAnimator.ofFloat(1f).apply { public var animator: ValueAnimator = ValueAnimator.ofFloat(1f).apply { duration = DEFAULT_ANIMATION_DURATION addUpdateListener { textInterpolator.progress = it.animatedValue as Float invalidateCallback() } addListener(object : AnimatorListenerAdapter() { addListener( object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator?) { textInterpolator.rebase() } override fun onAnimationCancel(animation: Animator?) = textInterpolator.rebase() }) } ) } sealed class PositionedGlyph { /** * Mutable X coordinate of the glyph position relative from drawing offset. */ /** Mutable X coordinate of the glyph position relative from drawing offset. */ var x: Float = 0f /** * Mutable Y coordinate of the glyph position relative from the baseline. */ /** Mutable Y coordinate of the glyph position relative from the baseline. */ var y: Float = 0f /** * The current line of text being drawn, in a multi-line TextView. */ /** The current line of text being drawn, in a multi-line TextView. */ var lineNo: Int = 0 /** * Mutable text size of the glyph in pixels. */ /** Mutable text size of the glyph in pixels. */ var textSize: Float = 0f /** * Mutable color of the glyph. */ /** Mutable color of the glyph. */ var color: Int = 0 /** * Immutable character offset in the text that the current font run start. */ /** Immutable character offset in the text that the current font run start. */ abstract var runStart: Int protected set /** * Immutable run length of the font run. */ /** Immutable run length of the font run. */ abstract var runLength: Int protected set /** * Immutable glyph index of the font run. */ /** Immutable glyph index of the font run. */ abstract var glyphIndex: Int protected set /** * Immutable font instance for this font run. */ /** Immutable font instance for this font run. */ abstract var font: Font protected set /** * Immutable glyph ID for this glyph. */ /** Immutable glyph ID for this glyph. */ abstract var glyphId: Int protected set } Loading @@ -147,20 +127,18 @@ class TextAnimator( /** * GlyphFilter applied just before drawing to canvas for tweaking positions and text size. * * This callback is called for each glyphs just before drawing the glyphs. This function will * be called with the intrinsic position, size, color, glyph ID and font instance. You can * mutate the position, size and color for tweaking animations. * Do not keep the reference of passed glyph object. The interpolator reuses that object for * avoiding object allocations. * This callback is called for each glyphs just before drawing the glyphs. This function will be * called with the intrinsic position, size, color, glyph ID and font instance. You can mutate * the position, size and color for tweaking animations. Do not keep the reference of passed * glyph object. The interpolator reuses that object for avoiding object allocations. * * Details: * The text is drawn with font run units. The font run is a text segment that draws with the * same font. The {@code runStart} and {@code runLimit} is a range of the font run in the text * that current glyph is in. Once the font run is determined, the system will convert characters * into glyph IDs. The {@code glyphId} is the glyph identifier in the font and * {@code glyphIndex} is the offset of the converted glyph array. Please note that the * {@code glyphIndex} is not a character index, because the character will not be converted to * glyph one-by-one. If there are ligatures including emoji sequence, etc, the glyph ID may be * Details: The text is drawn with font run units. The font run is a text segment that draws * with the same font. The {@code runStart} and {@code runLimit} is a range of the font run in * the text that current glyph is in. Once the font run is determined, the system will convert * characters into glyph IDs. The {@code glyphId} is the glyph identifier in the font and {@code * glyphIndex} is the offset of the converted glyph array. Please note that the {@code * glyphIndex} is not a character index, because the character will not be converted to glyph * one-by-one. If there are ligatures including emoji sequence, etc, the glyph ID may be * composed from multiple characters. * * Here is an example of font runs: "fin. 終わり" Loading Loading @@ -193,7 +171,9 @@ class TextAnimator( */ var glyphFilter: GlyphCallback? get() = textInterpolator.glyphFilter set(value) { textInterpolator.glyphFilter = value } set(value) { textInterpolator.glyphFilter = value } fun draw(c: Canvas) = textInterpolator.draw(c) Loading Loading @@ -237,7 +217,8 @@ class TextAnimator( if (weight >= 0) { // Paint#setFontVariationSettings creates Typeface instance from scratch. To reduce the // memory impact, cache the typeface result. textInterpolator.targetPaint.typeface = typefaceCache.getOrElse(weight) { textInterpolator.targetPaint.typeface = typefaceCache.getOrElse(weight) { textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight" textInterpolator.targetPaint.typeface } Loading @@ -249,14 +230,16 @@ class TextAnimator( if (animate) { animator.startDelay = delay animator.duration = if (duration == -1L) { animator.duration = if (duration == -1L) { DEFAULT_ANIMATION_DURATION } else { duration } interpolator?.let { animator.interpolator = it } if (onAnimationEnd != null) { val listener = object : AnimatorListenerAdapter() { val listener = object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator?) { onAnimationEnd.run() animator.removeListener(this) Loading packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt +113 −119 Original line number Diff line number Diff line Loading @@ -26,12 +26,8 @@ import android.util.MathUtils import com.android.internal.graphics.ColorUtils import java.lang.Math.max /** * Provide text style linear interpolation for plain text. */ class TextInterpolator( layout: Layout ) { /** Provide text style linear interpolation for plain text. */ class TextInterpolator(layout: Layout) { /** * Returns base paint used for interpolation. Loading Loading @@ -64,12 +60,11 @@ class TextInterpolator( var baseFont: Font, var targetFont: Font ) { val length: Int get() = end - start val length: Int get() = end - start } /** * A class represents text layout of a single run. */ /** A class represents text layout of a single run. */ private class Run( val glyphIds: IntArray, val baseX: FloatArray, // same length as glyphIds Loading @@ -79,12 +74,8 @@ class TextInterpolator( val fontRuns: List<FontRun> ) /** * A class represents text layout of a single line. */ private class Line( val runs: List<Run> ) /** 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() Loading @@ -106,8 +97,8 @@ class TextInterpolator( /** * The layout used for drawing text. * * Only non-styled text is supported. Even if the given layout is created from Spanned, the * span information is not used. * Only non-styled text is supported. Even if the given layout is created from Spanned, the span * information is not used. * * The paint objects used for interpolation are not changed by this method call. * Loading @@ -133,8 +124,8 @@ class TextInterpolator( /** * Recalculate internal text layout for interpolation. * * Whenever the target paint is modified, call this method to recalculate internal * text layout used for interpolation. * Whenever the target paint is modified, call this method to recalculate internal text layout * used for interpolation. */ fun onTargetPaintModified() { updatePositionsAndFonts(shapeText(layout, targetPaint), updateBase = false) Loading @@ -143,8 +134,8 @@ class TextInterpolator( /** * Recalculate internal text layout for interpolation. * * Whenever the base paint is modified, call this method to recalculate internal * text layout used for interpolation. * Whenever the base paint is modified, call this method to recalculate internal text layout * used for interpolation. */ fun onBasePaintModified() { updatePositionsAndFonts(shapeText(layout, basePaint), updateBase = true) Loading @@ -155,11 +146,11 @@ class TextInterpolator( * * The text interpolator does not calculate all the text position by text shaper due to * performance reasons. Instead, the text interpolator shape the start and end state and * calculate text position of the middle state by linear interpolation. Due to this trick, * the text positions of the middle state is likely different from the text shaper result. * So, if you want to start animation from the middle state, you will see the glyph jumps due to * this trick, i.e. the progress 0.5 of interpolation between weight 400 and 700 is different * from text shape result of weight 550. * calculate text position of the middle state by linear interpolation. Due to this trick, the * text positions of the middle state is likely different from the text shaper result. So, if * you want to start animation from the middle state, you will see the glyph jumps due to this * trick, i.e. the progress 0.5 of interpolation between weight 400 and 700 is different from * text shape result of weight 550. * * After calling this method, do not call onBasePaintModified() since it reshape the text and * update the base state. As in above notice, the text shaping result at current progress is Loading @@ -171,8 +162,8 @@ class TextInterpolator( * animate weight from 200 to 400, then if you want to move back to 200 at the half of the * animation, it will look like * * <pre> * <code> * <pre> <code> * ``` * val interp = TextInterpolator(layout) * * // Interpolate between weight 200 to 400. Loading Loading @@ -202,9 +193,8 @@ class TextInterpolator( * // progress is 0.5 * animator.start() * } * </code> * </pre> * * ``` * </code> </pre> */ fun rebase() { if (progress == 0f) { Loading Loading @@ -266,17 +256,20 @@ class TextInterpolator( } var maxRunLength = 0 lines = baseLayout.zip(targetLayout) { baseLine, targetLine -> val runs = baseLine.zip(targetLine) { base, target -> lines = baseLayout.zip(targetLayout) { baseLine, targetLine -> val runs = baseLine.zip(targetLine) { base, target -> require(base.glyphCount() == target.glyphCount()) { "Inconsistent glyph count at line ${lines.size}" } val glyphCount = base.glyphCount() // Good to recycle the array if the existing array can hold the new layout result. val glyphIds = IntArray(glyphCount) { // Good to recycle the array if the existing array can hold the new layout // result. val glyphIds = IntArray(glyphCount) { base.getGlyphId(it).also { baseGlyphId -> require(baseGlyphId == target.getGlyphId(it)) { "Inconsistent glyph ID at $it in line ${lines.size}" Loading Loading @@ -305,7 +298,7 @@ class TextInterpolator( if (baseFont !== nextBaseFont) { require(targetFont !== nextTargetFont) { "Base font has changed at $i but target font has not changed." "Base font has changed at $i but target font is unchanged." } // Font transition point. push run and reset context. fontRun.add(FontRun(start, i, baseFont, targetFont)) Loading @@ -314,11 +307,12 @@ class TextInterpolator( targetFont = nextTargetFont start = i require(FontInterpolator.canInterpolate(baseFont, targetFont)) { "Cannot interpolate font at $start ($baseFont vs $targetFont)" "Cannot interpolate font at $start" + " ($baseFont vs $targetFont)" } } else { // baseFont === nextBaseFont require(targetFont === nextTargetFont) { "Base font has not changed at $i but target font has changed." "Base font is unchanged at $i but target font has changed." } } } Loading Loading @@ -397,7 +391,8 @@ class TextInterpolator( 0, i - prevStart, font, tmpPaintForGlyph) tmpPaintForGlyph ) prevStart = i arrayIndex = 0 } Loading @@ -413,7 +408,8 @@ class TextInterpolator( 0, run.end - prevStart, font, tmpPaintForGlyph) tmpPaintForGlyph ) } private fun updatePositionsAndFonts( Loading @@ -421,9 +417,7 @@ class TextInterpolator( updateBase: Boolean ) { // Update target positions with newly calculated text layout. check(layoutResult.size == lines.size) { "The new layout result has different line count." } check(layoutResult.size == lines.size) { "The new layout result has different line count." } lines.zip(layoutResult) { line, runs -> line.runs.zip(runs) { lineRun, newGlyphs -> Loading Loading @@ -483,10 +477,7 @@ class TextInterpolator( } // Shape the text and stores the result to out argument. private fun shapeText( layout: Layout, paint: TextPaint ): List<List<PositionedGlyphs>> { private fun shapeText(layout: Layout, paint: TextPaint): List<List<PositionedGlyphs>> { var text = StringBuilder() val out = mutableListOf<List<PositionedGlyphs>>() for (lineNo in 0 until layout.lineCount) { // Shape all lines. Loading @@ -500,10 +491,13 @@ class TextInterpolator( } val runs = mutableListOf<PositionedGlyphs>() TextShaper.shapeText(layout.text, lineStart, count, layout.textDirectionHeuristic, paint) { _, _, glyphs, _ -> runs.add(glyphs) } TextShaper.shapeText( layout.text, lineStart, count, layout.textDirectionHeuristic, paint ) { _, _, glyphs, _ -> runs.add(glyphs) } out.add(runs) if (lineNo > 0) { Loading packages/SystemUI/ktfmt_includes.txt +0 −2 Original line number Diff line number Diff line +packages/SystemUI -packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt -packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt -packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt -packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt -packages/SystemUI/checks/src/com/android/internal/systemui/lint/BindServiceViaContextDetector.kt -packages/SystemUI/checks/src/com/android/internal/systemui/lint/BroadcastSentViaContextDetector.kt Loading Loading
packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt +56 −50 Original line number Diff line number Diff line Loading @@ -33,9 +33,7 @@ private const val FONT_ITALIC_MIN = 0f private const val FONT_ITALIC_ANIMATION_STEP = 0.1f private const val FONT_ITALIC_DEFAULT_VALUE = 0f /** * Provide interpolation of two fonts by adjusting font variation settings. */ /** Provide interpolation of two fonts by adjusting font variation settings. */ class FontInterpolator { /** Loading @@ -61,8 +59,11 @@ class FontInterpolator { var index: Int, val sortedAxes: MutableList<FontVariationAxis> ) { constructor(font: Font, axes: List<FontVariationAxis>) : this(font.sourceIdentifier, constructor( font: Font, axes: List<FontVariationAxis> ) : this( font.sourceIdentifier, font.ttcIndex, axes.toMutableList().apply { sortBy { it.tag } } ) Loading @@ -86,9 +87,7 @@ class FontInterpolator { private val tmpInterpKey = InterpKey(null, null, 0f) private val tmpVarFontKey = VarFontKey(0, 0, mutableListOf()) /** * Linear interpolate the font variation settings. */ /** Linear interpolate the font variation settings. */ fun lerp(start: Font, end: Font, progress: Float): Font { if (progress == 0f) { return start Loading @@ -115,19 +114,26 @@ class FontInterpolator { // this doesn't take much time since the variation axes is usually up to 5. If we need to // support more number of axes, we may want to preprocess the font and store the sorted axes // and also pre-fill the missing axes value with default value from 'fvar' table. val newAxes = lerp(startAxes, endAxes) { tag, startValue, endValue -> val newAxes = lerp(startAxes, endAxes) { tag, startValue, endValue -> when (tag) { // TODO: Good to parse 'fvar' table for retrieving default value. TAG_WGHT -> adjustWeight( TAG_WGHT -> adjustWeight( MathUtils.lerp( startValue ?: FONT_WEIGHT_DEFAULT_VALUE, endValue ?: FONT_WEIGHT_DEFAULT_VALUE, progress)) TAG_ITAL -> adjustItalic( progress ) ) TAG_ITAL -> adjustItalic( MathUtils.lerp( startValue ?: FONT_ITALIC_DEFAULT_VALUE, endValue ?: FONT_ITALIC_DEFAULT_VALUE, progress)) progress ) ) else -> { require(startValue != null && endValue != null) { "Unable to interpolate due to unknown default axes value : $tag" Loading @@ -149,9 +155,7 @@ class FontInterpolator { // 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() val newFont = Font.Builder(start).setFontVariationSettings(newAxes.toTypedArray()).build() interpCache[InterpKey(start, end, progress)] = newFont verFontCache[VarFontKey(start, newAxes)] = newFont return newFont Loading @@ -173,13 +177,15 @@ class FontInterpolator { val tagA = if (i < start.size) start[i].tag else null val tagB = if (j < end.size) end[j].tag else null val comp = when { val comp = when { tagA == null -> 1 tagB == null -> -1 else -> tagA.compareTo(tagB) } val axis = when { val axis = when { comp == 0 -> { val v = filter(tagA!!, start[i++].styleValue, end[j++].styleValue) FontVariationAxis(tagA, v) Loading
packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt +65 −82 Original line number Diff line number Diff line Loading @@ -36,8 +36,8 @@ typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit * Currently this class can provide text style animation for text weight and text size. For example * the simple view that draws text with animating text size is like as follows: * * <pre> * <code> * <pre> <code> * ``` * class SimpleTextAnimation : View { * @JvmOverloads constructor(...) * Loading @@ -53,83 +53,63 @@ typealias GlyphCallback = (TextAnimator.PositionedGlyph, Float) -> Unit * animator.setTextStyle(-1 /* unchanged weight */, sizePx, animate) * } * } * </code> * </pre> * ``` * </code> </pre> */ class TextAnimator( layout: Layout, private val invalidateCallback: () -> Unit ) { class TextAnimator(layout: Layout, private val invalidateCallback: () -> Unit) { // Following two members are for mutable for testing purposes. public var textInterpolator: TextInterpolator = TextInterpolator(layout) public var animator: ValueAnimator = ValueAnimator.ofFloat(1f).apply { public var animator: ValueAnimator = ValueAnimator.ofFloat(1f).apply { duration = DEFAULT_ANIMATION_DURATION addUpdateListener { textInterpolator.progress = it.animatedValue as Float invalidateCallback() } addListener(object : AnimatorListenerAdapter() { addListener( object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator?) { textInterpolator.rebase() } override fun onAnimationCancel(animation: Animator?) = textInterpolator.rebase() }) } ) } sealed class PositionedGlyph { /** * Mutable X coordinate of the glyph position relative from drawing offset. */ /** Mutable X coordinate of the glyph position relative from drawing offset. */ var x: Float = 0f /** * Mutable Y coordinate of the glyph position relative from the baseline. */ /** Mutable Y coordinate of the glyph position relative from the baseline. */ var y: Float = 0f /** * The current line of text being drawn, in a multi-line TextView. */ /** The current line of text being drawn, in a multi-line TextView. */ var lineNo: Int = 0 /** * Mutable text size of the glyph in pixels. */ /** Mutable text size of the glyph in pixels. */ var textSize: Float = 0f /** * Mutable color of the glyph. */ /** Mutable color of the glyph. */ var color: Int = 0 /** * Immutable character offset in the text that the current font run start. */ /** Immutable character offset in the text that the current font run start. */ abstract var runStart: Int protected set /** * Immutable run length of the font run. */ /** Immutable run length of the font run. */ abstract var runLength: Int protected set /** * Immutable glyph index of the font run. */ /** Immutable glyph index of the font run. */ abstract var glyphIndex: Int protected set /** * Immutable font instance for this font run. */ /** Immutable font instance for this font run. */ abstract var font: Font protected set /** * Immutable glyph ID for this glyph. */ /** Immutable glyph ID for this glyph. */ abstract var glyphId: Int protected set } Loading @@ -147,20 +127,18 @@ class TextAnimator( /** * GlyphFilter applied just before drawing to canvas for tweaking positions and text size. * * This callback is called for each glyphs just before drawing the glyphs. This function will * be called with the intrinsic position, size, color, glyph ID and font instance. You can * mutate the position, size and color for tweaking animations. * Do not keep the reference of passed glyph object. The interpolator reuses that object for * avoiding object allocations. * This callback is called for each glyphs just before drawing the glyphs. This function will be * called with the intrinsic position, size, color, glyph ID and font instance. You can mutate * the position, size and color for tweaking animations. Do not keep the reference of passed * glyph object. The interpolator reuses that object for avoiding object allocations. * * Details: * The text is drawn with font run units. The font run is a text segment that draws with the * same font. The {@code runStart} and {@code runLimit} is a range of the font run in the text * that current glyph is in. Once the font run is determined, the system will convert characters * into glyph IDs. The {@code glyphId} is the glyph identifier in the font and * {@code glyphIndex} is the offset of the converted glyph array. Please note that the * {@code glyphIndex} is not a character index, because the character will not be converted to * glyph one-by-one. If there are ligatures including emoji sequence, etc, the glyph ID may be * Details: The text is drawn with font run units. The font run is a text segment that draws * with the same font. The {@code runStart} and {@code runLimit} is a range of the font run in * the text that current glyph is in. Once the font run is determined, the system will convert * characters into glyph IDs. The {@code glyphId} is the glyph identifier in the font and {@code * glyphIndex} is the offset of the converted glyph array. Please note that the {@code * glyphIndex} is not a character index, because the character will not be converted to glyph * one-by-one. If there are ligatures including emoji sequence, etc, the glyph ID may be * composed from multiple characters. * * Here is an example of font runs: "fin. 終わり" Loading Loading @@ -193,7 +171,9 @@ class TextAnimator( */ var glyphFilter: GlyphCallback? get() = textInterpolator.glyphFilter set(value) { textInterpolator.glyphFilter = value } set(value) { textInterpolator.glyphFilter = value } fun draw(c: Canvas) = textInterpolator.draw(c) Loading Loading @@ -237,7 +217,8 @@ class TextAnimator( if (weight >= 0) { // Paint#setFontVariationSettings creates Typeface instance from scratch. To reduce the // memory impact, cache the typeface result. textInterpolator.targetPaint.typeface = typefaceCache.getOrElse(weight) { textInterpolator.targetPaint.typeface = typefaceCache.getOrElse(weight) { textInterpolator.targetPaint.fontVariationSettings = "'$TAG_WGHT' $weight" textInterpolator.targetPaint.typeface } Loading @@ -249,14 +230,16 @@ class TextAnimator( if (animate) { animator.startDelay = delay animator.duration = if (duration == -1L) { animator.duration = if (duration == -1L) { DEFAULT_ANIMATION_DURATION } else { duration } interpolator?.let { animator.interpolator = it } if (onAnimationEnd != null) { val listener = object : AnimatorListenerAdapter() { val listener = object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator?) { onAnimationEnd.run() animator.removeListener(this) Loading
packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt +113 −119 Original line number Diff line number Diff line Loading @@ -26,12 +26,8 @@ import android.util.MathUtils import com.android.internal.graphics.ColorUtils import java.lang.Math.max /** * Provide text style linear interpolation for plain text. */ class TextInterpolator( layout: Layout ) { /** Provide text style linear interpolation for plain text. */ class TextInterpolator(layout: Layout) { /** * Returns base paint used for interpolation. Loading Loading @@ -64,12 +60,11 @@ class TextInterpolator( var baseFont: Font, var targetFont: Font ) { val length: Int get() = end - start val length: Int get() = end - start } /** * A class represents text layout of a single run. */ /** A class represents text layout of a single run. */ private class Run( val glyphIds: IntArray, val baseX: FloatArray, // same length as glyphIds Loading @@ -79,12 +74,8 @@ class TextInterpolator( val fontRuns: List<FontRun> ) /** * A class represents text layout of a single line. */ private class Line( val runs: List<Run> ) /** 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() Loading @@ -106,8 +97,8 @@ class TextInterpolator( /** * The layout used for drawing text. * * Only non-styled text is supported. Even if the given layout is created from Spanned, the * span information is not used. * Only non-styled text is supported. Even if the given layout is created from Spanned, the span * information is not used. * * The paint objects used for interpolation are not changed by this method call. * Loading @@ -133,8 +124,8 @@ class TextInterpolator( /** * Recalculate internal text layout for interpolation. * * Whenever the target paint is modified, call this method to recalculate internal * text layout used for interpolation. * Whenever the target paint is modified, call this method to recalculate internal text layout * used for interpolation. */ fun onTargetPaintModified() { updatePositionsAndFonts(shapeText(layout, targetPaint), updateBase = false) Loading @@ -143,8 +134,8 @@ class TextInterpolator( /** * Recalculate internal text layout for interpolation. * * Whenever the base paint is modified, call this method to recalculate internal * text layout used for interpolation. * Whenever the base paint is modified, call this method to recalculate internal text layout * used for interpolation. */ fun onBasePaintModified() { updatePositionsAndFonts(shapeText(layout, basePaint), updateBase = true) Loading @@ -155,11 +146,11 @@ class TextInterpolator( * * The text interpolator does not calculate all the text position by text shaper due to * performance reasons. Instead, the text interpolator shape the start and end state and * calculate text position of the middle state by linear interpolation. Due to this trick, * the text positions of the middle state is likely different from the text shaper result. * So, if you want to start animation from the middle state, you will see the glyph jumps due to * this trick, i.e. the progress 0.5 of interpolation between weight 400 and 700 is different * from text shape result of weight 550. * calculate text position of the middle state by linear interpolation. Due to this trick, the * text positions of the middle state is likely different from the text shaper result. So, if * you want to start animation from the middle state, you will see the glyph jumps due to this * trick, i.e. the progress 0.5 of interpolation between weight 400 and 700 is different from * text shape result of weight 550. * * After calling this method, do not call onBasePaintModified() since it reshape the text and * update the base state. As in above notice, the text shaping result at current progress is Loading @@ -171,8 +162,8 @@ class TextInterpolator( * animate weight from 200 to 400, then if you want to move back to 200 at the half of the * animation, it will look like * * <pre> * <code> * <pre> <code> * ``` * val interp = TextInterpolator(layout) * * // Interpolate between weight 200 to 400. Loading Loading @@ -202,9 +193,8 @@ class TextInterpolator( * // progress is 0.5 * animator.start() * } * </code> * </pre> * * ``` * </code> </pre> */ fun rebase() { if (progress == 0f) { Loading Loading @@ -266,17 +256,20 @@ class TextInterpolator( } var maxRunLength = 0 lines = baseLayout.zip(targetLayout) { baseLine, targetLine -> val runs = baseLine.zip(targetLine) { base, target -> lines = baseLayout.zip(targetLayout) { baseLine, targetLine -> val runs = baseLine.zip(targetLine) { base, target -> require(base.glyphCount() == target.glyphCount()) { "Inconsistent glyph count at line ${lines.size}" } val glyphCount = base.glyphCount() // Good to recycle the array if the existing array can hold the new layout result. val glyphIds = IntArray(glyphCount) { // Good to recycle the array if the existing array can hold the new layout // result. val glyphIds = IntArray(glyphCount) { base.getGlyphId(it).also { baseGlyphId -> require(baseGlyphId == target.getGlyphId(it)) { "Inconsistent glyph ID at $it in line ${lines.size}" Loading Loading @@ -305,7 +298,7 @@ class TextInterpolator( if (baseFont !== nextBaseFont) { require(targetFont !== nextTargetFont) { "Base font has changed at $i but target font has not changed." "Base font has changed at $i but target font is unchanged." } // Font transition point. push run and reset context. fontRun.add(FontRun(start, i, baseFont, targetFont)) Loading @@ -314,11 +307,12 @@ class TextInterpolator( targetFont = nextTargetFont start = i require(FontInterpolator.canInterpolate(baseFont, targetFont)) { "Cannot interpolate font at $start ($baseFont vs $targetFont)" "Cannot interpolate font at $start" + " ($baseFont vs $targetFont)" } } else { // baseFont === nextBaseFont require(targetFont === nextTargetFont) { "Base font has not changed at $i but target font has changed." "Base font is unchanged at $i but target font has changed." } } } Loading Loading @@ -397,7 +391,8 @@ class TextInterpolator( 0, i - prevStart, font, tmpPaintForGlyph) tmpPaintForGlyph ) prevStart = i arrayIndex = 0 } Loading @@ -413,7 +408,8 @@ class TextInterpolator( 0, run.end - prevStart, font, tmpPaintForGlyph) tmpPaintForGlyph ) } private fun updatePositionsAndFonts( Loading @@ -421,9 +417,7 @@ class TextInterpolator( updateBase: Boolean ) { // Update target positions with newly calculated text layout. check(layoutResult.size == lines.size) { "The new layout result has different line count." } check(layoutResult.size == lines.size) { "The new layout result has different line count." } lines.zip(layoutResult) { line, runs -> line.runs.zip(runs) { lineRun, newGlyphs -> Loading Loading @@ -483,10 +477,7 @@ class TextInterpolator( } // Shape the text and stores the result to out argument. private fun shapeText( layout: Layout, paint: TextPaint ): List<List<PositionedGlyphs>> { private fun shapeText(layout: Layout, paint: TextPaint): List<List<PositionedGlyphs>> { var text = StringBuilder() val out = mutableListOf<List<PositionedGlyphs>>() for (lineNo in 0 until layout.lineCount) { // Shape all lines. Loading @@ -500,10 +491,13 @@ class TextInterpolator( } val runs = mutableListOf<PositionedGlyphs>() TextShaper.shapeText(layout.text, lineStart, count, layout.textDirectionHeuristic, paint) { _, _, glyphs, _ -> runs.add(glyphs) } TextShaper.shapeText( layout.text, lineStart, count, layout.textDirectionHeuristic, paint ) { _, _, glyphs, _ -> runs.add(glyphs) } out.add(runs) if (lineNo > 0) { Loading
packages/SystemUI/ktfmt_includes.txt +0 −2 Original line number Diff line number Diff line +packages/SystemUI -packages/SystemUI/animation/src/com/android/systemui/animation/FontInterpolator.kt -packages/SystemUI/animation/src/com/android/systemui/animation/TextAnimator.kt -packages/SystemUI/animation/src/com/android/systemui/animation/TextInterpolator.kt -packages/SystemUI/animation/src/com/android/systemui/animation/ViewHierarchyAnimator.kt -packages/SystemUI/checks/src/com/android/internal/systemui/lint/BindServiceViaContextDetector.kt -packages/SystemUI/checks/src/com/android/internal/systemui/lint/BroadcastSentViaContextDetector.kt Loading