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.test.annotation.UiThreadTest; 27import android.support.test.rule.ActivityTestRule; 28import android.support.test.runner.AndroidJUnit4; 29import android.support.v4.app.test.EmptyFragmentTestActivity; 30import android.support.v4.test.R; 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 ChildFragmentManagerFragment fragment = new ChildFragmentManagerFragment(); 439 fm.beginTransaction() 440 .add(android.R.id.content, fragment) 441 .commitNow(); 442 443 fc.dispatchActivityCreated(); 444 445 fc.dispatchStart(); 446 fc.dispatchResume(); 447 448 // Confirm that the parent fragment received onAttachFragment 449 assertTrue("parent fragment did not receive onAttachFragment", 450 fragment.mCalledOnAttachFragment); 451 452 fc.dispatchStop(); 453 fc.dispatchReallyStop(); 454 fc.dispatchDestroy(); 455 } 456 457 private void assertAnimationsMatch(FragmentManager fm, int enter, int exit, int popEnter, 458 int popExit) { 459 FragmentManagerImpl fmImpl = (FragmentManagerImpl) fm; 460 BackStackRecord record = fmImpl.mBackStack.get(fmImpl.mBackStack.size() - 1); 461 462 Assert.assertEquals(enter, record.mEnterAnim); 463 Assert.assertEquals(exit, record.mExitAnim); 464 Assert.assertEquals(popEnter, record.mPopEnterAnim); 465 Assert.assertEquals(popExit, record.mPopExitAnim); 466 } 467 468 private FragmentController restartFragmentController(FragmentController fc) { 469 Parcelable savedState = shutdownFragmentController(fc); 470 return startupFragmentController(savedState); 471 } 472 473 private FragmentController startupFragmentController(Parcelable savedState) { 474 final FragmentController fc = FragmentController.createController( 475 new HostCallbacks(mActivityRule.getActivity())); 476 fc.attachHost(null); 477 fc.restoreAllState(savedState, (FragmentManagerNonConfig) null); 478 fc.dispatchCreate(); 479 fc.dispatchActivityCreated(); 480 fc.noteStateNotSaved(); 481 fc.execPendingActions(); 482 fc.doLoaderStart(); 483 fc.dispatchStart(); 484 fc.reportLoaderStart(); 485 fc.dispatchResume(); 486 fc.execPendingActions(); 487 return fc; 488 } 489 490 private Parcelable shutdownFragmentController(FragmentController fc) { 491 fc.dispatchPause(); 492 final Parcelable savedState = fc.saveAllState(); 493 fc.dispatchStop(); 494 fc.dispatchReallyStop(); 495 fc.dispatchDestroy(); 496 return savedState; 497 } 498 499 private void executePendingTransactions(final FragmentManager fm) throws Throwable { 500 mActivityRule.runOnUiThread(new Runnable() { 501 @Override 502 public void run() { 503 fm.executePendingTransactions(); 504 } 505 }); 506 } 507 508 public static class StateSaveFragment extends StrictFragment { 509 private static final String STATE_KEY = "state"; 510 511 private String mSavedState; 512 private String mUnsavedState; 513 514 public StateSaveFragment() { 515 } 516 517 public StateSaveFragment(String savedState, String unsavedState) { 518 mSavedState = savedState; 519 mUnsavedState = unsavedState; 520 } 521 522 public String getSavedState() { 523 return mSavedState; 524 } 525 526 public String getUnsavedState() { 527 return mUnsavedState; 528 } 529 530 @Override 531 public void onCreate(Bundle savedInstanceState) { 532 super.onCreate(savedInstanceState); 533 if (savedInstanceState != null) { 534 mSavedState = savedInstanceState.getString(STATE_KEY); 535 } 536 } 537 538 @Override 539 public void onSaveInstanceState(Bundle outState) { 540 super.onSaveInstanceState(outState); 541 outState.putString(STATE_KEY, mSavedState); 542 } 543 } 544 545 public static class ChildFragmentManagerFragment extends StrictFragment { 546 private FragmentManager mSavedChildFragmentManager; 547 548 @Override 549 public void onAttach(Context context) { 550 super.onAttach(context); 551 mSavedChildFragmentManager = getChildFragmentManager(); 552 } 553 554 @Nullable 555 @Override 556 public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, 557 @Nullable Bundle savedInstanceState) { 558 assertSame("child FragmentManagers not the same instance", mSavedChildFragmentManager, 559 getChildFragmentManager()); 560 ChildFragmentManagerChildFragment child = new ChildFragmentManagerChildFragment("foo"); 561 mSavedChildFragmentManager.beginTransaction() 562 .add(child, "tag") 563 .commitNow(); 564 assertEquals("argument strings don't match", "foo", child.getString()); 565 return new TextView(container.getContext()); 566 } 567 } 568 569 public static class ChildFragmentManagerChildFragment extends StrictFragment { 570 private String mString; 571 572 public ChildFragmentManagerChildFragment() { 573 } 574 575 public ChildFragmentManagerChildFragment(String arg) { 576 final Bundle b = new Bundle(); 577 b.putString("string", arg); 578 setArguments(b); 579 } 580 581 @Override 582 public void onAttach(Context context) { 583 super.onAttach(context); 584 mString = getArguments().getString("string", "NO VALUE"); 585 } 586 587 public String getString() { 588 return mString; 589 } 590 } 591 592 static class HostCallbacks extends FragmentHostCallback<FragmentActivity> { 593 private final FragmentActivity mActivity; 594 595 public HostCallbacks(FragmentActivity activity) { 596 super(activity); 597 mActivity = activity; 598 } 599 600 @Override 601 public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { 602 } 603 604 @Override 605 public boolean onShouldSaveFragmentState(Fragment fragment) { 606 return !mActivity.isFinishing(); 607 } 608 609 @Override 610 public LayoutInflater onGetLayoutInflater() { 611 return mActivity.getLayoutInflater().cloneInContext(mActivity); 612 } 613 614 @Override 615 public FragmentActivity onGetHost() { 616 return mActivity; 617 } 618 619 @Override 620 public void onSupportInvalidateOptionsMenu() { 621 mActivity.supportInvalidateOptionsMenu(); 622 } 623 624 @Override 625 public void onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode) { 626 mActivity.startActivityFromFragment(fragment, intent, requestCode); 627 } 628 629 @Override 630 public void onStartActivityFromFragment( 631 Fragment fragment, Intent intent, int requestCode, @Nullable Bundle options) { 632 mActivity.startActivityFromFragment(fragment, intent, requestCode, options); 633 } 634 635 @Override 636 public void onRequestPermissionsFromFragment(@NonNull Fragment fragment, 637 @NonNull String[] permissions, int requestCode) { 638 throw new UnsupportedOperationException(); 639 } 640 641 @Override 642 public boolean onShouldShowRequestPermissionRationale(@NonNull String permission) { 643 return ActivityCompat.shouldShowRequestPermissionRationale( 644 mActivity, permission); 645 } 646 647 @Override 648 public boolean onHasWindowAnimations() { 649 return mActivity.getWindow() != null; 650 } 651 652 @Override 653 public int onGetWindowAnimations() { 654 final Window w = mActivity.getWindow(); 655 return (w == null) ? 0 : w.getAttributes().windowAnimations; 656 } 657 658 @Override 659 public void onAttachFragment(Fragment fragment) { 660 mActivity.onAttachFragment(fragment); 661 } 662 663 @Nullable 664 @Override 665 public View onFindViewById(int id) { 666 return mActivity.findViewById(id); 667 } 668 669 @Override 670 public boolean onHasView() { 671 final Window w = mActivity.getWindow(); 672 return (w != null && w.peekDecorView() != null); 673 } 674 } 675 676 public static class SimpleFragment extends Fragment { 677 private int mLayoutId; 678 private static final String LAYOUT_ID = "layoutId"; 679 680 @Override 681 public void onCreate(Bundle savedInstanceState) { 682 super.onCreate(savedInstanceState); 683 if (savedInstanceState != null) { 684 mLayoutId = savedInstanceState.getInt(LAYOUT_ID, mLayoutId); 685 } 686 } 687 688 @Override 689 public void onSaveInstanceState(Bundle outState) { 690 super.onSaveInstanceState(outState); 691 outState.putInt(LAYOUT_ID, mLayoutId); 692 } 693 694 @Override 695 public View onCreateView(LayoutInflater inflater, ViewGroup container, 696 Bundle savedInstanceState) { 697 return inflater.inflate(mLayoutId, container, false); 698 } 699 700 public static SimpleFragment create(int layoutId) { 701 SimpleFragment fragment = new SimpleFragment(); 702 fragment.mLayoutId = layoutId; 703 return fragment; 704 } 705 } 706} 707