1 /*
2  * Copyright (C) 2008 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 android.widget.cts;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertSame;
22 import static org.junit.Assert.assertTrue;
23 import static org.mockito.Mockito.atLeastOnce;
24 import static org.mockito.Mockito.reset;
25 import static org.mockito.Mockito.spy;
26 import static org.mockito.Mockito.times;
27 import static org.mockito.Mockito.verify;
28 
29 import android.app.Activity;
30 import android.app.Instrumentation;
31 import android.content.Context;
32 import android.content.res.ColorStateList;
33 import android.graphics.BlendMode;
34 import android.graphics.Color;
35 import android.graphics.PorterDuff;
36 import android.graphics.drawable.ColorDrawable;
37 import android.graphics.drawable.Drawable;
38 import android.util.AttributeSet;
39 import android.view.KeyEvent;
40 import android.widget.AbsSeekBar;
41 import android.widget.SeekBar;
42 import android.widget.cts.util.TestUtils;
43 
44 import androidx.test.InstrumentationRegistry;
45 import androidx.test.annotation.UiThreadTest;
46 import androidx.test.filters.SmallTest;
47 import androidx.test.rule.ActivityTestRule;
48 import androidx.test.runner.AndroidJUnit4;
49 
50 import com.android.compatibility.common.util.PollingCheck;
51 import com.android.compatibility.common.util.WidgetTestUtils;
52 
53 import org.junit.Before;
54 import org.junit.Rule;
55 import org.junit.Test;
56 import org.junit.runner.RunWith;
57 import org.mockito.ArgumentCaptor;
58 
59 import java.util.List;
60 
61 /**
62  * Test {@link AbsSeekBar}.
63  */
64 @SmallTest
65 @RunWith(AndroidJUnit4.class)
66 public class AbsSeekBarTest {
67     private Instrumentation mInstrumentation;
68     private Activity mActivity;
69 
70     @Rule
71     public ActivityTestRule<AbsSeekBarCtsActivity> mActivityRule =
72             new ActivityTestRule<>(AbsSeekBarCtsActivity.class);
73 
74     @Before
setup()75     public void setup() {
76         mInstrumentation = InstrumentationRegistry.getInstrumentation();
77         mActivity = mActivityRule.getActivity();
78     }
79 
80     @Test
testConstructor()81     public void testConstructor() {
82         new MyAbsSeekBar(mActivity);
83 
84         new MyAbsSeekBar(mActivity, null);
85 
86         new MyAbsSeekBar(mActivity, null, android.R.attr.progressBarStyle);
87 
88         new MyAbsSeekBar(mActivity, null, 0, android.R.style.Widget_Material_Light_ProgressBar);
89     }
90 
91     @Test
testAccessThumbOffset()92     public void testAccessThumbOffset() {
93         AbsSeekBar myAbsSeekBar = new MyAbsSeekBar(mActivity);
94         final int positive = 5;
95         final int negative = -5;
96         final int zero = 0;
97 
98         myAbsSeekBar.setThumbOffset(positive);
99         assertEquals(positive, myAbsSeekBar.getThumbOffset());
100 
101         myAbsSeekBar.setThumbOffset(zero);
102         assertEquals(zero, myAbsSeekBar.getThumbOffset());
103 
104         myAbsSeekBar.setThumbOffset(negative);
105         assertEquals(negative, myAbsSeekBar.getThumbOffset());
106     }
107 
108     @Test
testAccessThumb()109     public void testAccessThumb() {
110         // Both are pointing to the same object. This works around current limitation in CTS
111         // coverage report tool for properly reporting coverage of base class method calls.
112         final MyAbsSeekBar myAbsSeekBar = new MyAbsSeekBar(mActivity);
113         final AbsSeekBar absSeekBar = myAbsSeekBar;
114 
115         Drawable drawable1 = mActivity.getDrawable(R.drawable.scenery);
116         Drawable drawable2 = mActivity.getDrawable(R.drawable.pass);
117 
118         assertFalse(myAbsSeekBar.verifyDrawable(drawable1));
119         assertFalse(myAbsSeekBar.verifyDrawable(drawable2));
120 
121         absSeekBar.setThumb(drawable1);
122         assertSame(drawable1, absSeekBar.getThumb());
123         assertTrue(myAbsSeekBar.verifyDrawable(drawable1));
124         assertFalse(myAbsSeekBar.verifyDrawable(drawable2));
125 
126         absSeekBar.setThumb(drawable2);
127         assertSame(drawable2, absSeekBar.getThumb());
128         assertFalse(myAbsSeekBar.verifyDrawable(drawable1));
129         assertTrue(myAbsSeekBar.verifyDrawable(drawable2));
130     }
131 
132     @Test
testAccessTickMark()133     public void testAccessTickMark() {
134         // Both are pointing to the same object. This works around current limitation in CTS
135         // coverage report tool for properly reporting coverage of base class method calls.
136         final MyAbsSeekBar myAbsSeekBar = new MyAbsSeekBar(mActivity);
137         final AbsSeekBar absSeekBar = myAbsSeekBar;
138 
139         Drawable drawable1 = mActivity.getDrawable(R.drawable.black);
140         Drawable drawable2 = mActivity.getDrawable(R.drawable.black);
141 
142         assertFalse(myAbsSeekBar.verifyDrawable(drawable1));
143         assertFalse(myAbsSeekBar.verifyDrawable(drawable2));
144 
145         absSeekBar.setTickMark(drawable1);
146         assertSame(drawable1, absSeekBar.getTickMark());
147         assertTrue(myAbsSeekBar.verifyDrawable(drawable1));
148         assertFalse(myAbsSeekBar.verifyDrawable(drawable2));
149 
150         absSeekBar.setTickMark(drawable2);
151         assertSame(drawable2, absSeekBar.getTickMark());
152         assertFalse(myAbsSeekBar.verifyDrawable(drawable1));
153         assertTrue(myAbsSeekBar.verifyDrawable(drawable2));
154     }
155 
156     @Test
testDrawableStateChanged()157     public void testDrawableStateChanged() {
158         MyAbsSeekBar myAbsSeekBar = new MyAbsSeekBar(mActivity);
159         Drawable mockProgressDrawable = spy(new ColorDrawable(Color.YELLOW));
160         myAbsSeekBar.setProgressDrawable(mockProgressDrawable);
161 
162         ArgumentCaptor<Integer> alphaCaptor = ArgumentCaptor.forClass(Integer.class);
163         myAbsSeekBar.setEnabled(false);
164         myAbsSeekBar.drawableStateChanged();
165         verify(mockProgressDrawable, atLeastOnce()).setAlpha(alphaCaptor.capture());
166         // Verify that the last call to setAlpha was with argument 0x00
167         List<Integer> alphaCaptures = alphaCaptor.getAllValues();
168         assertTrue(!alphaCaptures.isEmpty());
169         assertEquals(Integer.valueOf(0x00), alphaCaptures.get(alphaCaptures.size() - 1));
170 
171         alphaCaptor = ArgumentCaptor.forClass(Integer.class);
172         myAbsSeekBar.setEnabled(true);
173         myAbsSeekBar.drawableStateChanged();
174         verify(mockProgressDrawable, atLeastOnce()).setAlpha(alphaCaptor.capture());
175         // Verify that the last call to setAlpha was with argument 0xFF
176         alphaCaptures = alphaCaptor.getAllValues();
177         assertTrue(!alphaCaptures.isEmpty());
178         assertEquals(Integer.valueOf(0xFF), alphaCaptures.get(alphaCaptures.size() - 1));
179     }
180 
181     @Test
testVerifyDrawable()182     public void testVerifyDrawable() {
183         MyAbsSeekBar myAbsSeekBar = new MyAbsSeekBar(mActivity);
184         Drawable drawable1 = mActivity.getDrawable(R.drawable.scenery);
185         Drawable drawable2 = mActivity.getDrawable(R.drawable.pass);
186         Drawable drawable3 = mActivity.getDrawable(R.drawable.blue);
187         Drawable drawable4 = mActivity.getDrawable(R.drawable.black);
188 
189         assertFalse(myAbsSeekBar.verifyDrawable(drawable1));
190         assertFalse(myAbsSeekBar.verifyDrawable(drawable2));
191         assertFalse(myAbsSeekBar.verifyDrawable(drawable3));
192         assertFalse(myAbsSeekBar.verifyDrawable(drawable4));
193 
194         myAbsSeekBar.setThumb(drawable1);
195         assertTrue(myAbsSeekBar.verifyDrawable(drawable1));
196         assertFalse(myAbsSeekBar.verifyDrawable(drawable2));
197         assertFalse(myAbsSeekBar.verifyDrawable(drawable3));
198         assertFalse(myAbsSeekBar.verifyDrawable(drawable4));
199 
200         myAbsSeekBar.setThumb(drawable2);
201         assertFalse(myAbsSeekBar.verifyDrawable(drawable1));
202         assertTrue(myAbsSeekBar.verifyDrawable(drawable2));
203         assertFalse(myAbsSeekBar.verifyDrawable(drawable3));
204         assertFalse(myAbsSeekBar.verifyDrawable(drawable4));
205 
206         myAbsSeekBar.setBackgroundDrawable(drawable2);
207         myAbsSeekBar.setProgressDrawable(drawable3);
208         myAbsSeekBar.setIndeterminateDrawable(drawable4);
209         assertFalse(myAbsSeekBar.verifyDrawable(drawable1));
210         assertTrue(myAbsSeekBar.verifyDrawable(drawable2));
211         assertTrue(myAbsSeekBar.verifyDrawable(drawable3));
212         assertTrue(myAbsSeekBar.verifyDrawable(drawable4));
213     }
214 
215     @Test
testAccessKeyProgressIncrement()216     public void testAccessKeyProgressIncrement() throws Throwable {
217         // AbsSeekBar is an abstract class, use its subclass: SeekBar to do this test.
218         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mActivity.getWindow().getDecorView(),
219                 () -> mActivity.setContentView(R.layout.seekbar_layout));
220 
221         final SeekBar seekBar = (SeekBar) mActivity.findViewById(R.id.seekBar);
222         final int keyProgressIncrement = 2;
223         mActivityRule.runOnUiThread(() -> {
224             seekBar.setKeyProgressIncrement(keyProgressIncrement);
225             seekBar.setFocusable(true);
226             seekBar.requestFocus();
227         });
228         PollingCheck.waitFor(1000, seekBar::hasWindowFocus);
229         assertEquals(keyProgressIncrement, seekBar.getKeyProgressIncrement());
230 
231         int oldProgress = seekBar.getProgress();
232         KeyEvent keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_RIGHT);
233         mInstrumentation.sendKeySync(keyEvent);
234         assertEquals(oldProgress + keyProgressIncrement, seekBar.getProgress());
235         oldProgress = seekBar.getProgress();
236         keyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_LEFT);
237         mInstrumentation.sendKeySync(keyEvent);
238         assertEquals(oldProgress - keyProgressIncrement, seekBar.getProgress());
239     }
240 
241     @Test
testAccessMax()242     public void testAccessMax() {
243         AbsSeekBar myAbsSeekBar = new MyAbsSeekBar(mActivity, null, R.style.TestProgressBar);
244 
245         int progress = 10;
246         myAbsSeekBar.setProgress(progress);
247         int max = progress + 1;
248         myAbsSeekBar.setMax(max);
249         assertEquals(max, myAbsSeekBar.getMax());
250         assertEquals(progress, myAbsSeekBar.getProgress());
251         assertEquals(1, myAbsSeekBar.getKeyProgressIncrement());
252 
253         max = progress - 1;
254         myAbsSeekBar.setMax(max);
255         assertEquals(max, myAbsSeekBar.getMax());
256         assertEquals(max, myAbsSeekBar.getProgress());
257         assertEquals(1, myAbsSeekBar.getKeyProgressIncrement());
258 
259         int keyProgressIncrement = 10;
260         myAbsSeekBar.setKeyProgressIncrement(keyProgressIncrement);
261         assertEquals(keyProgressIncrement, myAbsSeekBar.getKeyProgressIncrement());
262         max = (keyProgressIncrement - 1) * 20;
263         myAbsSeekBar.setMax(max);
264         assertEquals(keyProgressIncrement, myAbsSeekBar.getKeyProgressIncrement());
265         max = (keyProgressIncrement + 1) * 20;
266         myAbsSeekBar.setMax(max);
267         assertEquals(keyProgressIncrement + 1, myAbsSeekBar.getKeyProgressIncrement());
268     }
269 
270     @Test
testAccessMin()271     public void testAccessMin() {
272         AbsSeekBar myAbsSeekBar = new MyAbsSeekBar(mActivity, null, R.style.TestProgressBar);
273 
274         int progress = -5;
275         int min = progress - 1;
276         int max = 5;
277         myAbsSeekBar.setMax(max);
278         myAbsSeekBar.setMin(min);
279         myAbsSeekBar.setProgress(progress);
280         assertEquals(min, myAbsSeekBar.getMin());
281         assertEquals(progress, myAbsSeekBar.getProgress());
282         assertEquals(1, myAbsSeekBar.getKeyProgressIncrement());
283 
284         min = progress + 1;
285         myAbsSeekBar.setMin(min);
286         assertEquals(min, myAbsSeekBar.getMin());
287         assertEquals(min, myAbsSeekBar.getProgress());
288         assertEquals(1, myAbsSeekBar.getKeyProgressIncrement());
289 
290         int keyProgressIncrement = 10;
291         myAbsSeekBar.setKeyProgressIncrement(keyProgressIncrement);
292         assertEquals(keyProgressIncrement, myAbsSeekBar.getKeyProgressIncrement());
293         max = (keyProgressIncrement - 1) * 10;
294         min = -1 * keyProgressIncrement * 10;
295         myAbsSeekBar.setMax(max);
296         myAbsSeekBar.setMin(min);
297         assertEquals(keyProgressIncrement, myAbsSeekBar.getKeyProgressIncrement());
298 
299         max += 20;
300         myAbsSeekBar.setMax(max);
301         assertEquals(keyProgressIncrement + 1, myAbsSeekBar.getKeyProgressIncrement());
302         assertEquals(min, myAbsSeekBar.getMin());
303         assertEquals(max, myAbsSeekBar.getMax());
304     }
305 
306     @UiThreadTest
307     @Test
testThumbTint()308     public void testThumbTint() {
309         AbsSeekBar inflatedView = (AbsSeekBar) mActivity.findViewById(R.id.thumb_tint);
310 
311         assertEquals("Thumb tint inflated correctly",
312                 Color.WHITE, inflatedView.getThumbTintList().getDefaultColor());
313         assertEquals("Thumb tint mode inflated correctly",
314                 PorterDuff.Mode.SRC_OVER, inflatedView.getThumbTintMode());
315 
316         Drawable mockThumb = spy(new ColorDrawable(Color.BLUE));
317 
318         inflatedView.setThumb(mockThumb);
319         verify(mockThumb, times(1)).setTintList(TestUtils.colorStateListOf(Color.WHITE));
320 
321         reset(mockThumb);
322         inflatedView.setThumbTintList(ColorStateList.valueOf(Color.RED));
323         verify(mockThumb, times(1))
324                 .setTintList(TestUtils.colorStateListOf(Color.RED));
325 
326         inflatedView.setThumbTintMode(PorterDuff.Mode.DST_ATOP);
327         assertEquals("Thumb tint mode changed correctly",
328                 PorterDuff.Mode.DST_ATOP, inflatedView.getThumbTintMode());
329 
330         reset(mockThumb);
331         inflatedView.setThumb(null);
332         inflatedView.setThumb(mockThumb);
333         verify(mockThumb, times(1))
334                 .setTintList(TestUtils.colorStateListOf(Color.RED));
335     }
336 
337     @UiThreadTest
338     @Test
testThumbTintBlendMode()339     public void testThumbTintBlendMode() {
340         AbsSeekBar inflatedView = (AbsSeekBar) mActivity.findViewById(R.id.thumb_tint);
341 
342         assertEquals("Thumb tint inflated correctly",
343                 Color.WHITE, inflatedView.getThumbTintList().getDefaultColor());
344         assertEquals("Thumb tint mode inflated correctly",
345                 BlendMode.SRC_OVER, inflatedView.getThumbTintBlendMode());
346 
347         Drawable mockThumb = spy(new ColorDrawable(Color.BLUE));
348 
349         inflatedView.setThumb(mockThumb);
350         verify(mockThumb, times(1))
351                 .setTintList(TestUtils.colorStateListOf(Color.WHITE));
352 
353         reset(mockThumb);
354         inflatedView.setThumbTintList(ColorStateList.valueOf(Color.RED));
355         verify(mockThumb, times(1))
356                 .setTintList(TestUtils.colorStateListOf(Color.RED));
357 
358         inflatedView.setThumbTintBlendMode(BlendMode.DST_ATOP);
359         assertEquals("Thumb tint mode changed correctly",
360                 BlendMode.DST_ATOP, inflatedView.getThumbTintBlendMode());
361 
362         reset(mockThumb);
363         inflatedView.setThumb(null);
364         inflatedView.setThumb(mockThumb);
365         verify(mockThumb, times(1))
366                 .setTintList(TestUtils.colorStateListOf(Color.RED));
367     }
368 
369     @UiThreadTest
370     @Test
testTickMarkTint()371     public void testTickMarkTint() {
372         AbsSeekBar inflatedView = (AbsSeekBar) mActivity.findViewById(R.id.tick_mark_tint);
373 
374         assertEquals("TickMark tint inflated correctly",
375                 Color.WHITE, inflatedView.getTickMarkTintList().getDefaultColor());
376         assertEquals("TickMark tint mode inflated correctly",
377                 PorterDuff.Mode.SRC_OVER, inflatedView.getTickMarkTintMode());
378 
379         Drawable mockTickMark = spy(new ColorDrawable(Color.BLUE));
380 
381         inflatedView.setTickMark(mockTickMark);
382         verify(mockTickMark, times(1))
383                 .setTintList(TestUtils.colorStateListOf(Color.WHITE));
384 
385         reset(mockTickMark);
386         inflatedView.setTickMarkTintList(ColorStateList.valueOf(Color.RED));
387         verify(mockTickMark, times(1))
388                 .setTintList(TestUtils.colorStateListOf(Color.RED));
389 
390         inflatedView.setTickMarkTintMode(PorterDuff.Mode.DARKEN);
391         assertEquals("TickMark tint mode changed correctly",
392                 PorterDuff.Mode.DARKEN, inflatedView.getTickMarkTintMode());
393 
394         reset(mockTickMark);
395         inflatedView.setTickMark(null);
396         inflatedView.setTickMark(mockTickMark);
397         verify(mockTickMark, times(1))
398                 .setTintList(TestUtils.colorStateListOf(Color.RED));
399     }
400 
401     @UiThreadTest
402     @Test
testTickMarkTintBlendMode()403     public void testTickMarkTintBlendMode() {
404         AbsSeekBar inflatedView = (AbsSeekBar) mActivity.findViewById(R.id.tick_mark_tint);
405 
406         assertEquals("TickMark tint inflated correctly",
407                 Color.WHITE, inflatedView.getTickMarkTintList().getDefaultColor());
408         assertEquals("TickMark tint mode inflated correctly",
409                 BlendMode.SRC_OVER, inflatedView.getTickMarkTintBlendMode());
410 
411         Drawable mockTickMark = spy(new ColorDrawable(Color.BLUE));
412 
413         inflatedView.setTickMark(mockTickMark);
414         verify(mockTickMark, times(1))
415                 .setTintList(TestUtils.colorStateListOf(Color.WHITE));
416 
417         reset(mockTickMark);
418         inflatedView.setTickMarkTintList(ColorStateList.valueOf(Color.RED));
419         verify(mockTickMark, times(1))
420                 .setTintList(TestUtils.colorStateListOf(Color.RED));
421 
422         inflatedView.setTickMarkTintBlendMode(BlendMode.DARKEN);
423         assertEquals("TickMark tint mode changed correctly",
424                 BlendMode.DARKEN, inflatedView.getTickMarkTintBlendMode());
425 
426         reset(mockTickMark);
427         inflatedView.setTickMark(null);
428         inflatedView.setTickMark(mockTickMark);
429         verify(mockTickMark, times(1))
430                 .setTintList(TestUtils.colorStateListOf(Color.RED));
431     }
432 
433     @Test
testAccessSplitTrack()434     public void testAccessSplitTrack() throws Throwable {
435         AbsSeekBar inflatedView = (AbsSeekBar) mActivity.findViewById(R.id.tick_mark_tint);
436 
437         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, inflatedView,
438                 () -> inflatedView.setSplitTrack(true));
439         assertTrue(inflatedView.getSplitTrack());
440 
441         WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, inflatedView,
442                 () -> inflatedView.setSplitTrack(false));
443         assertFalse(inflatedView.getSplitTrack());
444     }
445 
446     private static class MyAbsSeekBar extends AbsSeekBar {
MyAbsSeekBar(Context context)447         public MyAbsSeekBar(Context context) {
448             super(context);
449         }
450 
MyAbsSeekBar(Context context, AttributeSet attrs)451         public MyAbsSeekBar(Context context, AttributeSet attrs) {
452             super(context, attrs);
453         }
454 
MyAbsSeekBar(Context context, AttributeSet attrs, int defStyle)455         public MyAbsSeekBar(Context context, AttributeSet attrs, int defStyle) {
456             super(context, attrs, defStyle);
457         }
458 
MyAbsSeekBar(Context context, AttributeSet attrs, int defStyle, int defStyleRes)459         public MyAbsSeekBar(Context context, AttributeSet attrs, int defStyle, int defStyleRes) {
460             super(context, attrs, defStyle, defStyleRes);
461         }
462 
463         @Override
drawableStateChanged()464         protected void drawableStateChanged() {
465             super.drawableStateChanged();
466         }
467 
468         @Override
verifyDrawable(Drawable who)469         protected boolean verifyDrawable(Drawable who) {
470             return super.verifyDrawable(who);
471         }
472     }
473 }
474