1 /* 2 * Copyright (C) 2019 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 17 package android.preference.cts; 18 19 import static android.preference.PreferenceActivity.EXTRA_NO_HEADERS; 20 import static android.preference.PreferenceActivity.EXTRA_SHOW_FRAGMENT; 21 import static android.preference.PreferenceActivity.EXTRA_SHOW_FRAGMENT_TITLE; 22 23 import static org.junit.Assert.assertEquals; 24 import static org.junit.Assert.assertFalse; 25 import static org.junit.Assert.assertTrue; 26 import static org.junit.Assert.fail; 27 import static org.junit.Assume.assumeTrue; 28 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.pm.PackageManager; 32 import android.graphics.Bitmap; 33 import android.os.Environment; 34 import android.util.Log; 35 36 import androidx.test.platform.app.InstrumentationRegistry; 37 38 import com.android.compatibility.common.util.BitmapUtils; 39 40 import org.junit.Rule; 41 import org.junit.rules.TestName; 42 43 import java.io.File; 44 import java.io.IOException; 45 46 /** 47 * This test suite covers {@link android.preference.PreferenceActivity} to ensure its correct 48 * behavior in orientation and multi-window changes together with navigation between different 49 * screens and going back in the history. It should also cover any possible transition between 50 * single and multi pane modes. Some tests are designed to run only on large or small screen devices 51 * and some can run both. These tests are run from {@link PreferenceActivityFlowLandscapeTest} and 52 * {@link PreferenceActivityFlowPortraitTest} to ensure that both display configurations are 53 * checked. 54 */ 55 public abstract class PreferenceActivityFlowTest { 56 57 private static final String TAG = "PreferenceFlowTest"; 58 59 // Helper strings to ensure that some parts of preferences are visible or not. 60 private static final String PREFS1_HEADER_TITLE = "Prefs 1"; 61 private static final String PREFS2_HEADER_TITLE = "Prefs 2"; 62 private static final String PREFS1_PANEL_TITLE = "Preferences panel 1"; 63 private static final String PREFS2_PANEL_TITLE = "Preferences panel 2"; 64 private static final String INNER_FRAGMENT_PREF_BUTTON = "Fragment preference"; 65 private static final String INNER_FRAGMENT_PREF_TITLE = "Inner fragment"; 66 private static final String LIST_PREF_TITLE = "List preference"; 67 private static final String LIST_PREF_OPTION = "Alpha Option 01"; 68 69 private static final int INITIAL_TITLE_RES_ID = R.string.test_title; 70 private static final int EXPECTED_HEADERS_COUNT = 3; 71 72 private static final String LOCAL_DIRECTORY = Environment.getExternalStorageDirectory() 73 + "/CtsPreferenceTestCases"; 74 75 @Rule 76 public final TestName mTestName = new TestName(); 77 78 TestUtils mTestUtils; 79 protected PreferenceWithHeaders mActivity; 80 protected final Context mContext = InstrumentationRegistry.getInstrumentation() 81 .getTargetContext(); 82 private boolean mIsMultiPane; 83 switchHeadersInner()84 void switchHeadersInner() { 85 launchActivity(); 86 if (shouldRunLargeDeviceTest()) { 87 largeScreenSwitchHeadersInner(); 88 } else { 89 smallScreenSwitchHeadersInner(); 90 } 91 } 92 93 /** 94 * For: Large screen (multi-pane). 95 * Scenario: Tests that tapping on header changes to its proper preference panel and that the 96 * headers are still visible. 97 */ largeScreenSwitchHeadersInner()98 private void largeScreenSwitchHeadersInner() { 99 assertTrue(shouldRunLargeDeviceTest()); 100 assertInitialState(); 101 102 tapOnPrefs2Header(); 103 104 // Headers and panel Prefs2 must be shown. 105 assertHeadersShown(); 106 assertPanelPrefs1Hidden(); 107 assertPanelPrefs2Shown(); 108 109 tapOnPrefs1Header(); 110 111 // Headers and panel Prefs1 must be shown. 112 assertHeadersShown(); 113 assertPanelPrefs1Shown(); 114 assertPanelPrefs2Hidden(); 115 } 116 117 /** 118 * For: Small screen (single-pane). 119 * Scenario: Tests that tapping on header changes to its proper preference panel and that the 120 * headers are hidden and pressing back button after that shows them again. 121 */ smallScreenSwitchHeadersInner()122 private void smallScreenSwitchHeadersInner() { 123 assertTrue(shouldRunSmallDeviceTest()); 124 assertInitialState(); 125 126 tapOnPrefs2Header(); 127 128 // Only Prefs2 must be shown. 129 assertHeadersHidden(); 130 assertPanelPrefs1Hidden(); 131 assertPanelPrefs2Shown(); 132 133 pressBack(); 134 135 tapOnPrefs1Header(); 136 137 // Only Prefs1 must be shown. 138 assertHeadersHidden(); 139 assertPanelPrefs1Shown(); 140 assertPanelPrefs2Hidden(); 141 } 142 143 /** 144 * For: Small screen (single-pane). 145 * Scenario: Tests that after navigating back to the headers list there will be no header 146 * highlighted and that the title was properly restored.. 147 */ smallScreenNoHighlightInHeadersListInner()148 void smallScreenNoHighlightInHeadersListInner() { 149 launchActivity(); 150 if (!shouldRunSmallDeviceTest()) { 151 return; 152 } 153 154 assertInitialState(); 155 156 CharSequence title = mActivity.getTitle(); 157 158 tapOnPrefs2Header(); 159 assertHeadersHidden(); 160 161 pressBack(); 162 assertHeadersShown(); 163 164 // Verify that no headers are focused. 165 assertHeadersNotFocused(); 166 167 // Verify that the title was properly restored. 168 assertEquals(title, mActivity.getTitle()); 169 170 // Verify that everything restores back to initial state again. 171 assertInitialState(); 172 } 173 backPressToExitInner()174 void backPressToExitInner() { 175 launchActivity(); 176 if (shouldRunLargeDeviceTest()) { 177 largeScreenBackPressToExitInner(); 178 } else { 179 smallScreenBackPressToExitInner(); 180 } 181 } 182 183 /** 184 * For: Small screen (single-pane). 185 * Scenario: Tests that pressing back button twice after having preference panel opened will 186 * exit the app when running single-pane. 187 */ smallScreenBackPressToExitInner()188 private void smallScreenBackPressToExitInner() { 189 assertTrue(shouldRunSmallDeviceTest()); 190 assertInitialState(); 191 192 tapOnPrefs2Header(); 193 194 // Only Prefs2 must be shown - covered by smallScreenSwitchHeadersTest 195 196 pressBack(); 197 pressBack(); 198 199 // Now we should be out of the activity 200 assertHeadersHidden(); 201 assertPanelPrefs1Hidden(); 202 assertPanelPrefs2Hidden(); 203 } 204 205 /** 206 * For: Large screen (multi-pane). 207 * Scenario: Selects a header and then leaves the activity by pressing back button. Tests that 208 * we don't transition to the previous header or list of header like in single-pane. 209 */ largeScreenBackPressToExitInner()210 private void largeScreenBackPressToExitInner() { 211 assertTrue(shouldRunLargeDeviceTest()); 212 assertInitialState(); 213 214 tapOnPrefs2Header(); 215 216 // Headers and panel Prefs2 must be shown - covered by largeScreenSwitchHeadersInner. 217 218 pressBack(); 219 220 assertHeadersHidden(); 221 } 222 goToFragmentInner()223 void goToFragmentInner() { 224 launchActivity(); 225 if (shouldRunLargeDeviceTest()) { 226 largeScreenGoToFragmentInner(); 227 } else { 228 smallScreenGoToFragmentInner(); 229 } 230 } 231 232 /** 233 * For: Large screen (multi-pane). 234 * Scenario: Navigates to inner fragment. Test that the fragment was opened correctly and 235 * headers are still visible. Also tests that back press doesn't close the app but navigates 236 * back from the fragment. 237 */ largeScreenGoToFragmentInner()238 private void largeScreenGoToFragmentInner() { 239 assertTrue(shouldRunLargeDeviceTest()); 240 assertInitialState(); 241 242 tapOnPrefs1Header(); 243 244 // Go to preferences inner fragment. 245 mTestUtils.tapOnViewWithText(INNER_FRAGMENT_PREF_BUTTON); 246 247 // Headers and inner fragment must be shown. 248 assertHeadersShown(); 249 assertPanelPrefs1Hidden(); 250 assertInnerFragmentShown(); 251 252 pressBack(); 253 254 // Headers and panel Prefs1 must be shown. 255 assertHeadersShown(); 256 assertPanelPrefs1Shown(); 257 assertPanelPrefs2Hidden(); 258 assertInnerFragmentHidden(); 259 } 260 261 /** 262 * For: Small screen (single-pane). 263 * Scenario: Navigates to inner fragment. Tests that the fragment was opened correctly and 264 * headers are hidden. Also tests that back press doesn't close the app but navigates back from 265 * the fragment. 266 */ smallScreenGoToFragmentInner()267 private void smallScreenGoToFragmentInner() { 268 assertTrue(shouldRunSmallDeviceTest()); 269 assertInitialState(); 270 271 tapOnPrefs1Header(); 272 273 // Go to preferences inner fragment. 274 mTestUtils.tapOnViewWithText(INNER_FRAGMENT_PREF_BUTTON); 275 276 // Only inner fragment must be shown. 277 assertHeadersHidden(); 278 assertPanelPrefs1Hidden(); 279 assertInnerFragmentShown(); 280 281 pressBack(); 282 283 // Prefs1 must be shown. 284 assertHeadersHidden(); 285 assertPanelPrefs1Shown(); 286 assertPanelPrefs2Hidden(); 287 assertInnerFragmentHidden(); 288 } 289 290 /** 291 * For: Any screen (single or multi-pane). 292 * Scenario: Tests that opening specific preference fragment directly via intent works properly. 293 */ startWithFragmentInner()294 void startWithFragmentInner() { 295 launchActivityWithExtras(PreferenceWithHeaders.PrefsTwoFragment.class, 296 false /* noHeaders */, -1 /* initialTitle */); 297 298 assertInitialStateForFragment(); 299 } 300 301 /** 302 * For: Any screen (single or multi-pane). 303 * Scenario: Tests that preference fragment opened directly survives recreation (via screenshot 304 * tests). 305 */ startWithFragmentAndRecreateInner()306 void startWithFragmentAndRecreateInner() { 307 launchActivityWithExtras(PreferenceWithHeaders.PrefsTwoFragment.class, 308 false /* noHeaders */, -1 /* initialTitle */); 309 310 assertInitialStateForFragment(); 311 312 // Take screenshot 313 Bitmap before = mTestUtils.takeScreenshot(); 314 315 // Force recreate 316 recreate(); 317 318 assertInitialStateForFragment(); 319 320 // Compare screenshots 321 Bitmap after = mTestUtils.takeScreenshot(); 322 assertScreenshotsAreEqual(before, after); 323 } 324 325 /** 326 * For: Any screen (single or multi-pane). 327 * Scenario: Starts preference fragment directly with the given initial title and tests that 328 * multi-pane does not show it and single-pane does. 329 */ startWithFragmentAndInitTitleInner()330 void startWithFragmentAndInitTitleInner() { 331 launchActivityWithExtras(PreferenceWithHeaders.PrefsTwoFragment.class, 332 false /* noHeaders */, INITIAL_TITLE_RES_ID); 333 334 assertInitialStateForFragment(); 335 336 if (mIsMultiPane) { 337 String testTitle = mActivity.getResources().getString(INITIAL_TITLE_RES_ID); 338 // Title should not be shown. 339 assertTextHidden(testTitle); 340 } else { 341 // Title should be shown. 342 assertTitleShown(); 343 } 344 } 345 346 /** 347 * For: Any screen (single or multi-pane). 348 * Scenario: Tests that EXTRA_NO_HEADERS intent arg that prevents showing headers in multi-pane 349 * is applied correctly. 350 */ startWithFragmentNoHeadersInner()351 void startWithFragmentNoHeadersInner() { 352 launchActivityWithExtras(PreferenceWithHeaders.PrefsTwoFragment.class, 353 true /* noHeaders */, -1 /* initialTitle */); 354 355 assertInitialStateForFragment(); 356 // Only Prefs2 should be shown. 357 assertHeadersHidden(); 358 assertPanelPrefs1Hidden(); 359 assertPanelPrefs2Shown(); 360 } 361 362 /** 363 * For: Any screen (single or multi-pane). 364 * Scenario: Tests that EXTRA_NO_HEADERS intent arg that prevents showing headers in multi-pane 365 * is applied correctly plus initial title is displayed. 366 */ startWithFragmentNoHeadersButInitTitleInner()367 void startWithFragmentNoHeadersButInitTitleInner() { 368 launchActivityWithExtras(PreferenceWithHeaders.PrefsTwoFragment.class, 369 true /* noHeaders */, INITIAL_TITLE_RES_ID); 370 371 assertInitialStateForFragment(); 372 // Only Prefs2 should be shown. 373 assertHeadersHidden(); 374 assertPanelPrefs1Hidden(); 375 assertPanelPrefs2Shown(); 376 377 assertTitleShown(); 378 } 379 380 /** 381 * For: Any screen (single or multi-pane). 382 * Scenario: Tests that list preference opens correctly and that back press correctly closes it. 383 */ listDialogTest()384 void listDialogTest() { 385 launchActivity(); 386 387 assertInitialState(); 388 if (!mIsMultiPane) { 389 tapOnPrefs1Header(); 390 } 391 392 mTestUtils.tapOnViewWithText(LIST_PREF_TITLE); 393 394 assertTextShown(LIST_PREF_OPTION); 395 396 pressBack(); 397 398 if (mIsMultiPane) { 399 // Headers and Prefs1 should be shown. 400 assertHeadersShown(); 401 assertPanelPrefs1Shown(); 402 } else { 403 // Only Prefs1 should be shown. 404 assertHeadersHidden(); 405 assertPanelPrefs1Shown(); 406 } 407 } 408 409 /** 410 * For: Any screen (single or multi-pane). 411 * Scenario: Tests that the PreferenceActivity properly restores its state after recreation. 412 * Test done via screenshots. 413 */ recreateTest()414 void recreateTest() { 415 launchActivity(); 416 417 assertInitialState(); 418 tapOnPrefs2Header(); 419 420 assertPanelPrefs2Shown(); 421 422 // Take screenshot 423 Bitmap before = mTestUtils.takeScreenshot(); 424 425 recreate(); 426 427 assertPanelPrefs2Shown(); 428 429 // Compare screenshots 430 Bitmap after = mTestUtils.takeScreenshot(); 431 assertScreenshotsAreEqual(before, after); 432 } 433 434 /** 435 * For: Any screen (single or multi-pane). 436 * Scenario: Tests that the PreferenceActivity properly restores its state after recreation 437 * while an inner fragment is shown. Test done via screenshots. 438 */ recreateInnerFragmentTest()439 void recreateInnerFragmentTest() { 440 launchActivity(); 441 442 assertInitialState(); 443 444 if (!mIsMultiPane) { 445 tapOnPrefs1Header(); 446 } 447 448 // Go to preferences inner fragment. 449 mTestUtils.tapOnViewWithText(INNER_FRAGMENT_PREF_BUTTON); 450 451 // Only inner fragment must be shown. 452 if (shouldRunLargeDeviceTest()) { 453 assertHeadersShown(); 454 } else { 455 assertHeadersHidden(); 456 } 457 assertPanelPrefs1Hidden(); 458 assertInnerFragmentShown(); 459 460 // Take screenshot 461 Log.v(TAG, "taking screenshot before"); 462 Bitmap before = mTestUtils.takeScreenshot(); 463 Log.v(TAG, "screenshot taken"); 464 465 recreate(); 466 467 // Only inner fragment must be shown. 468 if (shouldRunLargeDeviceTest()) { 469 assertHeadersShown(); 470 } else { 471 assertHeadersHidden(); 472 } 473 assertPanelPrefs1Hidden(); 474 assertInnerFragmentShown(); 475 476 // Compare screenshots 477 Log.v(TAG, "taking screenshot after"); 478 Bitmap after = mTestUtils.takeScreenshot(); 479 Log.v(TAG, "screenshot taken"); 480 assertScreenshotsAreEqual(before, after); 481 } 482 assertScreenshotsAreEqual(Bitmap before, Bitmap after)483 private void assertScreenshotsAreEqual(Bitmap before, Bitmap after) { 484 // TODO(b/134080964): remove the precision=0.99 arg so it does a pixel-by-pixel check 485 if (!BitmapUtils.compareBitmaps(before, after, 0.99)) { 486 String testName = getClass().getSimpleName() + "." + mTestName.getMethodName(); 487 File beforeFile = null; 488 File afterFile = null; 489 try { 490 beforeFile = dumpBitmap(before, testName + "-before.png"); 491 afterFile = dumpBitmap(after, testName + "-after.png"); 492 } catch (IOException e) { 493 Log.e(TAG, "Error dumping bitmap", e); 494 } 495 fail("Screenshots do not match (check " + beforeFile + " and " + afterFile + ")"); 496 } 497 } 498 499 // TODO: copied from Autofill; move to common CTS code dumpBitmap(Bitmap bitmap, String filename)500 private File dumpBitmap(Bitmap bitmap, String filename) throws IOException { 501 File file = createFile(filename); 502 if (file == null) return null; 503 Log.i(TAG, "Dumping bitmap at " + file); 504 BitmapUtils.saveBitmap(bitmap, file.getParent(), file.getName()); 505 return file; 506 507 } 508 createFile(String filename)509 private static File createFile(String filename) throws IOException { 510 File dir = getLocalDirectory(); 511 File file = new File(dir, filename); 512 if (file.exists()) { 513 Log.v(TAG, "Deleting file " + file); 514 file.delete(); 515 } 516 if (!file.createNewFile()) { 517 Log.e(TAG, "Could not create file " + file); 518 return null; 519 } 520 return file; 521 } 522 getLocalDirectory()523 private static File getLocalDirectory() { 524 File dir = new File(LOCAL_DIRECTORY); 525 dir.mkdirs(); 526 if (!dir.exists()) { 527 Log.e(TAG, "Could not create directory " + dir); 528 return null; 529 } 530 return dir; 531 } 532 533 // TODO: move to common code requirePortraitModeSupport()534 void requirePortraitModeSupport() { 535 String testName = mTestName.getMethodName(); 536 PackageManager pm = mContext.getPackageManager(); 537 538 boolean hasPortrait = pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_PORTRAIT); 539 boolean hasLandscape = pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE); 540 541 // From the javadoc: For backwards compatibility, you can assume that if neither 542 // FEATURE_SCREEN_PORTRAIT nor FEATURE_SCREEN_LANDSCAPE is set then the device 543 // supports both portrait and landscape. 544 boolean supportsPortrait = hasPortrait || !hasLandscape; 545 546 Log.v(TAG, "requirePortraitModeSupport(): FEATURE_SCREEN_PORTRAIT=" + hasPortrait 547 + ", FEATURE_SCREEN_LANDSCAPE=" + hasLandscape 548 + ", supportsPortrait=" + supportsPortrait); 549 550 assumeTrue(testName + ": device does not support portrait mode", supportsPortrait); 551 } 552 553 // TODO: move to common code requireLandscapeModeSupport()554 void requireLandscapeModeSupport() { 555 String testName = mTestName.getMethodName(); 556 PackageManager pm = mContext.getPackageManager(); 557 558 boolean hasPortrait = pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_PORTRAIT); 559 boolean hasLandscape = pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE); 560 561 // From the javadoc: For backwards compatibility, you can assume that if neither 562 // FEATURE_SCREEN_PORTRAIT nor FEATURE_SCREEN_LANDSCAPE is set then the device 563 // supports both portrait and landscape. 564 boolean supportsLandscape = hasLandscape || !hasPortrait; 565 566 Log.v(TAG, "requireLandscapeModeSupport(): FEATURE_SCREEN_PORTRAIT=" + hasPortrait 567 + ", FEATURE_SCREEN_LANDSCAPE=" + hasLandscape 568 + ", supportsLandscape=" + supportsLandscape); 569 570 assumeTrue(testName + ": device does not support portrait mode", supportsLandscape); 571 } 572 assertInitialState()573 private void assertInitialState() { 574 if (mIsMultiPane) { 575 // Headers and panel Prefs1 must be shown. 576 assertHeadersShown(); 577 runOnUiThread(() -> assertTrue(mActivity.hasHeaders())); 578 assertPanelPrefs1Shown(); 579 assertPanelPrefs2Hidden(); 580 } else { 581 // Headers must be shown and nothing else. 582 assertHeadersShown(); 583 runOnUiThread(() -> assertTrue(mActivity.hasHeaders())); 584 assertPanelPrefs1Hidden(); 585 assertPanelPrefs2Hidden(); 586 } 587 assertHeadersAreLoaded(); 588 } 589 assertInitialStateForFragment()590 private void assertInitialStateForFragment() { 591 if (mIsMultiPane) { 592 // Headers and Prefs2 should be shown. 593 assertHeadersShown(); 594 runOnUiThread(() -> assertTrue(mActivity.hasHeaders())); 595 assertPanelPrefs1Hidden(); 596 assertPanelPrefs2Shown(); 597 } else { 598 // Only Prefs2 should be shown. 599 assertHeadersHidden(); 600 runOnUiThread(() -> assertFalse(mActivity.hasHeaders())); 601 assertPanelPrefs1Hidden(); 602 assertPanelPrefs2Shown(); 603 } 604 } 605 shouldRunLargeDeviceTest()606 private boolean shouldRunLargeDeviceTest() { 607 if (mActivity.onIsMultiPane()) { 608 return true; 609 } 610 611 Log.d(TAG, "Skipping a large device test."); 612 return false; 613 } 614 shouldRunSmallDeviceTest()615 private boolean shouldRunSmallDeviceTest() { 616 if (!mActivity.onIsMultiPane()) { 617 return true; 618 } 619 620 Log.d(TAG, "Skipping a small device test."); 621 return false; 622 } 623 tapOnPrefs1Header()624 private void tapOnPrefs1Header() { 625 mTestUtils.tapOnViewWithText(PREFS1_HEADER_TITLE); 626 } 627 tapOnPrefs2Header()628 private void tapOnPrefs2Header() { 629 mTestUtils.tapOnViewWithText(PREFS2_HEADER_TITLE); 630 } 631 assertHeadersAreLoaded()632 private void assertHeadersAreLoaded() { 633 runOnUiThread(() -> assertEquals(EXPECTED_HEADERS_COUNT, 634 mActivity.loadedHeaders == null 635 ? 0 636 : mActivity.loadedHeaders.size())); 637 } 638 assertHeadersShown()639 private void assertHeadersShown() { 640 assertTextShown(PREFS1_HEADER_TITLE); 641 assertTextShown(PREFS2_HEADER_TITLE); 642 } 643 assertHeadersNotFocused()644 private void assertHeadersNotFocused() { 645 assertFalse(mTestUtils.isTextFocused(PREFS1_HEADER_TITLE)); 646 assertFalse(mTestUtils.isTextFocused(PREFS2_HEADER_TITLE)); 647 } 648 assertHeadersHidden()649 private void assertHeadersHidden() { 650 // We check that at least one is hidden instead of each individual one separately because 651 // these headers are also part of individual preference panels breadcrumbs so it would fail 652 // if we only checked for one. 653 assertTrue(mTestUtils.isTextHidden(PREFS1_HEADER_TITLE) 654 || mTestUtils.isTextHidden(PREFS2_HEADER_TITLE)); 655 } 656 assertPanelPrefs1Shown()657 private void assertPanelPrefs1Shown() { 658 assertTextShown(PREFS1_PANEL_TITLE); 659 } 660 assertPanelPrefs1Hidden()661 private void assertPanelPrefs1Hidden() { 662 assertTextHidden(PREFS1_PANEL_TITLE); 663 } 664 assertPanelPrefs2Shown()665 private void assertPanelPrefs2Shown() { 666 assertTextShown(PREFS2_PANEL_TITLE); 667 } 668 assertPanelPrefs2Hidden()669 private void assertPanelPrefs2Hidden() { 670 assertTextHidden(PREFS2_PANEL_TITLE); 671 } 672 assertInnerFragmentShown()673 private void assertInnerFragmentShown() { 674 assertTextShown(INNER_FRAGMENT_PREF_TITLE); 675 } 676 assertInnerFragmentHidden()677 private void assertInnerFragmentHidden() { 678 assertTextHidden(INNER_FRAGMENT_PREF_TITLE); 679 } 680 assertTextShown(String text)681 private void assertTextShown(String text) { 682 assertTrue(mTestUtils.isTextShown(text)); 683 } 684 assertTextHidden(String text)685 private void assertTextHidden(String text) { 686 assertTrue(mTestUtils.isTextHidden(text)); 687 } 688 assertTitleShown()689 private void assertTitleShown() { 690 if (!mTestUtils.isOnWatchUiMode()) { 691 // On watch, activity title is not shown by default. 692 String testTitle = mActivity.getResources().getString(INITIAL_TITLE_RES_ID); 693 assertTextShown(testTitle); 694 } 695 } 696 recreate()697 private void recreate() { 698 runOnUiThread(() -> mActivity.recreate()); 699 mTestUtils.waitForIdle(); 700 } 701 pressBack()702 private void pressBack() { 703 mTestUtils.mDevice.pressBack(); 704 mTestUtils.waitForIdle(); 705 } 706 launchActivity()707 private void launchActivity() { 708 mActivity = launchActivity(null); 709 mTestUtils.waitForIdle(); 710 runOnUiThread(() -> mIsMultiPane = mActivity.isMultiPane()); 711 } 712 launchActivityWithExtras(Class extraFragment, boolean noHeaders, int initialTitle)713 private void launchActivityWithExtras(Class extraFragment, boolean noHeaders, 714 int initialTitle) { 715 Intent intent = new Intent(Intent.ACTION_MAIN); 716 717 if (extraFragment != null) { 718 intent.putExtra(EXTRA_SHOW_FRAGMENT, extraFragment.getName()); 719 } 720 if (noHeaders) { 721 intent.putExtra(EXTRA_NO_HEADERS, true); 722 } 723 if (initialTitle != -1) { 724 intent.putExtra(EXTRA_SHOW_FRAGMENT_TITLE, initialTitle); 725 } 726 727 mActivity = launchActivity(intent); 728 mTestUtils.waitForIdle(); 729 runOnUiThread(() -> mIsMultiPane = mActivity.isMultiPane()); 730 } 731 launchActivity(Intent intent)732 protected abstract PreferenceWithHeaders launchActivity(Intent intent); 733 runOnUiThread(Runnable runnable)734 protected abstract void runOnUiThread(Runnable runnable); 735 } 736