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