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