xref: /aosp_15_r20/frameworks/base/packages/SystemUI/tests/src/com/android/systemui/animation/TextAnimatorTest.kt (revision d57664e9bc4670b3ecf6748a746a57c557b6bc9e)
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.animation
18 
19 import android.animation.AnimatorListenerAdapter
20 import android.animation.ValueAnimator
21 import android.graphics.Typeface
22 import android.text.Layout
23 import android.text.StaticLayout
24 import android.text.TextPaint
25 import androidx.test.ext.junit.runners.AndroidJUnit4
26 import androidx.test.filters.SmallTest
27 import com.android.systemui.SysuiTestCase
28 import com.google.common.truth.Truth.assertThat
29 import kotlin.math.ceil
30 import org.junit.Test
31 import org.junit.runner.RunWith
32 import org.mockito.ArgumentCaptor
33 import org.mockito.Mockito.eq
34 import org.mockito.Mockito.inOrder
35 import org.mockito.Mockito.mock
36 import org.mockito.Mockito.never
37 import org.mockito.Mockito.times
38 import org.mockito.Mockito.verify
39 import org.mockito.Mockito.`when`
40 
41 @RunWith(AndroidJUnit4::class)
42 @SmallTest
43 class TextAnimatorTest : SysuiTestCase() {
44 
45     private val typeface = Typeface.createFromFile("/system/fonts/Roboto-Regular.ttf")
46 
makeLayoutnull47     private fun makeLayout(text: String, paint: TextPaint): Layout {
48         val width = ceil(Layout.getDesiredWidth(text, 0, text.length, paint)).toInt()
49         return StaticLayout.Builder.obtain(text, 0, text.length, paint, width).build()
50     }
51 
52     @Test
testAnimationStartednull53     fun testAnimationStarted() {
54         val layout = makeLayout("Hello, World", PAINT)
55         val valueAnimator = mock(ValueAnimator::class.java)
56         val textInterpolator = mock(TextInterpolator::class.java)
57         val paint = mock(TextPaint::class.java)
58         `when`(textInterpolator.targetPaint).thenReturn(paint)
59 
60         val textAnimator =
61             TextAnimator(layout, TypefaceVariantCacheImpl(typeface, 20)).apply {
62                 this.textInterpolator = textInterpolator
63                 this.animator = valueAnimator
64             }
65 
66         textAnimator.setTextStyle(weight = 400, animate = true)
67 
68         // If animation is requested, the base state should be rebased and the target state should
69         // be updated.
70         val order = inOrder(textInterpolator)
71         order.verify(textInterpolator).rebase()
72         order.verify(textInterpolator).onTargetPaintModified()
73 
74         // In case of animation, should not shape the base state since the animation should start
75         // from current state.
76         verify(textInterpolator, never()).onBasePaintModified()
77 
78         // Then, animation should be started.
79         verify(valueAnimator, times(1)).start()
80     }
81 
82     @Test
testAnimationNotStartednull83     fun testAnimationNotStarted() {
84         val layout = makeLayout("Hello, World", PAINT)
85         val valueAnimator = mock(ValueAnimator::class.java)
86         val textInterpolator = mock(TextInterpolator::class.java)
87         val paint = mock(TextPaint::class.java)
88         `when`(textInterpolator.targetPaint).thenReturn(paint)
89 
90         val textAnimator =
91             TextAnimator(layout, TypefaceVariantCacheImpl(typeface, 20)).apply {
92                 this.textInterpolator = textInterpolator
93                 this.animator = valueAnimator
94             }
95 
96         textAnimator.setTextStyle(weight = 400, animate = false)
97 
98         // If animation is not requested, the progress should be 1 which is end of animation and the
99         // base state is rebased to target state by calling rebase.
100         val order = inOrder(textInterpolator)
101         order.verify(textInterpolator).onTargetPaintModified()
102         order.verify(textInterpolator).progress = 1f
103         order.verify(textInterpolator).rebase()
104 
105         // Then, animation start should not be called.
106         verify(valueAnimator, never()).start()
107     }
108 
109     @Test
testAnimationEndednull110     fun testAnimationEnded() {
111         val layout = makeLayout("Hello, World", PAINT)
112         val valueAnimator = mock(ValueAnimator::class.java)
113         val textInterpolator = mock(TextInterpolator::class.java)
114         val paint = mock(TextPaint::class.java)
115         `when`(textInterpolator.targetPaint).thenReturn(paint)
116         val animationEndCallback = mock(Runnable::class.java)
117 
118         val textAnimator =
119             TextAnimator(layout, TypefaceVariantCacheImpl(typeface, 20)).apply {
120                 this.textInterpolator = textInterpolator
121                 this.animator = valueAnimator
122             }
123 
124         textAnimator.setTextStyle(
125             weight = 400,
126             animate = true,
127             onAnimationEnd = animationEndCallback,
128         )
129 
130         // Verify animationEnd callback has been added.
131         val captor = ArgumentCaptor.forClass(AnimatorListenerAdapter::class.java)
132         verify(valueAnimator).addListener(captor.capture())
133         captor.value.onAnimationEnd(valueAnimator)
134 
135         // Verify animationEnd callback has been invoked and removed.
136         verify(animationEndCallback).run()
137         verify(valueAnimator).removeListener(eq(captor.value))
138     }
139 
140     @Test
testCacheTypefacenull141     fun testCacheTypeface() {
142         val layout = makeLayout("Hello, World", PAINT)
143         val valueAnimator = mock(ValueAnimator::class.java)
144         val textInterpolator = mock(TextInterpolator::class.java)
145         val paint = TextPaint().apply { this.typeface = typeface }
146         `when`(textInterpolator.targetPaint).thenReturn(paint)
147 
148         val textAnimator =
149             TextAnimator(layout, TypefaceVariantCacheImpl(typeface, 20)).apply {
150                 this.textInterpolator = textInterpolator
151                 this.animator = valueAnimator
152             }
153 
154         textAnimator.setTextStyle(weight = 400, animate = true)
155 
156         val prevTypeface = paint.typeface
157 
158         textAnimator.setTextStyle(weight = 700, animate = true)
159 
160         assertThat(paint.typeface).isNotSameInstanceAs(prevTypeface)
161 
162         textAnimator.setTextStyle(weight = 400, animate = true)
163 
164         assertThat(paint.typeface).isSameInstanceAs(prevTypeface)
165     }
166 }
167