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