1/*
2 * Copyright 2018 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
17package androidx.car.widget;
18
19import static android.support.test.espresso.Espresso.onView;
20import static android.support.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
21import static android.support.test.espresso.contrib.RecyclerViewActions.scrollToPosition;
22import static android.support.test.espresso.matcher.ViewMatchers.withId;
23
24import static org.hamcrest.core.Is.is;
25import static org.hamcrest.core.IsEqual.equalTo;
26import static org.junit.Assert.assertFalse;
27import static org.junit.Assert.assertThat;
28import static org.junit.Assert.assertTrue;
29
30import android.content.pm.PackageManager;
31import android.graphics.Color;
32import android.graphics.drawable.ColorDrawable;
33import android.graphics.drawable.Drawable;
34import android.support.test.espresso.UiController;
35import android.support.test.espresso.ViewAction;
36import android.support.test.filters.SmallTest;
37import android.support.test.rule.ActivityTestRule;
38import android.support.test.runner.AndroidJUnit4;
39import android.view.View;
40import android.view.ViewGroup;
41import android.widget.SeekBar;
42
43import androidx.car.test.R;
44
45import org.hamcrest.Matcher;
46import org.junit.Assume;
47import org.junit.Before;
48import org.junit.Rule;
49import org.junit.Test;
50import org.junit.runner.RunWith;
51
52import java.util.ArrayList;
53import java.util.Arrays;
54import java.util.List;
55
56/**
57 * Tests the layout configuration in {@link SeekbarListItem}.
58 */
59@RunWith(AndroidJUnit4.class)
60@SmallTest
61public class SeekbarListItemTest {
62
63    @Rule
64    public ActivityTestRule<PagedListViewTestActivity> mActivityRule =
65            new ActivityTestRule<>(PagedListViewTestActivity.class);
66
67    private PagedListViewTestActivity mActivity;
68    private PagedListView mPagedListView;
69
70    @Before
71    public void setUp() {
72        Assume.assumeTrue(isAutoDevice());
73        mActivity = mActivityRule.getActivity();
74        mPagedListView = mActivity.findViewById(R.id.paged_list_view);
75    }
76
77    private SeekbarListItem initSeekbarListItem() {
78        SeekbarListItem item = new SeekbarListItem(mActivity);
79        item.setMax(0);
80        item.setProgress(0);
81        item.setOnSeekBarChangeListener(null);
82        item.setText(null);
83        return item;
84    }
85
86    @Test
87    public void testOnlySliderIsVisibleInEmptyItem() {
88        SeekbarListItem item = initSeekbarListItem();
89
90        setupPagedListView(Arrays.asList(item));
91        verifyViewDefaultVisibility(mPagedListView.getRecyclerView().getLayoutManager()
92                .getChildAt(0));
93    }
94
95    @Test
96    public void testPrimaryActionVisible() {
97        SeekbarListItem item0 = initSeekbarListItem();
98        item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon);
99
100        SeekbarListItem item1 = initSeekbarListItem();
101        item1.setPrimaryActionIcon(new ColorDrawable(Color.BLACK));
102
103        setupPagedListView(Arrays.asList(item0, item1));
104
105        assertThat(getViewHolderAtPosition(0).getPrimaryIcon().getVisibility(),
106                is(equalTo(View.VISIBLE)));
107        assertThat(getViewHolderAtPosition(1).getPrimaryIcon().getVisibility(),
108                is(equalTo(View.VISIBLE)));
109    }
110
111    @Test
112    public void testSliderTextVisible() {
113        SeekbarListItem item = initSeekbarListItem();
114        item.setText("Text");
115
116        setupPagedListView(Arrays.asList(item));
117
118        assertThat(getViewHolderAtPosition(0).getText().getVisibility(), is(equalTo(View.VISIBLE)));
119    }
120
121    @Test
122    public void testSupplementalIconVisible() {
123        SeekbarListItem item = initSeekbarListItem();
124        item.setSupplementalIcon(android.R.drawable.sym_def_app_icon, false);
125
126        setupPagedListView(Arrays.asList(item));
127
128        assertThat(getViewHolderAtPosition(0).getSupplementalIcon().getVisibility(),
129                is(equalTo(View.VISIBLE)));
130        assertThat(getViewHolderAtPosition(0).getSupplementalIconDivider().getVisibility(),
131                is(equalTo(View.GONE)));
132    }
133
134    @Test
135    public void testSupplementalIconDividerVisible() {
136        SeekbarListItem item = initSeekbarListItem();
137        item.setSupplementalIcon(android.R.drawable.sym_def_app_icon, true);
138
139        setupPagedListView(Arrays.asList(item));
140
141        assertThat(getViewHolderAtPosition(0).getSupplementalIcon().getVisibility(),
142                is(equalTo(View.VISIBLE)));
143        assertThat(getViewHolderAtPosition(0).getSupplementalIconDivider().getVisibility(),
144                is(equalTo(View.VISIBLE)));
145    }
146
147    @Test
148    public void testSettingMax() {
149        final int max = 50;
150        SeekbarListItem item = new SeekbarListItem(mActivity);
151        item.setMax(max);
152
153        setupPagedListView(Arrays.asList(item));
154
155        assertThat(getViewHolderAtPosition(0).getSeekBar().getMax(), is(equalTo(max)));
156    }
157
158    @Test
159    public void testSettingProgress() {
160        final int progress = 100;
161        SeekbarListItem item = new SeekbarListItem(mActivity);
162        item.setMax(progress);
163        item.setProgress(progress);
164
165        setupPagedListView(Arrays.asList(item));
166
167        assertThat(getViewHolderAtPosition(0).getSeekBar().getProgress(), is(equalTo(progress)));
168    }
169
170    @Test
171    public void testSettingSecondaryProgress() {
172        final int secondaryProgress = 50;
173        SeekbarListItem item = new SeekbarListItem(mActivity);
174        item.setMax(secondaryProgress);
175        item.setSecondaryProgress(secondaryProgress);
176
177        setupPagedListView(Arrays.asList(item));
178
179        assertThat(getViewHolderAtPosition(0).getSeekBar().getSecondaryProgress(),
180                is(equalTo(secondaryProgress)));
181    }
182
183    @Test
184    public void testSettingOnSeekBarChangeListener() throws Throwable {
185        boolean[] changed = new boolean[]{false};
186
187        SeekbarListItem item = new SeekbarListItem(mActivity);
188        item.setMax(100);
189        item.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
190            @Override
191            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
192                changed[0] = true;
193            }
194            @Override
195            public void onStartTrackingTouch(SeekBar seekBar) {}
196
197            @Override
198            public void onStopTrackingTouch(SeekBar seekBar) {}
199        });
200
201        setupPagedListView(Arrays.asList(item));
202
203        mActivityRule.runOnUiThread(() -> getViewHolderAtPosition(0).getSeekBar().setProgress(50));
204        assertTrue(changed[0]);
205    }
206
207    @Test
208    public void testUpdatingProgress() {
209        final int progress = 50;
210        final int newProgress = 100;
211
212        SeekbarListItem item = new SeekbarListItem(mActivity);
213        item.setMax(newProgress);
214        item.setProgress(progress);
215        setupPagedListView(Arrays.asList(item));
216
217        item.setProgress(newProgress);
218        refreshUi();
219
220        assertThat(getViewHolderAtPosition(0).getSeekBar().getProgress(), is(equalTo(newProgress)));
221    }
222
223    @Test
224    public void testPrimaryIconIsNotClickableWithoutListener() {
225        SeekbarListItem item0 = initSeekbarListItem();
226        item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon);
227
228        SeekbarListItem item1 = initSeekbarListItem();
229        item1.setPrimaryActionIcon(new ColorDrawable(Color.BLACK));
230
231        setupPagedListView(Arrays.asList(item0, item1));
232
233        assertFalse(getViewHolderAtPosition(0).getPrimaryIcon().isClickable());
234        assertFalse(getViewHolderAtPosition(1).getPrimaryIcon().isClickable());
235    }
236
237    @Test
238    public void testClickingPrimaryActionIcon() {
239        boolean[] clicked = {false};
240        SeekbarListItem item = initSeekbarListItem();
241        item.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon);
242        item.setPrimaryActionIconListener(v -> clicked[0] = true);
243
244        setupPagedListView(Arrays.asList(item));
245
246        onView(withId(R.id.recycler_view)).perform(
247                actionOnItemAtPosition(0, clickChildViewWithId(R.id.primary_icon)));
248
249        assertTrue(clicked[0]);
250        assertTrue(getViewHolderAtPosition(0).getPrimaryIcon().isClickable());
251    }
252
253    @Test
254    public void testSupplementalIconNotClickableWithoutListener() {
255        SeekbarListItem item = initSeekbarListItem();
256        item.setSupplementalIcon(android.R.drawable.sym_def_app_icon, false);
257
258        setupPagedListView(Arrays.asList(item));
259
260        assertFalse(getViewHolderAtPosition(0).getSupplementalIcon().isClickable());
261    }
262
263    @Test
264    public void testClickingSupplementalIcon() {
265        boolean[] clicked = {false};
266        SeekbarListItem item = initSeekbarListItem();
267        item.setSupplementalIcon(android.R.drawable.sym_def_app_icon, false);
268        item.setSupplementalIconListener(v -> clicked[0] = true);
269
270        setupPagedListView(Arrays.asList(item));
271
272        onView(withId(R.id.recycler_view)).perform(
273                actionOnItemAtPosition(0, clickChildViewWithId(R.id.supplemental_icon)));
274
275        assertTrue(clicked[0]);
276    }
277
278    @Test
279    public void testPrimaryActionEmptyIconSpacing() {
280        SeekbarListItem item0 = initSeekbarListItem();
281        item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon);
282
283        SeekbarListItem item1 = initSeekbarListItem();
284        item1.setPrimaryActionIcon(new ColorDrawable(Color.BLACK));
285
286        SeekbarListItem item2 = initSeekbarListItem();
287        item2.setPrimaryActionEmptyIcon();
288
289        List<ListItem> items = Arrays.asList(item0, item1, item2);
290        setupPagedListView(items);
291
292        for (int i = 1; i < items.size(); i++) {
293            assertThat(getViewHolderAtPosition(i - 1).getSeekBar().getLeft(),
294                    is(equalTo(getViewHolderAtPosition(i).getSeekBar().getLeft())));
295        }
296    }
297
298    @Test
299    public void testSupplementalIconSpacingWithoutDivider() {
300        final boolean showDivider = false;
301        SeekbarListItem item0 = initSeekbarListItem();
302        item0.setSupplementalIcon(android.R.drawable.sym_def_app_icon, showDivider);
303
304        SeekbarListItem item1 = initSeekbarListItem();
305        item1.setSupplementalEmptyIcon(showDivider);
306
307        List<ListItem> items = Arrays.asList(item0, item1);
308        setupPagedListView(items);
309
310        for (int i = 1; i < items.size(); i++) {
311            assertThat(getViewHolderAtPosition(i - 1).getSeekBar().getRight(),
312                    is(equalTo(getViewHolderAtPosition(i).getSeekBar().getRight())));
313        }
314    }
315
316    @Test
317    public void testSupplementalIconSpacingWithDivider() {
318        final boolean showDivider = true;
319        SeekbarListItem item0 = initSeekbarListItem();
320        item0.setSupplementalIcon(android.R.drawable.sym_def_app_icon, showDivider);
321
322        SeekbarListItem item1 = initSeekbarListItem();
323        item1.setSupplementalEmptyIcon(showDivider);
324
325        List<ListItem> items = Arrays.asList(item0, item1);
326        setupPagedListView(items);
327
328        for (int i = 1; i < items.size(); i++) {
329            assertThat(getViewHolderAtPosition(i - 1).getSeekBar().getRight(),
330                    is(equalTo(getViewHolderAtPosition(i).getSeekBar().getRight())));
331        }
332    }
333
334    @Test
335    public void testSettingSupplementalIconWithDrawable() {
336        Drawable drawable = mActivity.getDrawable(android.R.drawable.sym_def_app_icon);
337        SeekbarListItem item = initSeekbarListItem();
338        item.setSupplementalIcon(drawable, false);
339
340        setupPagedListView(Arrays.asList(item));
341
342        assertThat(getViewHolderAtPosition(0).getSupplementalIcon().getDrawable(),
343                is(equalTo(drawable)));
344    }
345
346    private boolean isAutoDevice() {
347        PackageManager packageManager = mActivityRule.getActivity().getPackageManager();
348        return packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
349    }
350
351    private void setupPagedListView(List<? extends ListItem> items) {
352        ListItemProvider provider = new ListItemProvider.ListProvider(
353                new ArrayList<>(items));
354        try {
355            mActivityRule.runOnUiThread(() -> {
356                mPagedListView.setAdapter(new ListItemAdapter(mActivity, provider));
357            });
358        } catch (Throwable throwable) {
359            throwable.printStackTrace();
360            throw new RuntimeException(throwable);
361        }
362        // Wait for paged list view to layout by using espresso to scroll to a position.
363        onView(withId(R.id.recycler_view)).perform(scrollToPosition(0));
364    }
365
366    private void verifyViewDefaultVisibility(View view) {
367        if (view instanceof ViewGroup) {
368            ViewGroup viewGroup = (ViewGroup) view;
369            final int childCount = viewGroup.getChildCount();
370            for (int i = 0; i < childCount; i++) {
371                verifyViewDefaultVisibility(viewGroup.getChildAt(i));
372            }
373        } else if (view instanceof SeekBar) {
374            assertThat(view.getVisibility(), is(equalTo(View.VISIBLE)));
375        } else {
376            assertThat("Visibility of view "
377                            + mActivity.getResources().getResourceEntryName(view.getId())
378                            + " by default should be GONE.",
379                    view.getVisibility(), is(equalTo(View.GONE)));
380        }
381    }
382
383    private SeekbarListItem.ViewHolder getViewHolderAtPosition(int position) {
384        return (SeekbarListItem.ViewHolder) mPagedListView.getRecyclerView()
385                .findViewHolderForAdapterPosition(
386                        position);
387    }
388
389    private void refreshUi() {
390        try {
391            mActivityRule.runOnUiThread(() -> {
392                mPagedListView.getAdapter().notifyDataSetChanged();
393            });
394        } catch (Throwable throwable) {
395            throwable.printStackTrace();
396            throw new RuntimeException(throwable);
397        }
398        // Wait for paged list view to layout by using espresso to scroll to a position.
399        onView(withId(R.id.recycler_view)).perform(scrollToPosition(0));
400    }
401
402    private static ViewAction clickChildViewWithId(final int id) {
403        return new ViewAction() {
404            @Override
405            public Matcher<View> getConstraints() {
406                return null;
407            }
408
409            @Override
410            public String getDescription() {
411                return "Click on a child view with specific id.";
412            }
413
414            @Override
415            public void perform(UiController uiController, View view) {
416                View v = view.findViewById(id);
417                v.performClick();
418            }
419        };
420    }
421}
422