TextListItemTest.java revision 65ec7f1338516c11ea1442912a22d50033fe8fb8
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 testSetSupplementalActionWithDrawable() { 216 Drawable drawable = mActivity.getDrawable(android.R.drawable.sym_def_app_icon); 217 TextListItem item = new TextListItem(mActivity); 218 item.setSupplementalIcon(drawable, true); 219 220 setupPagedListView(Arrays.asList(item)); 221 222 assertThat(getViewHolderAtPosition(0).getSupplementalIcon().getDrawable(), 223 is(equalTo(drawable))); 224 } 225 226 @Test 227 public void testSwitchVisibleAndCheckedState() { 228 TextListItem item0 = new TextListItem(mActivity); 229 item0.setSwitch(true, true, null); 230 231 TextListItem item1 = new TextListItem(mActivity); 232 item1.setSwitch(false, true, null); 233 234 List<TextListItem> items = Arrays.asList(item0, item1); 235 setupPagedListView(items); 236 237 TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0); 238 assertThat(viewHolder.getSwitch().getVisibility(), is(equalTo(View.VISIBLE))); 239 assertThat(viewHolder.getSwitch().isChecked(), is(equalTo(true))); 240 assertThat(viewHolder.getSwitchDivider().getVisibility(), is(equalTo(View.VISIBLE))); 241 242 viewHolder = getViewHolderAtPosition(1); 243 assertThat(viewHolder.getSwitch().getVisibility(), is(equalTo(View.VISIBLE))); 244 assertThat(viewHolder.getSwitch().isChecked(), is(equalTo(false))); 245 assertThat(viewHolder.getSwitchDivider().getVisibility(), is(equalTo(View.VISIBLE))); 246 } 247 248 @Test 249 public void testSetSwitchState() { 250 TextListItem item0 = new TextListItem(mActivity); 251 item0.setSwitch(true, true, null); 252 253 setupPagedListView(Arrays.asList(item0)); 254 255 item0.setSwitchState(false); 256 257 refreshUi(); 258 259 TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0); 260 assertThat(viewHolder.getSwitch().getVisibility(), is(equalTo(View.VISIBLE))); 261 assertThat(viewHolder.getSwitch().isChecked(), is(equalTo(false))); 262 } 263 264 @Test 265 public void testSetSwitchStateHasNoEffectIfSwitchIsNotEnabled() { 266 TextListItem item0 = new TextListItem(mActivity); 267 setupPagedListView(Arrays.asList(item0)); 268 269 item0.setSwitchState(false); 270 271 refreshUi(); 272 273 TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0); 274 assertThat(viewHolder.getSwitch().getVisibility(), is(not(equalTo(View.VISIBLE)))); 275 } 276 277 @Test 278 public void testDividersAreOptional() { 279 TextListItem item0 = new TextListItem(mActivity); 280 item0.setSupplementalIcon(android.R.drawable.sym_def_app_icon, false); 281 282 TextListItem item1 = new TextListItem(mActivity); 283 item1.setAction("text", false, v -> { /* Do nothing. */ }); 284 285 TextListItem item2 = new TextListItem(mActivity); 286 item2.setActions("text", false, v -> { /* Do nothing. */ }, 287 "text", false, v -> { /* Do nothing. */ }); 288 289 TextListItem item3 = new TextListItem(mActivity); 290 item3.setSwitch(true, false, null); 291 292 List<TextListItem> items = Arrays.asList(item0, item1, item2, item3); 293 setupPagedListView(items); 294 295 TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0); 296 assertThat(viewHolder.getSupplementalIcon().getVisibility(), is(equalTo(View.VISIBLE))); 297 assertThat(viewHolder.getSupplementalIconDivider().getVisibility(), 298 is(equalTo(View.GONE))); 299 300 viewHolder = getViewHolderAtPosition(1); 301 assertThat(viewHolder.getAction1().getVisibility(), is(equalTo(View.VISIBLE))); 302 assertThat(viewHolder.getAction1Divider().getVisibility(), is(equalTo(View.GONE))); 303 304 viewHolder = getViewHolderAtPosition(2); 305 assertThat(viewHolder.getAction1().getVisibility(), is(equalTo(View.VISIBLE))); 306 assertThat(viewHolder.getAction1Divider().getVisibility(), is(equalTo(View.GONE))); 307 assertThat(viewHolder.getAction2().getVisibility(), is(equalTo(View.VISIBLE))); 308 assertThat(viewHolder.getAction2Divider().getVisibility(), is(equalTo(View.GONE))); 309 310 viewHolder = getViewHolderAtPosition(3); 311 assertThat(viewHolder.getSwitch().getVisibility(), is(equalTo(View.VISIBLE))); 312 assertThat(viewHolder.getSwitchDivider().getVisibility(), is(equalTo(View.GONE))); 313 } 314 315 @Test 316 public void testTextStartMarginMatchesPrimaryActionType() { 317 TextListItem item0 = new TextListItem(mActivity); 318 item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true); 319 320 TextListItem item1 = new TextListItem(mActivity); 321 item1.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false); 322 323 TextListItem item2 = new TextListItem(mActivity); 324 item2.setPrimaryActionEmptyIcon(); 325 326 TextListItem item3 = new TextListItem(mActivity); 327 item3.setPrimaryActionNoIcon(); 328 329 List<TextListItem> items = Arrays.asList(item0, item1, item2, item3); 330 List<Integer> expectedStartMargin = Arrays.asList(R.dimen.car_keyline_4, 331 R.dimen.car_keyline_3, R.dimen.car_keyline_3, R.dimen.car_keyline_1); 332 setupPagedListView(items); 333 334 for (int i = 0; i < items.size(); i++) { 335 TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(i); 336 337 int expected = InstrumentationRegistry.getContext().getResources() 338 .getDimensionPixelSize(expectedStartMargin.get(i)); 339 assertThat(((ViewGroup.MarginLayoutParams) viewHolder.getTitle().getLayoutParams()) 340 .getMarginStart(), is(equalTo(expected))); 341 assertThat(((ViewGroup.MarginLayoutParams) viewHolder.getBody().getLayoutParams()) 342 .getMarginStart(), is(equalTo(expected))); 343 } 344 } 345 346 @Test 347 public void testItemWithOnlyTitleIsSingleLine() { 348 // Only space. 349 TextListItem item0 = new TextListItem(mActivity); 350 item0.setTitle(" "); 351 352 // Underscore. 353 TextListItem item1 = new TextListItem(mActivity); 354 item1.setTitle("______"); 355 356 TextListItem item2 = new TextListItem(mActivity); 357 item2.setTitle("ALL UPPER CASE"); 358 359 // String wouldn't fit in one line. 360 TextListItem item3 = new TextListItem(mActivity); 361 item3.setTitle(InstrumentationRegistry.getContext().getResources().getString( 362 R.string.over_uxr_text_length_limit)); 363 364 List<TextListItem> items = Arrays.asList(item0, item1, item2, item3); 365 setupPagedListView(items); 366 367 double singleLineHeight = InstrumentationRegistry.getContext().getResources().getDimension( 368 R.dimen.car_single_line_list_item_height); 369 370 LinearLayoutManager layoutManager = 371 (LinearLayoutManager) mPagedListView.getRecyclerView().getLayoutManager(); 372 for (int i = 0; i < items.size(); i++) { 373 assertThat((double) layoutManager.findViewByPosition(i).getHeight(), 374 is(closeTo(singleLineHeight, 1.0d))); 375 } 376 } 377 378 @Test 379 public void testItemWithBodyTextIsAtLeastDoubleLine() { 380 // Only space. 381 TextListItem item0 = new TextListItem(mActivity); 382 item0.setBody(" "); 383 384 // Underscore. 385 TextListItem item1 = new TextListItem(mActivity); 386 item1.setBody("____"); 387 388 // String wouldn't fit in one line. 389 TextListItem item2 = new TextListItem(mActivity); 390 item2.setBody(InstrumentationRegistry.getContext().getResources().getString( 391 R.string.over_uxr_text_length_limit)); 392 393 List<TextListItem> items = Arrays.asList(item0, item1, item2); 394 setupPagedListView(items); 395 396 final int doubleLineHeight = 397 (int) InstrumentationRegistry.getContext().getResources().getDimension( 398 R.dimen.car_double_line_list_item_height); 399 400 LinearLayoutManager layoutManager = 401 (LinearLayoutManager) mPagedListView.getRecyclerView().getLayoutManager(); 402 for (int i = 0; i < items.size(); i++) { 403 assertThat(layoutManager.findViewByPosition(i).getHeight(), 404 is(greaterThanOrEqualTo(doubleLineHeight))); 405 } 406 } 407 408 @Test 409 public void testPrimaryIconDrawable() { 410 Drawable drawable = InstrumentationRegistry.getContext().getResources().getDrawable( 411 android.R.drawable.sym_def_app_icon, null); 412 413 TextListItem item0 = new TextListItem(mActivity); 414 item0.setPrimaryActionIcon(drawable, true); 415 416 List<TextListItem> items = Arrays.asList(item0); 417 setupPagedListView(items); 418 419 assertTrue(getViewHolderAtPosition(0).getPrimaryIcon().getDrawable().getConstantState() 420 .equals(drawable.getConstantState())); 421 } 422 423 @Test 424 public void testLargePrimaryIconHasNoStartMargin() { 425 TextListItem item0 = new TextListItem(mActivity); 426 item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true); 427 428 List<TextListItem> items = Arrays.asList(item0); 429 setupPagedListView(items); 430 431 TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0); 432 assertThat(((ViewGroup.MarginLayoutParams) viewHolder.getPrimaryIcon().getLayoutParams()) 433 .getMarginStart(), is(equalTo(0))); 434 } 435 436 @Test 437 public void testSmallPrimaryIconStartMargin() { 438 TextListItem item0 = new TextListItem(mActivity); 439 item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false); 440 441 List<TextListItem> items = Arrays.asList(item0); 442 setupPagedListView(items); 443 444 int expected = InstrumentationRegistry.getContext().getResources().getDimensionPixelSize( 445 R.dimen.car_keyline_1); 446 447 TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0); 448 assertThat(((ViewGroup.MarginLayoutParams) viewHolder.getPrimaryIcon().getLayoutParams()) 449 .getMarginStart(), is(equalTo(expected))); 450 } 451 452 @Test 453 public void testSmallPrimaryIconTopMarginRemainsTheSameRegardlessOfTextLength() { 454 final String longText = InstrumentationRegistry.getContext().getResources().getString( 455 R.string.over_uxr_text_length_limit); 456 457 // Single line item. 458 TextListItem item0 = new TextListItem(mActivity); 459 item0.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false); 460 item0.setTitle("one line text"); 461 462 // Double line item with one line text. 463 TextListItem item1 = new TextListItem(mActivity); 464 item1.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false); 465 item1.setTitle("one line text"); 466 item1.setBody("one line text"); 467 468 // Double line item with long text. 469 TextListItem item2 = new TextListItem(mActivity); 470 item2.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false); 471 item2.setTitle("one line text"); 472 item2.setBody(longText); 473 474 // Body text only - long text. 475 TextListItem item3 = new TextListItem(mActivity); 476 item3.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false); 477 item3.setBody(longText); 478 479 // Body text only - one line text. 480 TextListItem item4 = new TextListItem(mActivity); 481 item4.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false); 482 item4.setBody("one line text"); 483 484 List<TextListItem> items = Arrays.asList(item0, item1, item2, item3, item4); 485 setupPagedListView(items); 486 487 for (int i = 1; i < items.size(); i++) { 488 onView(withId(R.id.recycler_view)).perform(scrollToPosition(i)); 489 // Implementation uses integer division so it may be off by 1 vs centered vertically. 490 assertThat((double) getViewHolderAtPosition(i - 1).getPrimaryIcon().getTop(), 491 is(closeTo( 492 (double) getViewHolderAtPosition(i).getPrimaryIcon().getTop(), 1.0d))); 493 } 494 } 495 496 @Test 497 public void testClickingPrimaryActionIsSeparateFromSupplementalAction() { 498 final boolean[] clicked = {false, false}; 499 500 TextListItem item0 = new TextListItem(mActivity); 501 item0.setOnClickListener(v -> clicked[0] = true); 502 item0.setSupplementalIcon(android.R.drawable.sym_def_app_icon, true, 503 v -> clicked[1] = true); 504 505 List<TextListItem> items = Arrays.asList(item0); 506 setupPagedListView(items); 507 508 onView(withId(R.id.recycler_view)).perform(actionOnItemAtPosition(0, click())); 509 assertTrue(clicked[0]); 510 assertFalse(clicked[1]); 511 512 onView(withId(R.id.recycler_view)).perform( 513 actionOnItemAtPosition(0, clickChildViewWithId(R.id.supplemental_icon))); 514 assertTrue(clicked[1]); 515 } 516 517 @Test 518 public void testClickingSupplementalIcon() { 519 final boolean[] clicked = {false}; 520 521 TextListItem item0 = new TextListItem(mActivity); 522 item0.setSupplementalIcon(android.R.drawable.sym_def_app_icon, true, 523 v -> clicked[0] = true); 524 525 List<TextListItem> items = Arrays.asList(item0); 526 setupPagedListView(items); 527 528 onView(withId(R.id.recycler_view)).perform( 529 actionOnItemAtPosition(0, clickChildViewWithId(R.id.supplemental_icon))); 530 assertTrue(clicked[0]); 531 } 532 533 @Test 534 public void testSupplementalIconWithoutClickListenerIsNotClickable() { 535 TextListItem item0 = new TextListItem(mActivity); 536 item0.setSupplementalIcon(android.R.drawable.sym_def_app_icon, true); 537 538 List<TextListItem> items = Arrays.asList(item0); 539 setupPagedListView(items); 540 541 TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0); 542 assertFalse(viewHolder.getSupplementalIcon().isClickable()); 543 } 544 545 @Test 546 public void testCheckingSwitch() { 547 final boolean[] clicked = {false, false}; 548 549 TextListItem item0 = new TextListItem(mActivity); 550 item0.setSwitch(false, false, (button, isChecked) -> { 551 // Initial value is false. 552 assertTrue(isChecked); 553 clicked[0] = true; 554 }); 555 556 TextListItem item1 = new TextListItem(mActivity); 557 item1.setSwitch(true, false, (button, isChecked) -> { 558 // Initial value is true. 559 assertFalse(isChecked); 560 clicked[1] = true; 561 }); 562 563 List<TextListItem> items = Arrays.asList(item0, item1); 564 setupPagedListView(items); 565 566 onView(withId(R.id.recycler_view)).perform( 567 actionOnItemAtPosition(0, clickChildViewWithId(R.id.switch_widget))); 568 assertTrue(clicked[0]); 569 570 onView(withId(R.id.recycler_view)).perform( 571 actionOnItemAtPosition(1, clickChildViewWithId(R.id.switch_widget))); 572 assertTrue(clicked[1]); 573 } 574 575 @Test 576 public void testClickingSupplementalAction() { 577 final boolean[] clicked = {false}; 578 579 TextListItem item0 = new TextListItem(mActivity); 580 item0.setAction("action", true, v -> clicked[0] = true); 581 582 List<TextListItem> items = Arrays.asList(item0); 583 setupPagedListView(items); 584 585 onView(withId(R.id.recycler_view)).perform( 586 actionOnItemAtPosition(0, clickChildViewWithId(R.id.action1))); 587 assertTrue(clicked[0]); 588 } 589 590 @Test 591 public void testClickingBothSupplementalActions() { 592 final boolean[] clicked = {false, false}; 593 594 TextListItem item0 = new TextListItem(mActivity); 595 item0.setActions("action 1", true, v -> clicked[0] = true, 596 "action 2", true, v -> clicked[1] = true); 597 598 List<TextListItem> items = Arrays.asList(item0); 599 setupPagedListView(items); 600 601 onView(withId(R.id.recycler_view)).perform( 602 actionOnItemAtPosition(0, clickChildViewWithId(R.id.action1))); 603 assertTrue(clicked[0]); 604 assertFalse(clicked[1]); 605 606 onView(withId(R.id.recycler_view)).perform( 607 actionOnItemAtPosition(0, clickChildViewWithId(R.id.action2))); 608 assertTrue(clicked[1]); 609 } 610 611 @Test 612 public void testCustomViewBinderBindsLast() { 613 final String updatedTitle = "updated title"; 614 615 TextListItem item0 = new TextListItem(mActivity); 616 item0.setTitle("original title"); 617 item0.addViewBinder((viewHolder) -> viewHolder.getTitle().setText(updatedTitle)); 618 619 List<TextListItem> items = Arrays.asList(item0); 620 setupPagedListView(items); 621 622 TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0); 623 assertThat(viewHolder.getTitle().getText(), is(equalTo(updatedTitle))); 624 } 625 626 @Test 627 public void testCustomViewBinderOnUnusedViewsHasNoEffect() { 628 TextListItem item0 = new TextListItem(mActivity); 629 item0.addViewBinder((viewHolder) -> viewHolder.getBody().setText("text")); 630 631 List<TextListItem> items = Arrays.asList(item0); 632 setupPagedListView(items); 633 634 TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0); 635 assertThat(viewHolder.getBody().getVisibility(), is(equalTo(View.GONE))); 636 // Custom binder interacts with body but has no effect. 637 // Expect card height to remain single line. 638 assertThat((double) viewHolder.itemView.getHeight(), is(closeTo( 639 InstrumentationRegistry.getContext().getResources().getDimension( 640 R.dimen.car_single_line_list_item_height), 1.0d))); 641 } 642 643 @Test 644 public void testRevertingViewBinder() throws Throwable { 645 TextListItem item0 = new TextListItem(mActivity); 646 item0.setBody("one item"); 647 item0.addViewBinder( 648 (viewHolder) -> viewHolder.getBody().setEllipsize(TextUtils.TruncateAt.END), 649 (viewHolder -> viewHolder.getBody().setEllipsize(null))); 650 651 List<TextListItem> items = Arrays.asList(item0); 652 setupPagedListView(items); 653 654 TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0); 655 656 // Bind view holder to a new item - the customization made by item0 should be reverted. 657 TextListItem item1 = new TextListItem(mActivity); 658 item1.setBody("new item"); 659 mActivityRule.runOnUiThread(() -> item1.bind(viewHolder)); 660 661 assertThat(viewHolder.getBody().getEllipsize(), is(equalTo(null))); 662 } 663 664 @Test 665 public void testRemovingViewBinder() { 666 TextListItem item0 = new TextListItem(mActivity); 667 item0.setBody("one item"); 668 ListItem.ViewBinder<TextListItem.ViewHolder> binder = 669 (viewHolder) -> viewHolder.getTitle().setEllipsize(TextUtils.TruncateAt.END); 670 item0.addViewBinder(binder); 671 672 assertTrue(item0.removeViewBinder(binder)); 673 674 List<TextListItem> items = Arrays.asList(item0); 675 setupPagedListView(items); 676 677 assertThat(getViewHolderAtPosition(0).getBody().getEllipsize(), is(equalTo(null))); 678 } 679 680 @Test 681 public void testSettingTitleOrBodyAsPrimaryText() { 682 // Create 2 items, one with Title as primary (default) and one with Body. 683 // The primary text, regardless of view, should have consistent look (as primary). 684 TextListItem item0 = new TextListItem(mActivity); 685 item0.setTitle("title"); 686 item0.setBody("body"); 687 688 TextListItem item1 = new TextListItem(mActivity); 689 item1.setTitle("title"); 690 item1.setBody("body", true); 691 692 List<TextListItem> items = Arrays.asList(item0, item1); 693 setupPagedListView(items); 694 695 TextListItem.ViewHolder titlePrimary = getViewHolderAtPosition(0); 696 TextListItem.ViewHolder bodyPrimary = getViewHolderAtPosition(1); 697 assertThat(titlePrimary.getTitle().getTextSize(), 698 is(equalTo(bodyPrimary.getBody().getTextSize()))); 699 assertThat(titlePrimary.getTitle().getTextColors(), 700 is(equalTo(bodyPrimary.getBody().getTextColors()))); 701 } 702 703 @Test 704 public void testNoCarriedOverLayoutParamsForTextView() throws Throwable { 705 TextListItem singleLine = new TextListItem(mActivity); 706 singleLine.setTitle("title"); 707 708 setupPagedListView(Arrays.asList(singleLine)); 709 710 // Manually rebind the view holder of a single line item to a double line item. 711 TextListItem doubleLine = new TextListItem(mActivity); 712 doubleLine.setTitle("title"); 713 doubleLine.setBody("body"); 714 TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0); 715 mActivityRule.runOnUiThread(() -> doubleLine.bind(viewHolder)); 716 717 RelativeLayout.LayoutParams titleLayoutParams = 718 (RelativeLayout.LayoutParams) viewHolder.getTitle().getLayoutParams(); 719 RelativeLayout.LayoutParams bodyLayoutParams = 720 (RelativeLayout.LayoutParams) viewHolder.getTitle().getLayoutParams(); 721 assertThat(titleLayoutParams.getRule(RelativeLayout.CENTER_VERTICAL), is(equalTo(0))); 722 assertThat(bodyLayoutParams.getRule(RelativeLayout.CENTER_VERTICAL), is(equalTo(0))); 723 } 724 725 @Test 726 public void testNoCarriedOverLayoutParamsForPrimaryIcon() throws Throwable { 727 TextListItem smallIcon = new TextListItem(mActivity); 728 smallIcon.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, false); 729 smallIcon.setBody("body"); // Small icon of items with body text should use top margin. 730 setupPagedListView(Arrays.asList(smallIcon)); 731 732 // Manually rebind the view holder. 733 TextListItem largeIcon = new TextListItem(mActivity); 734 largeIcon.setPrimaryActionIcon(android.R.drawable.sym_def_app_icon, true); 735 TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0); 736 mActivityRule.runOnUiThread(() -> largeIcon.bind(viewHolder)); 737 738 RelativeLayout.LayoutParams iconLayoutParams = 739 (RelativeLayout.LayoutParams) viewHolder.getPrimaryIcon().getLayoutParams(); 740 assertThat(iconLayoutParams.getRule(RelativeLayout.CENTER_VERTICAL), 741 is(equalTo(RelativeLayout.TRUE))); 742 assertThat(iconLayoutParams.topMargin, is(equalTo(0))); 743 } 744 745 @Test 746 public void testNoCarriedOverOnClickListener() throws Throwable { 747 boolean[] clicked = new boolean[] {false}; 748 TextListItem item0 = new TextListItem(mActivity); 749 item0.setOnClickListener(v -> clicked[0] = true); 750 751 setupPagedListView(Arrays.asList(item0)); 752 753 onView(withId(R.id.recycler_view)).perform(actionOnItemAtPosition(0, click())); 754 assertTrue(clicked[0]); 755 756 // item1 does not have onClickListener. 757 TextListItem item1 = new TextListItem(mActivity); 758 TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0); 759 // Manually rebind the view holder. 760 mActivityRule.runOnUiThread(() -> item1.bind(viewHolder)); 761 762 // Reset for testing. 763 clicked[0] = false; 764 onView(withId(R.id.recycler_view)).perform(actionOnItemAtPosition(0, click())); 765 assertFalse(clicked[0]); 766 } 767 768 @Test 769 public void testUpdateItem() { 770 TextListItem item = new TextListItem(mActivity); 771 setupPagedListView(Arrays.asList(item)); 772 773 String title = "updated title"; 774 item.setTitle(title); 775 776 refreshUi(); 777 778 TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0); 779 assertThat(viewHolder.getTitle().getText(), is(equalTo(title))); 780 } 781 782 @Test 783 public void testUxRestrictionsChange() { 784 String longText = mActivity.getString(R.string.over_uxr_text_length_limit); 785 TextListItem item = new TextListItem(mActivity); 786 item.setBody(longText); 787 788 setupPagedListView(Arrays.asList(item)); 789 790 TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0); 791 // Default behavior without UXR is unrestricted. 792 assertThat(viewHolder.getBody().getText(), is(equalTo(longText))); 793 794 viewHolder.complyWithUxRestrictions(CarUxRestrictionsTestUtils.getFullyRestricted()); 795 refreshUi(); 796 797 // Verify that the body text length is limited. 798 assertThat(viewHolder.getBody().getText().length(), is(lessThan(longText.length()))); 799 } 800 801 @Test 802 public void testUxRestrictionsChangesDoNotAlterExistingInputFilters() { 803 InputFilter filter = new InputFilter.AllCaps(Locale.US); 804 String bodyText = "bodytext"; 805 TextListItem item = new TextListItem(mActivity); 806 item.setBody(bodyText); 807 item.addViewBinder(vh -> vh.getBody().setFilters(new InputFilter[] {filter})); 808 809 setupPagedListView(Arrays.asList(item)); 810 811 TextListItem.ViewHolder viewHolder = getViewHolderAtPosition(0); 812 813 // Toggle UX restrictions between fully restricted and unrestricted should not affect 814 // existing filters. 815 viewHolder.complyWithUxRestrictions(CarUxRestrictionsTestUtils.getFullyRestricted()); 816 refreshUi(); 817 assertTrue(Arrays.asList(viewHolder.getBody().getFilters()).contains(filter)); 818 819 viewHolder.complyWithUxRestrictions(CarUxRestrictionsTestUtils.getBaseline()); 820 refreshUi(); 821 assertTrue(Arrays.asList(viewHolder.getBody().getFilters()).contains(filter)); 822 } 823 824 private static ViewAction clickChildViewWithId(final int id) { 825 return new ViewAction() { 826 @Override 827 public Matcher<View> getConstraints() { 828 return null; 829 } 830 831 @Override 832 public String getDescription() { 833 return "Click on a child view with specific id."; 834 } 835 836 @Override 837 public void perform(UiController uiController, View view) { 838 View v = view.findViewById(id); 839 v.performClick(); 840 } 841 }; 842 } 843} 844