TextListItemTest.java revision 5dd6cdb91b24bb42fce840b3398b13f416a8c9d0
1/*
2 * Copyright 2017 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.action.ViewActions.click;
21import static android.support.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
22import static android.support.test.espresso.contrib.RecyclerViewActions.scrollToPosition;
23import static android.support.test.espresso.matcher.ViewMatchers.withId;
24
25import static org.hamcrest.Matchers.greaterThanOrEqualTo;
26import static org.hamcrest.core.Is.is;
27import static org.hamcrest.core.IsEqual.equalTo;
28import static org.hamcrest.number.IsCloseTo.closeTo;
29import static org.junit.Assert.assertFalse;
30import static org.junit.Assert.assertThat;
31import static org.junit.Assert.assertTrue;
32
33import android.graphics.drawable.Drawable;
34import android.support.test.InstrumentationRegistry;
35import android.support.test.espresso.UiController;
36import android.support.test.espresso.ViewAction;
37import android.support.test.filters.SmallTest;
38import android.support.test.rule.ActivityTestRule;
39import android.support.test.runner.AndroidJUnit4;
40import android.support.v7.widget.LinearLayoutManager;
41import android.view.View;
42import android.view.ViewGroup;
43import android.widget.RelativeLayout;
44
45import org.hamcrest.Matcher;
46import org.junit.Before;
47import org.junit.Rule;
48import org.junit.Test;
49import org.junit.runner.RunWith;
50
51import java.util.ArrayList;
52import java.util.Arrays;
53import java.util.List;
54
55import androidx.car.test.R;
56
57/**
58* Tests the layout configuration in {@link TextListItem}.
59 */
60@RunWith(AndroidJUnit4.class)
61@SmallTest
62public class TextListItemTest {
63
64    @Rule
65    public ActivityTestRule<PagedListViewTestActivity> mActivityRule =
66            new ActivityTestRule<>(PagedListViewTestActivity.class);
67
68    private PagedListViewTestActivity mActivity;
69    private PagedListView mPagedListView;
70
71    @Before
72    public void setUp() {
73        mActivity = mActivityRule.getActivity();
74        mPagedListView = mActivity.findViewById(R.id.paged_list_view);
75    }
76
77    private void setupPagedListView(List<TextListItem> items) {
78        ListItemProvider provider = new ListItemProvider.ListProvider(
79                new ArrayList<>(items));
80        try {
81            mActivityRule.runOnUiThread(() -> {
82                mPagedListView.setAdapter(new ListItemAdapter(mActivity, provider));
83            });
84        } catch (Throwable throwable) {
85            throwable.printStackTrace();
86            throw new RuntimeException(throwable);
87        }
88        // Wait for paged list view to layout by using espresso to scroll to a position.
89        onView(withId(R.id.recycler_view)).perform(scrollToPosition(0));
90    }
91
92    private static void verifyViewIsHidden(View view) {
93        if (view instanceof ViewGroup) {
94            ViewGroup viewGroup = (ViewGroup) view;
95            final int childCount = viewGroup.getChildCount();
96            for (int i = 0; i < childCount; i++) {
97                verifyViewIsHidden(viewGroup.getChildAt(i));
98            }
99        } else {
100            assertThat(view.getVisibility(), is(equalTo(View.GONE)));
101        }
102    }
103
104    private TextListItem.ViewHolder getViewHolderAtPosition(int position) {
105        return (TextListItem.ViewHolder) mPagedListView.getRecyclerView()
106                .findViewHolderForAdapterPosition(
107                position);
108    }
109
110    @Test
111    public void testEmptyItemHidesAllViews() {
112        TextListItem item = new TextListItem(mActivity);
113        setupPagedListView(Arrays.asList(item));
114        verifyViewIsHidden(mPagedListView.getRecyclerView().getLayoutManager().getChildAt(0));
115    }
116
117    @Test
118    public void testPrimaryActionVisible() {
119        TextListItem item0 = new TextListItem(mActivity);
120        item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true);
121
122        TextListItem item1 = new TextListItem(mActivity);
123        item1.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
124
125        List<TextListItem> items = Arrays.asList(item0, item1);
126        setupPagedListView(items);
127
128        assertThat(getViewHolderAtPosition(0).getPrimaryIcon().getVisibility(),
129                is(equalTo(View.VISIBLE)));
130        assertThat(getViewHolderAtPosition(1).getPrimaryIcon().getVisibility(),
131                is(equalTo(View.VISIBLE)));
132    }
133
134    @Test
135    public void testTextVisible() {
136        TextListItem item0 = new TextListItem(mActivity);
137        item0.setTitle("title");
138
139        TextListItem item1 = new TextListItem(mActivity);
140        item1.setBody("body");
141
142        List<TextListItem> items = Arrays.asList(item0, item1);
143        setupPagedListView(items);
144
145        assertThat(getViewHolderAtPosition(0).getTitle().getVisibility(),
146                is(equalTo(View.VISIBLE)));
147        assertThat(getViewHolderAtPosition(1).getBody().getVisibility(),
148                is(equalTo(View.VISIBLE)));
149    }
150
151    @Test
152    public void testSupplementalActionVisible() {
153        TextListItem item0 = new TextListItem(mActivity);
154        item0.setSupplementalIcon(android.R.drawable.sym_def_app_icon, true);
155
156        TextListItem item1 = new TextListItem(mActivity);
157        item1.setAction("text", true, v -> { /* Do nothing. */ });
158
159
160        TextListItem item2 = new TextListItem(mActivity);
161        item2.setActions("text", true, v -> { /* Do nothing. */ },
162                        "text", true, v -> { /* Do nothing. */ });
163
164        List<TextListItem> items = Arrays.asList(item0, item1, item2);
165        setupPagedListView(items);
166
167        TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
168        assertThat(viewHolder.getSupplementalIcon().getVisibility(), is(equalTo(View.VISIBLE)));
169        assertThat(viewHolder.getSupplementalIconDivider().getVisibility(),
170                is(equalTo(View.VISIBLE)));
171
172        viewHolder = getViewHolderAtPosition(1);
173        assertThat(viewHolder.getAction1().getVisibility(), is(equalTo(View.VISIBLE)));
174        assertThat(viewHolder.getAction1Divider().getVisibility(), is(equalTo(View.VISIBLE)));
175
176        viewHolder = getViewHolderAtPosition(2);
177        assertThat(viewHolder.getAction1().getVisibility(), is(equalTo(View.VISIBLE)));
178        assertThat(viewHolder.getAction1Divider().getVisibility(), is(equalTo(View.VISIBLE)));
179        assertThat(viewHolder.getAction2().getVisibility(), is(equalTo(View.VISIBLE)));
180        assertThat(viewHolder.getAction2Divider().getVisibility(), is(equalTo(View.VISIBLE)));
181    }
182
183    @Test
184    public void testSwitchVisibleAndCheckedState() {
185        TextListItem item0 = new TextListItem(mActivity);
186        item0.setSwitch(true, true, null);
187
188        TextListItem item1 = new TextListItem(mActivity);
189        item1.setSwitch(false, true, null);
190
191        List<TextListItem> items = Arrays.asList(item0, item1);
192        setupPagedListView(items);
193
194        TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
195        assertThat(viewHolder.getSwitch().getVisibility(), is(equalTo(View.VISIBLE)));
196        assertThat(viewHolder.getSwitch().isChecked(), is(equalTo(true)));
197        assertThat(viewHolder.getSwitchDivider().getVisibility(), is(equalTo(View.VISIBLE)));
198
199        viewHolder = getViewHolderAtPosition(1);
200        assertThat(viewHolder.getSwitch().getVisibility(), is(equalTo(View.VISIBLE)));
201        assertThat(viewHolder.getSwitch().isChecked(), is(equalTo(false)));
202        assertThat(viewHolder.getSwitchDivider().getVisibility(), is(equalTo(View.VISIBLE)));
203    }
204
205    @Test
206    public void testDividersAreOptional() {
207        TextListItem item0 = new TextListItem(mActivity);
208        item0.setSupplementalIcon(android.R.drawable.sym_def_app_icon, false);
209
210        TextListItem item1 = new TextListItem(mActivity);
211        item1.setAction("text", false, v -> { /* Do nothing. */ });
212
213        TextListItem item2 = new TextListItem(mActivity);
214        item2.setActions("text", false, v -> { /* Do nothing. */ },
215                "text", false, v -> { /* Do nothing. */ });
216
217        TextListItem item3 = new TextListItem(mActivity);
218        item3.setSwitch(true, false, null);
219
220        List<TextListItem> items = Arrays.asList(item0, item1, item2, item3);
221        setupPagedListView(items);
222
223        TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
224        assertThat(viewHolder.getSupplementalIcon().getVisibility(), is(equalTo(View.VISIBLE)));
225        assertThat(viewHolder.getSupplementalIconDivider().getVisibility(),
226                is(equalTo(View.GONE)));
227
228        viewHolder = getViewHolderAtPosition(1);
229        assertThat(viewHolder.getAction1().getVisibility(), is(equalTo(View.VISIBLE)));
230        assertThat(viewHolder.getAction1Divider().getVisibility(), is(equalTo(View.GONE)));
231
232        viewHolder = getViewHolderAtPosition(2);
233        assertThat(viewHolder.getAction1().getVisibility(), is(equalTo(View.VISIBLE)));
234        assertThat(viewHolder.getAction1Divider().getVisibility(), is(equalTo(View.GONE)));
235        assertThat(viewHolder.getAction2().getVisibility(), is(equalTo(View.VISIBLE)));
236        assertThat(viewHolder.getAction2Divider().getVisibility(), is(equalTo(View.GONE)));
237
238        viewHolder = getViewHolderAtPosition(3);
239        assertThat(viewHolder.getSwitch().getVisibility(), is(equalTo(View.VISIBLE)));
240        assertThat(viewHolder.getSwitchDivider().getVisibility(), is(equalTo(View.GONE)));
241    }
242
243    @Test
244    public void testTextStartMarginMatchesPrimaryActionType() {
245        TextListItem item0 = new TextListItem(mActivity);
246        item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true);
247
248        TextListItem item1 = new TextListItem(mActivity);
249        item1.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
250
251        TextListItem item2 = new TextListItem(mActivity);
252        item2.setPrimaryActionEmptyIcon();
253
254        TextListItem item3 = new TextListItem(mActivity);
255        item3.setPrimaryActionNoIcon();
256
257        List<TextListItem> items = Arrays.asList(item0, item1, item2, item3);
258        List<Integer> expectedStartMargin = Arrays.asList(R.dimen.car_keyline_4,
259                R.dimen.car_keyline_3, R.dimen.car_keyline_3, R.dimen.car_keyline_1);
260        setupPagedListView(items);
261
262        for (int i = 0; i < items.size(); i++) {
263            TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(i);
264
265            int expected = InstrumentationRegistry.getContext().getResources()
266                    .getDimensionPixelSize(expectedStartMargin.get(i));
267            assertThat(((ViewGroup.MarginLayoutParams) viewHolder.getTitle().getLayoutParams())
268                    .getMarginStart(), is(equalTo(expected)));
269            assertThat(((ViewGroup.MarginLayoutParams) viewHolder.getBody().getLayoutParams())
270                    .getMarginStart(), is(equalTo(expected)));
271        }
272    }
273
274    @Test
275    public void testItemWithOnlyTitleIsSingleLine() {
276        // Only space.
277        TextListItem item0 = new TextListItem(mActivity);
278        item0.setTitle(" ");
279
280        // Underscore.
281        TextListItem item1 = new TextListItem(mActivity);
282        item1.setTitle("______");
283
284        TextListItem item2 = new TextListItem(mActivity);
285        item2.setTitle("ALL UPPER CASE");
286
287        // String wouldn't fit in one line.
288        TextListItem item3 = new TextListItem(mActivity);
289        item3.setTitle(InstrumentationRegistry.getContext().getResources().getString(
290                R.string.over_120_chars));
291
292        List<TextListItem> items = Arrays.asList(item0, item1, item2, item3);
293        setupPagedListView(items);
294
295        double singleLineHeight = InstrumentationRegistry.getContext().getResources().getDimension(
296                R.dimen.car_single_line_list_item_height);
297
298        LinearLayoutManager layoutManager =
299                (LinearLayoutManager) mPagedListView.getRecyclerView().getLayoutManager();
300        for (int i = 0; i < items.size(); i++) {
301            assertThat((double) layoutManager.findViewByPosition(i).getHeight(),
302                    is(closeTo(singleLineHeight, 1.0d)));
303        }
304    }
305
306    @Test
307    public void testItemWithBodyTextIsAtLeastDoubleLine() {
308        // Only space.
309        TextListItem item0 = new TextListItem(mActivity);
310        item0.setBody(" ");
311
312        // Underscore.
313        TextListItem item1 = new TextListItem(mActivity);
314        item1.setBody("____");
315
316        // String wouldn't fit in one line.
317        TextListItem item2 = new TextListItem(mActivity);
318        item2.setBody(InstrumentationRegistry.getContext().getResources().getString(
319                R.string.over_120_chars));
320
321        List<TextListItem> items = Arrays.asList(item0, item1, item2);
322        setupPagedListView(items);
323
324        final int doubleLineHeight =
325                (int) InstrumentationRegistry.getContext().getResources().getDimension(
326                        R.dimen.car_double_line_list_item_height);
327
328        LinearLayoutManager layoutManager =
329                (LinearLayoutManager) mPagedListView.getRecyclerView().getLayoutManager();
330        for (int i = 0; i < items.size(); i++) {
331            assertThat(layoutManager.findViewByPosition(i).getHeight(),
332                    is(greaterThanOrEqualTo(doubleLineHeight)));
333        }
334    }
335
336    @Test
337    public void testBodyTextLengthLimit() {
338        final String longText = InstrumentationRegistry.getContext().getResources().getString(
339                R.string.over_120_chars);
340        final int limit = InstrumentationRegistry.getContext().getResources().getInteger(
341                R.integer.car_list_item_text_length_limit);
342
343        TextListItem item0 = new TextListItem(mActivity);
344        item0.setBody(longText);
345
346        List<TextListItem> items = Arrays.asList(item0);
347        setupPagedListView(items);
348
349        // + 1 for appended ellipsis.
350        assertThat(getViewHolderAtPosition(0).getBody().getText().length(),
351                is(equalTo(limit + 1)));
352    }
353
354    @Test
355    public void testPrimaryIconDrawable() {
356        Drawable drawable = InstrumentationRegistry.getContext().getResources().getDrawable(
357                android.R.drawable.sym_def_app_icon, null);
358
359        TextListItem item0 = new TextListItem(mActivity);
360        item0.setPrimaryActionIcon(drawable, true);
361
362        List<TextListItem> items = Arrays.asList(item0);
363        setupPagedListView(items);
364
365        assertTrue(getViewHolderAtPosition(0).getPrimaryIcon().getDrawable().getConstantState()
366                .equals(drawable.getConstantState()));
367    }
368
369    @Test
370    public void testLargePrimaryIconHasNoStartMargin() {
371        TextListItem item0 = new TextListItem(mActivity);
372        item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true);
373
374        List<TextListItem> items = Arrays.asList(item0);
375        setupPagedListView(items);
376
377        TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
378        assertThat(((ViewGroup.MarginLayoutParams) viewHolder.getPrimaryIcon().getLayoutParams())
379                .getMarginStart(), is(equalTo(0)));
380    }
381
382    @Test
383    public void testSmallPrimaryIconStartMargin() {
384        TextListItem item0 = new TextListItem(mActivity);
385        item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
386
387        List<TextListItem> items = Arrays.asList(item0);
388        setupPagedListView(items);
389
390        int expected = InstrumentationRegistry.getContext().getResources().getDimensionPixelSize(
391                R.dimen.car_keyline_1);
392
393        TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
394        assertThat(((ViewGroup.MarginLayoutParams) viewHolder.getPrimaryIcon().getLayoutParams())
395                .getMarginStart(), is(equalTo(expected)));
396    }
397
398    @Test
399    public void testSmallPrimaryIconTopMarginRemainsTheSameRegardlessOfTextLength() {
400        final String longText = InstrumentationRegistry.getContext().getResources().getString(
401                R.string.over_120_chars);
402
403        // Single line item.
404        TextListItem item0 = new TextListItem(mActivity);
405        item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
406        item0.setTitle("one line text");
407
408        // Double line item with one line text.
409        TextListItem item1 = new TextListItem(mActivity);
410        item1.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
411        item1.setTitle("one line text");
412        item1.setBody("one line text");
413
414        // Double line item with long text.
415        TextListItem item2 = new TextListItem(mActivity);
416        item2.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
417        item2.setTitle("one line text");
418        item2.setBody(longText);
419
420        // Body text only - long text.
421        TextListItem item3 = new TextListItem(mActivity);
422        item3.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
423        item3.setBody(longText);
424
425        // Body text only - one line text.
426        TextListItem item4 = new TextListItem(mActivity);
427        item4.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
428        item4.setBody("one line text");
429
430        List<TextListItem> items = Arrays.asList(item0, item1, item2, item3, item4);
431        setupPagedListView(items);
432
433        for (int i = 1; i < items.size(); i++) {
434            onView(withId(R.id.recycler_view)).perform(scrollToPosition(i));
435            // Implementation uses integer division so it may be off by 1 vs centered vertically.
436            assertThat((double) getViewHolderAtPosition(i - 1).getPrimaryIcon().getTop(),
437                    is(closeTo(
438                    (double) getViewHolderAtPosition(i).getPrimaryIcon().getTop(), 1.0d)));
439        }
440    }
441
442    @Test
443    public void testClickingPrimaryActionIsSeparateFromSupplementalAction() {
444        final boolean[] clicked = {false, false};
445
446        TextListItem item0 = new TextListItem(mActivity);
447        item0.setOnClickListener(v -> clicked[0] = true);
448        item0.setSupplementalIcon(android.R.drawable.sym_def_app_icon, true,
449                v -> clicked[1] = true);
450
451        List<TextListItem> items = Arrays.asList(item0);
452        setupPagedListView(items);
453
454        onView(withId(R.id.recycler_view)).perform(actionOnItemAtPosition(0, click()));
455        assertTrue(clicked[0]);
456        assertFalse(clicked[1]);
457
458        onView(withId(R.id.recycler_view)).perform(
459                actionOnItemAtPosition(0, clickChildViewWithId(R.id.supplemental_icon)));
460        assertTrue(clicked[1]);
461    }
462
463    @Test
464    public void testClickingSupplementalIcon() {
465        final boolean[] clicked = {false};
466
467        TextListItem item0 = new TextListItem(mActivity);
468        item0.setSupplementalIcon(android.R.drawable.sym_def_app_icon, true,
469                v -> clicked[0] = true);
470
471        List<TextListItem> items = Arrays.asList(item0);
472        setupPagedListView(items);
473
474        onView(withId(R.id.recycler_view)).perform(
475                actionOnItemAtPosition(0, clickChildViewWithId(R.id.supplemental_icon)));
476        assertTrue(clicked[0]);
477    }
478
479    @Test
480    public void testSupplementalIconWithoutClickListenerIsNotClickable() {
481        TextListItem item0 = new TextListItem(mActivity);
482        item0.setSupplementalIcon(android.R.drawable.sym_def_app_icon, true);
483
484        List<TextListItem> items = Arrays.asList(item0);
485        setupPagedListView(items);
486
487        TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
488        assertFalse(viewHolder.getSupplementalIcon().isClickable());
489    }
490
491    @Test
492    public void testCheckingSwitch() {
493        final boolean[] clicked = {false, false};
494
495        TextListItem item0 = new TextListItem(mActivity);
496        item0.setSwitch(false, false, (button, isChecked) -> {
497            // Initial value is false.
498            assertTrue(isChecked);
499            clicked[0] = true;
500        });
501
502        TextListItem item1 = new TextListItem(mActivity);
503        item1.setSwitch(true, false, (button, isChecked) -> {
504            // Initial value is true.
505            assertFalse(isChecked);
506            clicked[1] = true;
507        });
508
509        List<TextListItem> items = Arrays.asList(item0, item1);
510        setupPagedListView(items);
511
512        onView(withId(R.id.recycler_view)).perform(
513                actionOnItemAtPosition(0, clickChildViewWithId(R.id.switch_widget)));
514        assertTrue(clicked[0]);
515
516        onView(withId(R.id.recycler_view)).perform(
517                actionOnItemAtPosition(1, clickChildViewWithId(R.id.switch_widget)));
518        assertTrue(clicked[1]);
519    }
520
521    @Test
522    public void testClickingSupplementalAction() {
523        final boolean[] clicked = {false};
524
525        TextListItem item0 = new TextListItem(mActivity);
526        item0.setAction("action", true, v -> clicked[0] = true);
527
528        List<TextListItem> items = Arrays.asList(item0);
529        setupPagedListView(items);
530
531        onView(withId(R.id.recycler_view)).perform(
532                actionOnItemAtPosition(0, clickChildViewWithId(R.id.action1)));
533        assertTrue(clicked[0]);
534    }
535
536    @Test
537    public void testClickingBothSupplementalActions() {
538        final boolean[] clicked = {false, false};
539
540        TextListItem item0 = new TextListItem(mActivity);
541        item0.setActions("action 1", true, v -> clicked[0] = true,
542                        "action 2", true, v -> clicked[1] = true);
543
544        List<TextListItem> items = Arrays.asList(item0);
545        setupPagedListView(items);
546
547        onView(withId(R.id.recycler_view)).perform(
548                actionOnItemAtPosition(0, clickChildViewWithId(R.id.action1)));
549        assertTrue(clicked[0]);
550        assertFalse(clicked[1]);
551
552        onView(withId(R.id.recycler_view)).perform(
553                actionOnItemAtPosition(0, clickChildViewWithId(R.id.action2)));
554        assertTrue(clicked[1]);
555    }
556
557    @Test
558    public void testCustomViewBinderBindsLast() {
559        final String updatedTitle = "updated title";
560
561        TextListItem item0 = new TextListItem(mActivity);
562        item0.setTitle("original title");
563        item0.addViewBinder((viewHolder) -> viewHolder.getTitle().setText(updatedTitle));
564
565        List<TextListItem> items = Arrays.asList(item0);
566        setupPagedListView(items);
567
568        TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
569        assertThat(viewHolder.getTitle().getText(), is(equalTo(updatedTitle)));
570    }
571
572    @Test
573    public void testCustomViewBinderOnUnusedViewsHasNoEffect() {
574        TextListItem item0 = new TextListItem(mActivity);
575        item0.addViewBinder((viewHolder) -> viewHolder.getBody().setText("text"));
576
577        List<TextListItem> items = Arrays.asList(item0);
578        setupPagedListView(items);
579
580        TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
581        assertThat(viewHolder.getBody().getVisibility(), is(equalTo(View.GONE)));
582        // Custom binder interacts with body but has no effect.
583        // Expect card height to remain single line.
584        assertThat((double) viewHolder.itemView.getHeight(), is(closeTo(
585                InstrumentationRegistry.getContext().getResources().getDimension(
586                        R.dimen.car_single_line_list_item_height), 1.0d)));
587    }
588
589    @Test
590    public void testSettingTitleOrBodyAsPrimaryText() {
591        // Create 2 items, one with Title as primary (default) and one with Body.
592        // The primary text, regardless of view, should have consistent look (as primary).
593        TextListItem item0 = new TextListItem(mActivity);
594        item0.setTitle("title");
595        item0.setBody("body");
596
597        TextListItem item1 = new TextListItem(mActivity);
598        item1.setTitle("title");
599        item1.setBody("body", true);
600
601        List<TextListItem> items = Arrays.asList(item0, item1);
602        setupPagedListView(items);
603
604        TextListItem.ViewHolder titlePrimary = getViewHolderAtPosition(0);
605        TextListItem.ViewHolder bodyPrimary = getViewHolderAtPosition(1);
606        assertThat(titlePrimary.getTitle().getTextSize(),
607                is(equalTo(bodyPrimary.getBody().getTextSize())));
608        assertThat(titlePrimary.getTitle().getTextColors(),
609                is(equalTo(bodyPrimary.getBody().getTextColors())));
610    }
611
612    @Test
613    public void testNoCarriedOverLayoutParamsForTextView() throws Throwable {
614        TextListItem singleLine = new TextListItem(mActivity);
615        singleLine.setTitle("title");
616
617        setupPagedListView(Arrays.asList(singleLine));
618
619        // Manually rebind the view holder of a single line item to a double line item.
620        TextListItem doubleLine = new TextListItem(mActivity);
621        doubleLine.setTitle("title");
622        doubleLine.setBody("body");
623        TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
624        mActivityRule.runOnUiThread(() -> doubleLine.bind(viewHolder));
625
626        RelativeLayout.LayoutParams titleLayoutParams =
627                (RelativeLayout.LayoutParams) viewHolder.getTitle().getLayoutParams();
628        RelativeLayout.LayoutParams bodyLayoutParams =
629                (RelativeLayout.LayoutParams) viewHolder.getTitle().getLayoutParams();
630        assertThat(titleLayoutParams.getRule(RelativeLayout.CENTER_VERTICAL), is(equalTo(0)));
631        assertThat(bodyLayoutParams.getRule(RelativeLayout.CENTER_VERTICAL), is(equalTo(0)));
632    }
633
634    @Test
635    public void testNoCarriedOverLayoutParamsForPrimaryIcon() throws Throwable {
636        TextListItem smallIcon = new TextListItem(mActivity);
637        smallIcon.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false);
638        smallIcon.setBody("body");  // Small icon of items with body text should use top margin.
639        setupPagedListView(Arrays.asList(smallIcon));
640
641        // Manually rebind the view holder.
642        TextListItem largeIcon = new TextListItem(mActivity);
643        largeIcon.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true);
644        TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
645        mActivityRule.runOnUiThread(() -> largeIcon.bind(viewHolder));
646
647        RelativeLayout.LayoutParams iconLayoutParams =
648                (RelativeLayout.LayoutParams) viewHolder.getPrimaryIcon().getLayoutParams();
649        assertThat(iconLayoutParams.getRule(RelativeLayout.CENTER_VERTICAL),
650                is(equalTo(RelativeLayout.TRUE)));
651        assertThat(iconLayoutParams.topMargin, is(equalTo(0)));
652    }
653
654    @Test
655    public void testUpdateItem() throws Throwable {
656        TextListItem item = new TextListItem(mActivity);
657        setupPagedListView(Arrays.asList(item));
658
659        String title = "updated title";
660        item.setTitle(title);
661        mActivityRule.runOnUiThread(() -> mPagedListView.getAdapter().notifyItemChanged(0));
662
663        // Wait for paged list view to layout by using espresso to scroll to a position.
664        onView(withId(R.id.recycler_view)).perform(scrollToPosition(0));
665
666        TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0);
667        assertThat(viewHolder.getTitle().getText(), is(equalTo(title)));
668    }
669
670    private static ViewAction clickChildViewWithId(final int id) {
671        return new ViewAction() {
672            @Override
673            public Matcher<View> getConstraints() {
674                return null;
675            }
676
677            @Override
678            public String getDescription() {
679                return "Click on a child view with specific id.";
680            }
681
682            @Override
683            public void perform(UiController uiController, View view) {
684                View v = view.findViewById(id);
685                v.performClick();
686            }
687        };
688    }
689}
690