1 /*
2  * Copyright (C) 2021 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.statusbar.phone.ongoingcall
18 
19 import android.testing.AndroidTestingRunner
20 import android.testing.TestableLooper
21 import android.view.LayoutInflater
22 import android.view.View
23 import androidx.test.filters.SmallTest
24 import com.android.systemui.R
25 import com.android.systemui.SysuiTestCase
26 import com.google.common.truth.Truth.assertThat
27 import org.junit.Before
28 import org.junit.Test
29 import org.junit.runner.RunWith
30 
31 private const val TEXT_VIEW_MAX_WIDTH = 400
32 
33 // When a [Chronometer] is created, it starts off with "00:00" as its text.
34 private const val INITIAL_TEXT = "00:00"
35 private const val LARGE_TEXT = "00:000"
36 private const val XL_TEXT = "00:0000"
37 
38 @SmallTest
39 @RunWith(AndroidTestingRunner::class)
40 @TestableLooper.RunWithLooper
41 class OngoingCallChronometerTest : SysuiTestCase() {
42 
43     private lateinit var textView: OngoingCallChronometer
44     private lateinit var doesNotFitText: String
45 
46     @Before
47     fun setUp() {
48         allowTestableLooperAsMainThread()
49         TestableLooper.get(this).runWithLooper {
50             val chipView = LayoutInflater.from(mContext).inflate(R.layout.ongoing_call_chip, null)
51             textView = chipView.findViewById(R.id.ongoing_call_chip_time)!!
52             measureTextView()
53             calculateDoesNotFixText()
54         }
55     }
56 
57     @Test
58     fun verifyTextSizes() {
59         val initialTextLength = textView.paint.measureText(INITIAL_TEXT)
60         val largeTextLength = textView.paint.measureText(LARGE_TEXT)
61         val xlTextLength = textView.paint.measureText(XL_TEXT)
62 
63         // Assert that our test text sizes do what we expect them to do in the rest of the tests.
64         assertThat(initialTextLength).isLessThan(TEXT_VIEW_MAX_WIDTH)
65         assertThat(largeTextLength).isLessThan(TEXT_VIEW_MAX_WIDTH)
66         assertThat(xlTextLength).isLessThan(TEXT_VIEW_MAX_WIDTH)
67         assertThat(textView.paint.measureText(doesNotFitText)).isGreaterThan(TEXT_VIEW_MAX_WIDTH)
68 
69         assertThat(largeTextLength).isGreaterThan(initialTextLength)
70         assertThat(xlTextLength).isGreaterThan(largeTextLength)
71     }
72 
73     @Test
74     fun onMeasure_initialTextFitsInSpace_textDisplayed() {
75         assertThat(textView.measuredWidth).isGreaterThan(0)
76     }
77 
78     @Test
79     fun onMeasure_newTextLargerThanPreviousText_widthGetsLarger() {
80         val initialTextLength = textView.measuredWidth
81 
82         setTextAndMeasure(LARGE_TEXT)
83 
84         assertThat(textView.measuredWidth).isGreaterThan(initialTextLength)
85     }
86 
87     @Test
88     fun onMeasure_newTextSmallerThanPreviousText_widthDoesNotGetSmaller() {
89         setTextAndMeasure(XL_TEXT)
90         val xlWidth = textView.measuredWidth
91 
92         setTextAndMeasure(LARGE_TEXT)
93 
94         assertThat(textView.measuredWidth).isEqualTo(xlWidth)
95     }
96 
97     @Test
98     fun onMeasure_textDoesNotFit_textHidden() {
99         setTextAndMeasure(doesNotFitText)
100 
101         assertThat(textView.measuredWidth).isEqualTo(0)
102     }
103 
104     @Test
105     fun onMeasure_newTextFitsButPreviousTextDidNot_textHidden() {
106         setTextAndMeasure(doesNotFitText)
107 
108         setTextAndMeasure(LARGE_TEXT)
109 
110         assertThat(textView.measuredWidth).isEqualTo(0)
111     }
112 
113     @Test
114     fun resetBase_hadLongerTextThenSetBaseThenShorterText_widthIsShort() {
115         setTextAndMeasure(XL_TEXT)
116         val xlWidth = textView.measuredWidth
117 
118         textView.base = 0L
119         setTextAndMeasure(INITIAL_TEXT)
120 
121         assertThat(textView.measuredWidth).isLessThan(xlWidth)
122         assertThat(textView.measuredWidth).isGreaterThan(0)
123     }
124 
125     @Test
126     fun setBase_wasHidingTextThenSetBaseThenShorterText_textShown() {
127         setTextAndMeasure(doesNotFitText)
128 
129         textView.base = 0L
130         setTextAndMeasure(INITIAL_TEXT)
131 
132         assertThat(textView.measuredWidth).isGreaterThan(0)
133     }
134 
135     @Test
136     fun setShouldHideText_true_textHidden() {
137         textView.setShouldHideText(true)
138         measureTextView()
139 
140         assertThat(textView.measuredWidth).isEqualTo(0)
141     }
142 
143     @Test
144     fun setShouldHideText_false_textShown() {
145         // First, set to true so that setting it to false will definitely have an effect.
146         textView.setShouldHideText(true)
147         measureTextView()
148 
149         textView.setShouldHideText(false)
150         measureTextView()
151 
152         assertThat(textView.measuredWidth).isGreaterThan(0)
153     }
154 
155     private fun setTextAndMeasure(text: String) {
156         textView.text = text
157         measureTextView()
158     }
159 
160     private fun measureTextView() {
161         textView.measure(
162                 View.MeasureSpec.makeMeasureSpec(TEXT_VIEW_MAX_WIDTH, View.MeasureSpec.AT_MOST),
163                 View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
164         )
165     }
166 
167     /**
168      * Calculates what [doesNotFitText] should be. Needs to be done dynamically because different
169      * devices have different densities, which means the textView can fit different amounts of
170      * characters.
171      */
172     private fun calculateDoesNotFixText() {
173         var currentText = XL_TEXT + "0"
174         while (textView.paint.measureText(currentText) <= TEXT_VIEW_MAX_WIDTH) {
175             currentText += "0"
176         }
177         doesNotFitText = currentText
178     }
179 }
180