FragmentLifecycleTest.java revision ff22d81f6561f6cdd2a91eb63238c41079927a22
1/* 2 * Copyright (C) 2016 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 18package android.support.v4.app; 19 20import android.content.Context; 21import android.content.Intent; 22import android.os.Bundle; 23import android.os.Parcelable; 24import android.support.annotation.NonNull; 25import android.support.annotation.Nullable; 26import android.support.fragment.test.R; 27import android.support.test.annotation.UiThreadTest; 28import android.support.test.rule.ActivityTestRule; 29import android.support.test.runner.AndroidJUnit4; 30import android.support.v4.app.test.EmptyFragmentTestActivity; 31import android.test.suitebuilder.annotation.MediumTest; 32import android.view.LayoutInflater; 33import android.view.View; 34import android.view.ViewGroup; 35import android.view.Window; 36 37import android.widget.TextView; 38import org.junit.Assert; 39import org.junit.Rule; 40import org.junit.Test; 41import org.junit.runner.RunWith; 42 43import java.io.FileDescriptor; 44import java.io.PrintWriter; 45 46import static junit.framework.Assert.assertEquals; 47import static junit.framework.Assert.assertFalse; 48import static junit.framework.Assert.assertNotNull; 49import static junit.framework.Assert.assertNotSame; 50import static junit.framework.Assert.assertNull; 51import static junit.framework.Assert.assertSame; 52import static junit.framework.Assert.assertTrue; 53import static org.junit.Assert.assertNotEquals; 54 55@RunWith(AndroidJUnit4.class) 56@MediumTest 57public class FragmentLifecycleTest { 58 59 @Rule 60 public ActivityTestRule<EmptyFragmentTestActivity> mActivityRule = 61 new ActivityTestRule<EmptyFragmentTestActivity>(EmptyFragmentTestActivity.class); 62 63 @Test 64 public void basicLifecycle() throws Throwable { 65 final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager(); 66 final StrictFragment strictFragment = new StrictFragment(); 67 68 // Add fragment; StrictFragment will throw if it detects any violation 69 // in standard lifecycle method ordering or expected preconditions. 70 fm.beginTransaction().add(strictFragment, "EmptyHeadless").commit(); 71 executePendingTransactions(fm); 72 73 assertTrue("fragment is not added", strictFragment.isAdded()); 74 assertFalse("fragment is detached", strictFragment.isDetached()); 75 assertTrue("fragment is not resumed", strictFragment.isResumed()); 76 77 // Test removal as well; StrictFragment will throw here too. 78 fm.beginTransaction().remove(strictFragment).commit(); 79 executePendingTransactions(fm); 80 81 assertFalse("fragment is added", strictFragment.isAdded()); 82 assertFalse("fragment is resumed", strictFragment.isResumed()); 83 84 // This one is perhaps counterintuitive; "detached" means specifically detached 85 // but still managed by a FragmentManager. The .remove call above 86 // should not enter this state. 87 assertFalse("fragment is detached", strictFragment.isDetached()); 88 } 89 90 @Test 91 public void detachment() throws Throwable { 92 final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager(); 93 final StrictFragment f1 = new StrictFragment(); 94 final StrictFragment f2 = new StrictFragment(); 95 96 fm.beginTransaction().add(f1, "1").add(f2, "2").commit(); 97 executePendingTransactions(fm); 98 99 assertTrue("fragment 1 is not added", f1.isAdded()); 100 assertTrue("fragment 2 is not added", f2.isAdded()); 101 102 // Test detaching fragments using StrictFragment to throw on errors. 103 fm.beginTransaction().detach(f1).detach(f2).commit(); 104 executePendingTransactions(fm); 105 106 assertTrue("fragment 1 is not detached", f1.isDetached()); 107 assertTrue("fragment 2 is not detached", f2.isDetached()); 108 assertFalse("fragment 1 is added", f1.isAdded()); 109 assertFalse("fragment 2 is added", f2.isAdded()); 110 111 // Only reattach f1; leave v2 detached. 112 fm.beginTransaction().attach(f1).commit(); 113 executePendingTransactions(fm); 114 115 assertTrue("fragment 1 is not added", f1.isAdded()); 116 assertFalse("fragment 1 is detached", f1.isDetached()); 117 assertTrue("fragment 2 is not detached", f2.isDetached()); 118 119 // Remove both from the FragmentManager. 120 fm.beginTransaction().remove(f1).remove(f2).commit(); 121 executePendingTransactions(fm); 122 123 assertFalse("fragment 1 is added", f1.isAdded()); 124 assertFalse("fragment 2 is added", f2.isAdded()); 125 assertFalse("fragment 1 is detached", f1.isDetached()); 126 assertFalse("fragment 2 is detached", f2.isDetached()); 127 } 128 129 @Test 130 public void basicBackStack() throws Throwable { 131 final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager(); 132 final StrictFragment f1 = new StrictFragment(); 133 final StrictFragment f2 = new StrictFragment(); 134 135 // Add a fragment normally to set up 136 fm.beginTransaction().add(f1, "1").commit(); 137 executePendingTransactions(fm); 138 139 assertTrue("fragment 1 is not added", f1.isAdded()); 140 141 // Remove the first one and add a second. We're not using replace() here since 142 // these fragments are headless and as of this test writing, replace() only works 143 // for fragments with views and a container view id. 144 // Add it to the back stack so we can pop it afterwards. 145 fm.beginTransaction().remove(f1).add(f2, "2").addToBackStack("stack1").commit(); 146 executePendingTransactions(fm); 147 148 assertFalse("fragment 1 is added", f1.isAdded()); 149 assertTrue("fragment 2 is not added", f2.isAdded()); 150 151 // Test popping the stack 152 fm.popBackStack(); 153 executePendingTransactions(fm); 154 155 assertFalse("fragment 2 is added", f2.isAdded()); 156 assertTrue("fragment 1 is not added", f1.isAdded()); 157 } 158 159 @Test 160 public void attachBackStack() throws Throwable { 161 final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager(); 162 final StrictFragment f1 = new StrictFragment(); 163 final StrictFragment f2 = new StrictFragment(); 164 165 // Add a fragment normally to set up 166 fm.beginTransaction().add(f1, "1").commit(); 167 executePendingTransactions(fm); 168 169 assertTrue("fragment 1 is not added", f1.isAdded()); 170 171 fm.beginTransaction().detach(f1).add(f2, "2").addToBackStack("stack1").commit(); 172 executePendingTransactions(fm); 173 174 assertTrue("fragment 1 is not detached", f1.isDetached()); 175 assertFalse("fragment 2 is detached", f2.isDetached()); 176 assertFalse("fragment 1 is added", f1.isAdded()); 177 assertTrue("fragment 2 is not added", f2.isAdded()); 178 } 179 180 @Test 181 public void viewLifecycle() throws Throwable { 182 // Test basic lifecycle when the fragment creates a view 183 184 final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager(); 185 final StrictViewFragment f1 = new StrictViewFragment(); 186 187 fm.beginTransaction().add(android.R.id.content, f1).commit(); 188 executePendingTransactions(fm); 189 190 assertTrue("fragment 1 is not added", f1.isAdded()); 191 final View view = f1.getView(); 192 assertNotNull("fragment 1 returned null from getView", view); 193 assertTrue("fragment 1's view is not attached to a window", view.isAttachedToWindow()); 194 195 fm.beginTransaction().remove(f1).commit(); 196 executePendingTransactions(fm); 197 198 assertFalse("fragment 1 is added", f1.isAdded()); 199 assertNull("fragment 1 returned non-null from getView after removal", f1.getView()); 200 assertFalse("fragment 1's previous view is still attached to a window", 201 view.isAttachedToWindow()); 202 } 203 204 @Test 205 public void viewReplace() throws Throwable { 206 // Replace one view with another, then reverse it with the back stack 207 208 final FragmentManager fm = mActivityRule.getActivity().getSupportFragmentManager(); 209 final StrictViewFragment f1 = new StrictViewFragment(); 210 final StrictViewFragment f2 = new StrictViewFragment(); 211 212 fm.beginTransaction().add(android.R.id.content, f1).commit(); 213 executePendingTransactions(fm); 214 215 assertTrue("fragment 1 is not added", f1.isAdded()); 216 217 View origView1 = f1.getView(); 218 assertNotNull("fragment 1 returned null view", origView1); 219 assertTrue("fragment 1's view not attached", origView1.isAttachedToWindow()); 220 221 fm.beginTransaction().replace(android.R.id.content, f2).addToBackStack("stack1").commit(); 222 executePendingTransactions(fm); 223 224 assertFalse("fragment 1 is added", f1.isAdded()); 225 assertTrue("fragment 2 is added", f2.isAdded()); 226 assertNull("fragment 1 returned non-null view", f1.getView()); 227 assertFalse("fragment 1's old view still attached", origView1.isAttachedToWindow()); 228 View origView2 = f2.getView(); 229 assertNotNull("fragment 2 returned null view", origView2); 230 assertTrue("fragment 2's view not attached", origView2.isAttachedToWindow()); 231 232 fm.popBackStack(); 233 executePendingTransactions(fm); 234 235 assertTrue("fragment 1 is not added", f1.isAdded()); 236 assertFalse("fragment 2 is added", f2.isAdded()); 237 assertNull("fragment 2 returned non-null view", f2.getView()); 238 assertFalse("fragment 2's view still attached", origView2.isAttachedToWindow()); 239 View newView1 = f1.getView(); 240 assertNotSame("fragment 1 had same view from last attachment", origView1, newView1); 241 assertTrue("fragment 1's view not attached", newView1.isAttachedToWindow()); 242 } 243 244 @Test 245 @UiThreadTest 246 public void restoreRetainedInstanceFragments() throws Throwable { 247 // Create a new FragmentManager in isolation, nest some assorted fragments 248 // and then restore them to a second new FragmentManager. 249 250 final FragmentController fc1 = FragmentController.createController( 251 new HostCallbacks(mActivityRule.getActivity())); 252 253 final FragmentManager fm1 = fc1.getSupportFragmentManager(); 254 255 fc1.attachHost(null); 256 fc1.dispatchCreate(); 257 258 // Configure fragments. 259 260 // Grandparent fragment will not retain instance 261 final StateSaveFragment grandparentFragment = new StateSaveFragment("Grandparent", 262 "UnsavedGrandparent"); 263 assertNotNull("grandparent fragment saved state not initialized", 264 grandparentFragment.getSavedState()); 265 assertNotNull("grandparent fragment unsaved state not initialized", 266 grandparentFragment.getUnsavedState()); 267 fm1.beginTransaction().add(grandparentFragment, "tag:grandparent").commitNow(); 268 269 // Parent fragment will retain instance 270 final StateSaveFragment parentFragment = new StateSaveFragment("Parent", "UnsavedParent"); 271 assertNotNull("parent fragment saved state not initialized", 272 parentFragment.getSavedState()); 273 assertNotNull("parent fragment unsaved state not initialized", 274 parentFragment.getUnsavedState()); 275 parentFragment.setRetainInstance(true); 276 grandparentFragment.getChildFragmentManager().beginTransaction() 277 .add(parentFragment, "tag:parent").commitNow(); 278 assertSame("parent fragment is not a child of grandparent", 279 grandparentFragment, parentFragment.getParentFragment()); 280 281 // Child fragment will not retain instance 282 final StateSaveFragment childFragment = new StateSaveFragment("Child", "UnsavedChild"); 283 assertNotNull("child fragment saved state not initialized", 284 childFragment.getSavedState()); 285 assertNotNull("child fragment unsaved state not initialized", 286 childFragment.getUnsavedState()); 287 parentFragment.getChildFragmentManager().beginTransaction() 288 .add(childFragment, "tag:child").commitNow(); 289 assertSame("child fragment is not a child of grandpanret", 290 parentFragment, childFragment.getParentFragment()); 291 292 // Saved for comparison later 293 final FragmentManager parentChildFragmentManager = parentFragment.getChildFragmentManager(); 294 295 fc1.dispatchActivityCreated(); 296 fc1.noteStateNotSaved(); 297 fc1.execPendingActions(); 298 fc1.doLoaderStart(); 299 fc1.dispatchStart(); 300 fc1.reportLoaderStart(); 301 fc1.dispatchResume(); 302 fc1.execPendingActions(); 303 304 // Bring the state back down to destroyed, simulating an activity restart 305 fc1.dispatchPause(); 306 final Parcelable savedState = fc1.saveAllState(); 307 final FragmentManagerNonConfig nonconf = fc1.retainNestedNonConfig(); 308 fc1.dispatchStop(); 309 fc1.dispatchReallyStop(); 310 fc1.dispatchDestroy(); 311 312 // Create the new controller and restore state 313 final FragmentController fc2 = FragmentController.createController( 314 new HostCallbacks(mActivityRule.getActivity())); 315 316 final FragmentManager fm2 = fc2.getSupportFragmentManager(); 317 318 fc2.attachHost(null); 319 fc2.restoreAllState(savedState, nonconf); 320 fc2.dispatchCreate(); 321 322 // Confirm that the restored fragments are available and in the expected states 323 final StateSaveFragment restoredGrandparent = (StateSaveFragment) fm2.findFragmentByTag( 324 "tag:grandparent"); 325 assertNotNull("grandparent fragment not restored", restoredGrandparent); 326 327 assertNotSame("grandparent fragment instance was saved", 328 grandparentFragment, restoredGrandparent); 329 assertEquals("grandparent fragment saved state was not equal", 330 grandparentFragment.getSavedState(), restoredGrandparent.getSavedState()); 331 assertNotEquals("grandparent fragment unsaved state was unexpectedly preserved", 332 grandparentFragment.getUnsavedState(), restoredGrandparent.getUnsavedState()); 333 334 final StateSaveFragment restoredParent = (StateSaveFragment) restoredGrandparent 335 .getChildFragmentManager().findFragmentByTag("tag:parent"); 336 assertNotNull("parent fragment not restored", restoredParent); 337 338 assertSame("parent fragment instance was not saved", parentFragment, restoredParent); 339 assertEquals("parent fragment saved state was not equal", 340 parentFragment.getSavedState(), restoredParent.getSavedState()); 341 assertEquals("parent fragment unsaved state was not equal", 342 parentFragment.getUnsavedState(), restoredParent.getUnsavedState()); 343 assertNotSame("parent fragment has the same child FragmentManager", 344 parentChildFragmentManager, restoredParent.getChildFragmentManager()); 345 346 final StateSaveFragment restoredChild = (StateSaveFragment) restoredParent 347 .getChildFragmentManager().findFragmentByTag("tag:child"); 348 assertNotNull("child fragment not restored", restoredChild); 349 350 assertNotSame("child fragment instance state was saved", childFragment, restoredChild); 351 assertEquals("child fragment saved state was not equal", 352 childFragment.getSavedState(), restoredChild.getSavedState()); 353 assertNotEquals("child fragment saved state was unexpectedly equal", 354 childFragment.getUnsavedState(), restoredChild.getUnsavedState()); 355 356 fc2.dispatchActivityCreated(); 357 fc2.noteStateNotSaved(); 358 fc2.execPendingActions(); 359 fc2.doLoaderStart(); 360 fc2.dispatchStart(); 361 fc2.reportLoaderStart(); 362 fc2.dispatchResume(); 363 fc2.execPendingActions(); 364 365 // Test that the fragments are in the configuration we expect 366 367 // Bring the state back down to destroyed before we finish the test 368 fc2.dispatchPause(); 369 fc2.saveAllState(); 370 fc2.dispatchStop(); 371 fc2.dispatchReallyStop(); 372 fc2.dispatchDestroy(); 373 374 assertTrue("grandparent not destroyed", restoredGrandparent.mCalledOnDestroy); 375 assertTrue("parent not destroyed", restoredParent.mCalledOnDestroy); 376 assertTrue("child not destroyed", restoredChild.mCalledOnDestroy); 377 } 378 379 @Test 380 @UiThreadTest 381 public void saveAnimationState() throws Throwable { 382 FragmentController fc = startupFragmentController(null); 383 FragmentManager fm = fc.getSupportFragmentManager(); 384 385 fm.beginTransaction() 386 .setCustomAnimations(0, R.anim.fade_out, R.anim.fade_in, R.anim.fade_out) 387 .add(android.R.id.content, SimpleFragment.create(R.layout.fragment_a)) 388 .addToBackStack(null) 389 .commit(); 390 fm.executePendingTransactions(); 391 392 assertAnimationsMatch(fm, 0, R.anim.fade_out, R.anim.fade_in, R.anim.fade_out); 393 394 // Causes save and restore of fragments and back stack 395 fc = restartFragmentController(fc); 396 fm = fc.getSupportFragmentManager(); 397 398 assertAnimationsMatch(fm, 0, R.anim.fade_out, R.anim.fade_in, R.anim.fade_out); 399 400 fm.beginTransaction() 401 .setCustomAnimations(R.anim.fade_in, R.anim.fade_out, 0, 0) 402 .replace(android.R.id.content, SimpleFragment.create(R.layout.fragment_b)) 403 .addToBackStack(null) 404 .commit(); 405 fm.executePendingTransactions(); 406 407 assertAnimationsMatch(fm, R.anim.fade_in, R.anim.fade_out, 0, 0); 408 409 // Causes save and restore of fragments and back stack 410 fc = restartFragmentController(fc); 411 fm = fc.getSupportFragmentManager(); 412 413 assertAnimationsMatch(fm, R.anim.fade_in, R.anim.fade_out, 0, 0); 414 415 fm.popBackStackImmediate(); 416 417 assertAnimationsMatch(fm, 0, R.anim.fade_out, R.anim.fade_in, R.anim.fade_out); 418 419 shutdownFragmentController(fc); 420 } 421 422 /** 423 * This test confirms that as long as a parent fragment has called super.onCreate, 424 * any child fragments added, committed and with transactions executed will be brought 425 * to at least the CREATED state by the time the parent fragment receives onCreateView. 426 * This means the child fragment will have received onAttach/onCreate. 427 */ 428 @Test 429 @UiThreadTest 430 public void childFragmentManagerAttach() throws Throwable { 431 FragmentController fc = FragmentController.createController( 432 new HostCallbacks(mActivityRule.getActivity())); 433 fc.attachHost(null); 434 fc.dispatchCreate(); 435 436 FragmentManager fm = fc.getSupportFragmentManager(); 437 438 fm.beginTransaction() 439 .add(android.R.id.content, new ChildFragmentManagerFragment()) 440 .commitNow(); 441 442 fc.dispatchActivityCreated(); 443 444 fc.dispatchStart(); 445 fc.dispatchResume(); 446 fc.dispatchStop(); 447 fc.dispatchReallyStop(); 448 fc.dispatchDestroy(); 449 } 450 451 private void assertAnimationsMatch(FragmentManager fm, int enter, int exit, int popEnter, 452 int popExit) { 453 FragmentManagerImpl fmImpl = (FragmentManagerImpl) fm; 454 BackStackRecord record = fmImpl.mBackStack.get(fmImpl.mBackStack.size() - 1); 455 456 Assert.assertEquals(enter, record.mEnterAnim); 457 Assert.assertEquals(exit, record.mExitAnim); 458 Assert.assertEquals(popEnter, record.mPopEnterAnim); 459 Assert.assertEquals(popExit, record.mPopExitAnim); 460 } 461 462 private FragmentController restartFragmentController(FragmentController fc) { 463 Parcelable savedState = shutdownFragmentController(fc); 464 return startupFragmentController(savedState); 465 } 466 467 private FragmentController startupFragmentController(Parcelable savedState) { 468 final FragmentController fc = FragmentController.createController( 469 new HostCallbacks(mActivityRule.getActivity())); 470 fc.attachHost(null); 471 fc.restoreAllState(savedState, (FragmentManagerNonConfig) null); 472 fc.dispatchCreate(); 473 fc.dispatchActivityCreated(); 474 fc.noteStateNotSaved(); 475 fc.execPendingActions(); 476 fc.doLoaderStart(); 477 fc.dispatchStart(); 478 fc.reportLoaderStart(); 479 fc.dispatchResume(); 480 fc.execPendingActions(); 481 return fc; 482 } 483 484 private Parcelable shutdownFragmentController(FragmentController fc) { 485 fc.dispatchPause(); 486 final Parcelable savedState = fc.saveAllState(); 487 fc.dispatchStop(); 488 fc.dispatchReallyStop(); 489 fc.dispatchDestroy(); 490 return savedState; 491 } 492 493 private void executePendingTransactions(final FragmentManager fm) throws Throwable { 494 mActivityRule.runOnUiThread(new Runnable() { 495 @Override 496 public void run() { 497 fm.executePendingTransactions(); 498 } 499 }); 500 } 501 502 public static class StateSaveFragment extends StrictFragment { 503 private static final String STATE_KEY = "state"; 504 505 private String mSavedState; 506 private String mUnsavedState; 507 508 public StateSaveFragment() { 509 } 510 511 public StateSaveFragment(String savedState, String unsavedState) { 512 mSavedState = savedState; 513 mUnsavedState = unsavedState; 514 } 515 516 public String getSavedState() { 517 return mSavedState; 518 } 519 520 public String getUnsavedState() { 521 return mUnsavedState; 522 } 523 524 @Override 525 public void onCreate(Bundle savedInstanceState) { 526 super.onCreate(savedInstanceState); 527 if (savedInstanceState != null) { 528 mSavedState = savedInstanceState.getString(STATE_KEY); 529 } 530 } 531 532 @Override 533 public void onSaveInstanceState(Bundle outState) { 534 super.onSaveInstanceState(outState); 535 outState.putString(STATE_KEY, mSavedState); 536 } 537 } 538 539 public static class ChildFragmentManagerFragment extends StrictFragment { 540 private FragmentManager mSavedChildFragmentManager; 541 542 @Override 543 public void onAttach(Context context) { 544 super.onAttach(context); 545 mSavedChildFragmentManager = getChildFragmentManager(); 546 } 547 548 @Nullable 549 @Override 550 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 551 @Nullable Bundle savedInstanceState) { 552 assertSame("child FragmentManagers not the same instance", mSavedChildFragmentManager, 553 getChildFragmentManager()); 554 ChildFragmentManagerChildFragment child = new ChildFragmentManagerChildFragment("foo"); 555 mSavedChildFragmentManager.beginTransaction() 556 .add(child, "tag") 557 .commitNow(); 558 assertEquals("argument strings don't match", "foo", child.getString()); 559 return new TextView(container.getContext()); 560 } 561 } 562 563 public static class ChildFragmentManagerChildFragment extends StrictFragment { 564 private String mString; 565 566 public ChildFragmentManagerChildFragment() { 567 } 568 569 public ChildFragmentManagerChildFragment(String arg) { 570 final Bundle b = new Bundle(); 571 b.putString("string", arg); 572 setArguments(b); 573 } 574 575 @Override 576 public void onAttach(Context context) { 577 super.onAttach(context); 578 mString = getArguments().getString("string", "NO VALUE"); 579 } 580 581 public String getString() { 582 return mString; 583 } 584 } 585 586 static class HostCallbacks extends FragmentHostCallback<FragmentActivity> { 587 private final FragmentActivity mActivity; 588 589 public HostCallbacks(FragmentActivity activity) { 590 super(activity); 591 mActivity = activity; 592 } 593 594 @Override 595 public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 596 } 597 598 @Override 599 public boolean onShouldSaveFragmentState(Fragment fragment) { 600 return !mActivity.isFinishing(); 601 } 602 603 @Override 604 public LayoutInflater onGetLayoutInflater() { 605 return mActivity.getLayoutInflater().cloneInContext(mActivity); 606 } 607 608 @Override 609 public FragmentActivity onGetHost() { 610 return mActivity; 611 } 612 613 @Override 614 public void onSupportInvalidateOptionsMenu() { 615 mActivity.supportInvalidateOptionsMenu(); 616 } 617 618 @Override 619 public void onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode) { 620 mActivity.startActivityFromFragment(fragment, intent, requestCode); 621 } 622 623 @Override 624 public void onStartActivityFromFragment( 625 Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) { 626 mActivity.startActivityFromFragment(fragment, intent, requestCode, options); 627 } 628 629 @Override 630 public void onRequestPermissionsFromFragment(@NonNull Fragment fragment, 631 @NonNull String[] permissions, int requestCode) { 632 throw new UnsupportedOperationException(); 633 } 634 635 @Override 636 public boolean onShouldShowRequestPermissionRationale(@NonNull String permission) { 637 return ActivityCompat.shouldShowRequestPermissionRationale( 638 mActivity, permission); 639 } 640 641 @Override 642 public boolean onHasWindowAnimations() { 643 return mActivity.getWindow() != null; 644 } 645 646 @Override 647 public int onGetWindowAnimations() { 648 final Window w = mActivity.getWindow(); 649 return (w == null) ? 0 : w.getAttributes().windowAnimations; 650 } 651 652 @Override 653 public void onAttachFragment(Fragment fragment) { 654 mActivity.onAttachFragment(fragment); 655 } 656 657 @Nullable 658 @Override 659 public View onFindViewById(int id) { 660 return mActivity.findViewById(id); 661 } 662 663 @Override 664 public boolean onHasView() { 665 final Window w = mActivity.getWindow(); 666 return (w != null && w.peekDecorView() != null); 667 } 668 } 669 670 public static class SimpleFragment extends Fragment { 671 private int mLayoutId; 672 private static final String LAYOUT_ID = "layoutId"; 673 674 @Override 675 public void onCreate(Bundle savedInstanceState) { 676 super.onCreate(savedInstanceState); 677 if (savedInstanceState != null) { 678 mLayoutId = savedInstanceState.getInt(LAYOUT_ID, mLayoutId); 679 } 680 } 681 682 @Override 683 public void onSaveInstanceState(Bundle outState) { 684 super.onSaveInstanceState(outState); 685 outState.putInt(LAYOUT_ID, mLayoutId); 686 } 687 688 @Override 689 public View onCreateView(LayoutInflater inflater, ViewGroup container, 690 Bundle savedInstanceState) { 691 return inflater.inflate(mLayoutId, container, false); 692 } 693 694 public static SimpleFragment create(int layoutId) { 695 SimpleFragment fragment = new SimpleFragment(); 696 fragment.mLayoutId = layoutId; 697 return fragment; 698 } 699 } 700} 701