Home | History | Annotate | Download | only in cts
      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 
     18 package android.fragment.cts;
     19 
     20 import static junit.framework.Assert.assertNotNull;
     21 import static junit.framework.Assert.fail;
     22 import static junit.framework.TestCase.assertEquals;
     23 import static junit.framework.TestCase.assertFalse;
     24 import static junit.framework.TestCase.assertNotSame;
     25 import static junit.framework.TestCase.assertNull;
     26 import static junit.framework.TestCase.assertSame;
     27 import static junit.framework.TestCase.assertTrue;
     28 
     29 import static org.mockito.Mockito.mock;
     30 import static org.mockito.Mockito.times;
     31 import static org.mockito.Mockito.verify;
     32 
     33 import android.app.Activity;
     34 import android.app.Fragment;
     35 import android.app.FragmentController;
     36 import android.app.FragmentHostCallback;
     37 import android.app.FragmentManager;
     38 import android.app.FragmentManager.FragmentLifecycleCallbacks;
     39 import android.app.FragmentManagerNonConfig;
     40 import android.content.Context;
     41 import android.content.Intent;
     42 import android.os.Bundle;
     43 import android.os.Parcelable;
     44 import android.test.suitebuilder.annotation.MediumTest;
     45 import android.util.Pair;
     46 import android.view.LayoutInflater;
     47 import android.view.Menu;
     48 import android.view.View;
     49 import android.view.ViewGroup;
     50 import android.view.Window;
     51 import android.widget.TextView;
     52 
     53 import androidx.test.rule.ActivityTestRule;
     54 import androidx.test.runner.AndroidJUnit4;
     55 
     56 import org.junit.Rule;
     57 import org.junit.Test;
     58 import org.junit.runner.RunWith;
     59 
     60 import java.io.FileDescriptor;
     61 import java.io.PrintWriter;
     62 
     63 @MediumTest
     64 @RunWith(AndroidJUnit4.class)
     65 public class FragmentLifecycleTest {
     66 
     67     @Rule
     68     public ActivityTestRule<FragmentTestActivity> mActivityRule =
     69             new ActivityTestRule<FragmentTestActivity>(FragmentTestActivity.class);
     70 
     71     @Test
     72     public void basicLifecycle() throws Throwable {
     73         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
     74         final StrictFragment strictFragment = new StrictFragment();
     75 
     76         // Add fragment; StrictFragment will throw if it detects any violation
     77         // in standard lifecycle method ordering or expected preconditions.
     78         fm.beginTransaction().add(strictFragment, "EmptyHeadless").commit();
     79         executePendingTransactions(fm);
     80 
     81         assertTrue("fragment is not added", strictFragment.isAdded());
     82         assertFalse("fragment is detached", strictFragment.isDetached());
     83         assertTrue("fragment is not resumed", strictFragment.isResumed());
     84 
     85         // Test removal as well; StrictFragment will throw here too.
     86         fm.beginTransaction().remove(strictFragment).commit();
     87         executePendingTransactions(fm);
     88 
     89         assertFalse("fragment is added", strictFragment.isAdded());
     90         assertFalse("fragment is resumed", strictFragment.isResumed());
     91 
     92         // This one is perhaps counterintuitive; "detached" means specifically detached
     93         // but still managed by a FragmentManager. The .remove call above
     94         // should not enter this state.
     95         assertFalse("fragment is detached", strictFragment.isDetached());
     96     }
     97 
     98     @Test
     99     public void detachment() throws Throwable {
    100         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
    101         final StrictFragment f1 = new StrictFragment();
    102         final StrictFragment f2 = new StrictFragment();
    103 
    104         fm.beginTransaction().add(f1, "1").add(f2, "2").commit();
    105         executePendingTransactions(fm);
    106 
    107         assertTrue("fragment 1 is not added", f1.isAdded());
    108         assertTrue("fragment 2 is not added", f2.isAdded());
    109 
    110         // Test detaching fragments using StrictFragment to throw on errors.
    111         fm.beginTransaction().detach(f1).detach(f2).commit();
    112         executePendingTransactions(fm);
    113 
    114         assertTrue("fragment 1 is not detached", f1.isDetached());
    115         assertTrue("fragment 2 is not detached", f2.isDetached());
    116         assertFalse("fragment 1 is added", f1.isAdded());
    117         assertFalse("fragment 2 is added", f2.isAdded());
    118 
    119         // Only reattach f1; leave v2 detached.
    120         fm.beginTransaction().attach(f1).commit();
    121         executePendingTransactions(fm);
    122 
    123         assertTrue("fragment 1 is not added", f1.isAdded());
    124         assertFalse("fragment 1 is detached", f1.isDetached());
    125         assertTrue("fragment 2 is not detached", f2.isDetached());
    126 
    127         // Remove both from the FragmentManager.
    128         fm.beginTransaction().remove(f1).remove(f2).commit();
    129         executePendingTransactions(fm);
    130 
    131         assertFalse("fragment 1 is added", f1.isAdded());
    132         assertFalse("fragment 2 is added", f2.isAdded());
    133         assertFalse("fragment 1 is detached", f1.isDetached());
    134         assertFalse("fragment 2 is detached", f2.isDetached());
    135     }
    136 
    137     @Test
    138     public void basicBackStack() throws Throwable {
    139         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
    140         final StrictFragment f1 = new StrictFragment();
    141         final StrictFragment f2 = new StrictFragment();
    142 
    143         // Add a fragment normally to set up
    144         fm.beginTransaction().add(f1, "1").commit();
    145         executePendingTransactions(fm);
    146 
    147         assertTrue("fragment 1 is not added", f1.isAdded());
    148 
    149         // Remove the first one and add a second. We're not using replace() here since
    150         // these fragments are headless and as of this test writing, replace() only works
    151         // for fragments with views and a container view id.
    152         // Add it to the back stack so we can pop it afterwards.
    153         fm.beginTransaction().remove(f1).add(f2, "2").addToBackStack("stack1").commit();
    154         executePendingTransactions(fm);
    155 
    156         assertFalse("fragment 1 is added", f1.isAdded());
    157         assertTrue("fragment 2 is not added", f2.isAdded());
    158 
    159         // Test popping the stack
    160         fm.popBackStack();
    161         executePendingTransactions(fm);
    162 
    163         assertFalse("fragment 2 is added", f2.isAdded());
    164         assertTrue("fragment 1 is not added", f1.isAdded());
    165     }
    166 
    167     @Test
    168     public void attachBackStack() throws Throwable {
    169         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
    170         final StrictFragment f1 = new StrictFragment();
    171         final StrictFragment f2 = new StrictFragment();
    172 
    173         // Add a fragment normally to set up
    174         fm.beginTransaction().add(f1, "1").commit();
    175         executePendingTransactions(fm);
    176 
    177         assertTrue("fragment 1 is not added", f1.isAdded());
    178 
    179         fm.beginTransaction().detach(f1).add(f2, "2").addToBackStack("stack1").commit();
    180         executePendingTransactions(fm);
    181 
    182         assertTrue("fragment 1 is not detached", f1.isDetached());
    183         assertFalse("fragment 2 is detached", f2.isDetached());
    184         assertFalse("fragment 1 is added", f1.isAdded());
    185         assertTrue("fragment 2 is not added", f2.isAdded());
    186     }
    187 
    188     @Test
    189     public void viewLifecycle() throws Throwable {
    190         // Test basic lifecycle when the fragment creates a view
    191 
    192         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
    193         final StrictViewFragment f1 = new StrictViewFragment();
    194 
    195         fm.beginTransaction().add(android.R.id.content, f1).commit();
    196         executePendingTransactions(fm);
    197 
    198         assertTrue("fragment 1 is not added", f1.isAdded());
    199         final View view = f1.getView();
    200         assertNotNull("fragment 1 returned null from getView", view);
    201         assertTrue("fragment 1's view is not attached to a window", view.isAttachedToWindow());
    202 
    203         fm.beginTransaction().remove(f1).commit();
    204         executePendingTransactions(fm);
    205 
    206         assertFalse("fragment 1 is added", f1.isAdded());
    207         assertNull("fragment 1 returned non-null from getView after removal", f1.getView());
    208         assertFalse("fragment 1's previous view is still attached to a window",
    209                 view.isAttachedToWindow());
    210     }
    211 
    212     @Test
    213     public void viewReplace() throws Throwable {
    214         // Replace one view with another, then reverse it with the back stack
    215 
    216         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
    217         final StrictViewFragment f1 = new StrictViewFragment();
    218         final StrictViewFragment f2 = new StrictViewFragment();
    219 
    220         fm.beginTransaction().add(android.R.id.content, f1).commit();
    221         executePendingTransactions(fm);
    222 
    223         assertTrue("fragment 1 is not added", f1.isAdded());
    224 
    225         View origView1 = f1.getView();
    226         assertNotNull("fragment 1 returned null view", origView1);
    227         assertTrue("fragment 1's view not attached", origView1.isAttachedToWindow());
    228 
    229         fm.beginTransaction().replace(android.R.id.content, f2).addToBackStack("stack1").commit();
    230         executePendingTransactions(fm);
    231 
    232         assertFalse("fragment 1 is added", f1.isAdded());
    233         assertTrue("fragment 2 is added", f2.isAdded());
    234         assertNull("fragment 1 returned non-null view", f1.getView());
    235         assertFalse("fragment 1's old view still attached", origView1.isAttachedToWindow());
    236         View origView2 = f2.getView();
    237         assertNotNull("fragment 2 returned null view", origView2);
    238         assertTrue("fragment 2's view not attached", origView2.isAttachedToWindow());
    239 
    240         fm.popBackStack();
    241         executePendingTransactions(fm);
    242 
    243         assertTrue("fragment 1 is not added", f1.isAdded());
    244         assertFalse("fragment 2 is added", f2.isAdded());
    245         assertNull("fragment 2 returned non-null view", f2.getView());
    246         assertFalse("fragment 2's view still attached", origView2.isAttachedToWindow());
    247         View newView1 = f1.getView();
    248         assertNotSame("fragment 1 had same view from last attachment", origView1, newView1);
    249         assertTrue("fragment 1's view not attached", newView1.isAttachedToWindow());
    250     }
    251 
    252     @Test
    253     public void viewReplaceMultiple() throws Throwable {
    254         // Replace several views with one, then reverse it with the back stack
    255 
    256         final FragmentManager fm = mActivityRule.getActivity().getFragmentManager();
    257         final StrictViewFragment f1 = new StrictViewFragment();
    258         final StrictViewFragment f2 = new StrictViewFragment();
    259         final StrictViewFragment f3 = new StrictViewFragment();
    260 
    261         fm.beginTransaction().add(android.R.id.content, f1).commit();
    262         fm.beginTransaction().add(android.R.id.content, f2).commit();
    263         executePendingTransactions(fm);
    264 
    265         assertTrue("fragment 1 is not added", f1.isAdded());
    266         assertTrue("fragment 2 is not added", f2.isAdded());
    267 
    268         View origView1 = f1.getView();
    269         assertNotNull("fragment 1 returned null view", origView1);
    270         assertTrue("fragment 1's view not attached", origView1.isAttachedToWindow());
    271         assertSame(origView1, ((ViewGroup)origView1.getParent()).getChildAt(0));
    272 
    273         View origView2 = f2.getView();
    274         assertNotNull("fragment 2 returned null view", origView2);
    275         assertTrue("fragment 2's view not attached", origView2.isAttachedToWindow());
    276         assertSame(origView2, ((ViewGroup)origView1.getParent()).getChildAt(1));
    277 
    278         fm.beginTransaction().replace(android.R.id.content, f3).addToBackStack("stack1").commit();
    279         executePendingTransactions(fm);
    280 
    281         assertFalse("fragment 1 is added", f1.isAdded());
    282         assertFalse("fragment 2 is added", f2.isAdded());
    283         assertTrue("fragment 3 is added", f3.isAdded());
    284         assertNull("fragment 1 returned non-null view", f1.getView());
    285         assertNull("fragment 2 returned non-null view", f2.getView());
    286         assertFalse("fragment 1's old view still attached", origView1.isAttachedToWindow());
    287         assertFalse("fragment 2's old view still attached", origView2.isAttachedToWindow());
    288         View origView3 = f3.getView();
    289         assertNotNull("fragment 3 returned null view", origView3);
    290         assertTrue("fragment 3's view not attached", origView3.isAttachedToWindow());
    291 
    292         fm.popBackStack();
    293         executePendingTransactions(fm);
    294 
    295         assertTrue("fragment 1 is not added", f1.isAdded());
    296         assertTrue("fragment 2 is not added", f2.isAdded());
    297         assertFalse("fragment 3 is added", f3.isAdded());
    298         assertNull("fragment 3 returned non-null view", f3.getView());
    299         assertFalse("fragment 3's view still attached", origView3.isAttachedToWindow());
    300         View newView1 = f1.getView();
    301         View newView2 = f2.getView();
    302         assertNotSame("fragment 1 had same view from last attachment", origView1, newView1);
    303         assertNotSame("fragment 2 had same view from last attachment", origView2, newView1);
    304         assertTrue("fragment 1's view not attached", newView1.isAttachedToWindow());
    305         assertTrue("fragment 2's view not attached", newView2.isAttachedToWindow());
    306         assertSame(newView1, ((ViewGroup)newView1.getParent()).getChildAt(0));
    307         assertSame(newView2, ((ViewGroup)newView1.getParent()).getChildAt(1));
    308     }
    309 
    310     /**
    311      * This tests that fragments call onDestroy when the activity finishes.
    312      */
    313     @Test
    314     public void fragmentDestroyedOnFinish() throws Throwable {
    315         final FragmentController fc = FragmentTestUtil.createController(mActivityRule);
    316         FragmentTestUtil.resume(mActivityRule, fc, null);
    317         final StrictViewFragment fragmentA = StrictViewFragment.create(R.layout.text_a);
    318         final StrictViewFragment fragmentB = StrictViewFragment.create(R.layout.text_b);
    319         mActivityRule.runOnUiThread(() -> {
    320             FragmentManager fm = fc.getFragmentManager();
    321 
    322             fm.beginTransaction()
    323                     .add(android.R.id.content, fragmentA)
    324                     .commit();
    325             fm.executePendingTransactions();
    326             fm.beginTransaction()
    327                     .replace(android.R.id.content, fragmentB)
    328                     .addToBackStack(null)
    329                     .commit();
    330             fm.executePendingTransactions();
    331         });
    332         FragmentTestUtil.destroy(mActivityRule, fc);
    333         assertTrue(fragmentB.mCalledOnDestroy);
    334         assertTrue(fragmentA.mCalledOnDestroy);
    335     }
    336 
    337     /**
    338      * This test confirms that as long as a parent fragment has called super.onCreate,
    339      * any child fragments added, committed and with transactions executed will be brought
    340      * to at least the CREATED state by the time the parent fragment receives onCreateView.
    341      * This means the child fragment will have received onAttach/onCreate.
    342      */
    343     @Test
    344     @MediumTest
    345     public void childFragmentManagerAttach() throws Throwable {
    346         mActivityRule.runOnUiThread(new Runnable() {
    347             public void run() {
    348                 FragmentController fc = FragmentController.createController(
    349                         new HostCallbacks(mActivityRule.getActivity()));
    350                 fc.attachHost(null);
    351                 fc.dispatchCreate();
    352 
    353                 FragmentLifecycleCallbacks mockLc = mock(FragmentLifecycleCallbacks.class);
    354                 FragmentLifecycleCallbacks mockRecursiveLc = mock(FragmentLifecycleCallbacks.class);
    355 
    356                 FragmentManager fm = fc.getFragmentManager();
    357                 fm.registerFragmentLifecycleCallbacks(mockLc, false);
    358                 fm.registerFragmentLifecycleCallbacks(mockRecursiveLc, true);
    359 
    360                 ChildFragmentManagerFragment fragment = new ChildFragmentManagerFragment();
    361                 fm.beginTransaction()
    362                         .add(android.R.id.content, fragment)
    363                         .commitNow();
    364 
    365                 verify(mockLc, times(1)).onFragmentCreated(fm, fragment, null);
    366 
    367                 fc.dispatchActivityCreated();
    368 
    369                 Fragment childFragment = fragment.getChildFragment();
    370 
    371                 verify(mockLc, times(1)).onFragmentActivityCreated(fm, fragment, null);
    372                 verify(mockRecursiveLc, times(1)).onFragmentActivityCreated(fm, fragment, null);
    373                 verify(mockRecursiveLc, times(1)).onFragmentActivityCreated(fm, childFragment, null);
    374 
    375                 fc.dispatchStart();
    376 
    377                 verify(mockLc, times(1)).onFragmentStarted(fm, fragment);
    378                 verify(mockRecursiveLc, times(1)).onFragmentStarted(fm, fragment);
    379                 verify(mockRecursiveLc, times(1)).onFragmentStarted(fm, childFragment);
    380 
    381                 fc.dispatchResume();
    382 
    383                 verify(mockLc, times(1)).onFragmentResumed(fm, fragment);
    384                 verify(mockRecursiveLc, times(1)).onFragmentResumed(fm, fragment);
    385                 verify(mockRecursiveLc, times(1)).onFragmentResumed(fm, childFragment);
    386 
    387                 // Confirm that the parent fragment received onAttachFragment
    388                 assertTrue("parent fragment did not receive onAttachFragment",
    389                         fragment.mCalledOnAttachFragment);
    390 
    391                 fc.dispatchStop();
    392 
    393                 verify(mockLc, times(1)).onFragmentStopped(fm, fragment);
    394                 verify(mockRecursiveLc, times(1)).onFragmentStopped(fm, fragment);
    395                 verify(mockRecursiveLc, times(1)).onFragmentStopped(fm, childFragment);
    396 
    397                 fc.dispatchDestroy();
    398 
    399                 verify(mockLc, times(1)).onFragmentDestroyed(fm, fragment);
    400                 verify(mockRecursiveLc, times(1)).onFragmentDestroyed(fm, fragment);
    401                 verify(mockRecursiveLc, times(1)).onFragmentDestroyed(fm, childFragment);
    402             }
    403         });
    404     }
    405 
    406     /**
    407      * Test to ensure that when dispatch* is called that the fragment manager
    408      * doesn't cause the contained fragment states to change even if no state changes.
    409      */
    410     @Test
    411     public void noPrematureStateChange() throws Throwable {
    412         final FragmentController fc = FragmentTestUtil.createController(mActivityRule);
    413         FragmentTestUtil.resume(mActivityRule, fc, null);
    414 
    415         mActivityRule.runOnUiThread(() -> {
    416             fc.getFragmentManager().beginTransaction()
    417                     .add(new StrictFragment(), "1")
    418                     .commitNow();
    419         });
    420 
    421         Pair<Parcelable, FragmentManagerNonConfig> savedState =
    422                 FragmentTestUtil.destroy(mActivityRule, fc);
    423 
    424         final FragmentController fragmentController = FragmentTestUtil.createController(mActivityRule);
    425 
    426         mActivityRule.runOnUiThread(() -> {
    427             fragmentController.attachHost(null);
    428             fragmentController.dispatchCreate();
    429             fragmentController.dispatchActivityCreated();
    430             fragmentController.noteStateNotSaved();
    431             fragmentController.execPendingActions();
    432             fragmentController.dispatchStart();
    433             fragmentController.reportLoaderStart();
    434             fragmentController.dispatchResume();
    435             fragmentController.restoreAllState(savedState.first, savedState.second);
    436             fragmentController.dispatchResume();
    437         });
    438 
    439         FragmentManager fm = fragmentController.getFragmentManager();
    440 
    441         StrictFragment fragment1 = (StrictFragment) fm.findFragmentByTag("1");
    442 
    443         assertNotNull(fragment1);
    444         assertFalse(fragment1.mCalledOnResume);
    445     }
    446 
    447     @Test
    448     public void testIsStateSaved() throws Throwable {
    449         FragmentController fc = FragmentTestUtil.createController(mActivityRule);
    450         FragmentTestUtil.resume(mActivityRule, fc, null);
    451         FragmentManager fm = fc.getFragmentManager();
    452 
    453         mActivityRule.runOnUiThread(new Runnable() {
    454             @Override
    455             public void run() {
    456                 Fragment f = new StrictFragment();
    457                 fm.beginTransaction()
    458                         .add(f, "1")
    459                         .commitNow();
    460 
    461                 assertFalse("fragment reported state saved while resumed",
    462                         f.isStateSaved());
    463 
    464                 fc.dispatchPause();
    465                 fc.saveAllState();
    466 
    467                 assertTrue("fragment reported state not saved after saveAllState",
    468                         f.isStateSaved());
    469 
    470                 fc.dispatchStop();
    471 
    472                 assertTrue("fragment reported state not saved after stop",
    473                         f.isStateSaved());
    474 
    475                 fc.dispatchDestroy();
    476 
    477                 assertFalse("fragment reported state saved after destroy",
    478                         f.isStateSaved());
    479             }
    480         });
    481     }
    482 
    483     @Test
    484     public void testSetArgumentsLifecycle() throws Throwable {
    485         FragmentController fc = FragmentTestUtil.createController(mActivityRule);
    486         FragmentTestUtil.resume(mActivityRule, fc, null);
    487         FragmentManager fm = fc.getFragmentManager();
    488 
    489         mActivityRule.runOnUiThread(new Runnable() {
    490             @Override
    491             public void run() {
    492                 Fragment f = new StrictFragment();
    493                 f.setArguments(new Bundle());
    494 
    495                 fm.beginTransaction()
    496                         .add(f, "1")
    497                         .commitNow();
    498 
    499                 f.setArguments(new Bundle());
    500 
    501                 fc.dispatchPause();
    502                 fc.saveAllState();
    503 
    504                 boolean threw = false;
    505                 try {
    506                     f.setArguments(new Bundle());
    507                 } catch (IllegalStateException ise) {
    508                     threw = true;
    509                 }
    510                 assertTrue("fragment allowed setArguments after state save", threw);
    511 
    512                 fc.dispatchStop();
    513 
    514                 threw = false;
    515                 try {
    516                     f.setArguments(new Bundle());
    517                 } catch (IllegalStateException ise) {
    518                     threw = true;
    519                 }
    520                 assertTrue("fragment allowed setArguments after stop", threw);
    521 
    522                 fc.dispatchDestroy();
    523 
    524                 // Fully destroyed, so fragments have been removed.
    525                 f.setArguments(new Bundle());
    526             }
    527         });
    528 
    529     }
    530 
    531     /*
    532      * Test that target fragments are in a useful state when we restore them, even if they're
    533      * on the back stack.
    534      */
    535 
    536     @Test
    537     public void targetFragmentRestoreLifecycleStateBackStack() throws Throwable {
    538         mActivityRule.runOnUiThread(new Runnable() {
    539             @Override
    540             public void run() {
    541                 final FragmentController fc1 = FragmentController.createController(
    542                         new HostCallbacks(mActivityRule.getActivity()));
    543 
    544                 final FragmentManager fm1 = fc1.getFragmentManager();
    545 
    546                 fc1.attachHost(null);
    547                 fc1.dispatchCreate();
    548 
    549                 final Fragment target = new TargetFragment();
    550                 fm1.beginTransaction().add(target, "target").commitNow();
    551 
    552                 final Fragment referrer = new ReferrerFragment();
    553                 referrer.setTargetFragment(target, 0);
    554 
    555                 fm1.beginTransaction()
    556                         .remove(target)
    557                         .add(referrer, "referrer")
    558                         .addToBackStack(null)
    559                         .commit();
    560 
    561                 fc1.dispatchActivityCreated();
    562                 fc1.noteStateNotSaved();
    563                 fc1.execPendingActions();
    564                 fc1.doLoaderStart();
    565                 fc1.dispatchStart();
    566                 fc1.reportLoaderStart();
    567                 fc1.dispatchResume();
    568                 fc1.execPendingActions();
    569 
    570                 // Bring the state back down to destroyed, simulating an activity restart
    571                 fc1.dispatchPause();
    572                 final Parcelable savedState = fc1.saveAllState();
    573                 final FragmentManagerNonConfig nonconf = fc1.retainNestedNonConfig();
    574                 fc1.dispatchStop();
    575                 fc1.dispatchDestroy();
    576 
    577                 final FragmentController fc2 = FragmentController.createController(
    578                         new HostCallbacks(mActivityRule.getActivity()));
    579 
    580                 fc2.attachHost(null);
    581                 fc2.restoreAllState(savedState, nonconf);
    582                 fc2.dispatchCreate();
    583 
    584                 fc2.dispatchActivityCreated();
    585                 fc2.noteStateNotSaved();
    586                 fc2.execPendingActions();
    587                 fc2.doLoaderStart();
    588                 fc2.dispatchStart();
    589                 fc2.reportLoaderStart();
    590                 fc2.dispatchResume();
    591                 fc2.execPendingActions();
    592 
    593                 // Bring the state back down to destroyed before we finish the test
    594                 fc2.dispatchPause();
    595                 fc2.saveAllState();
    596                 fc2.dispatchStop();
    597                 fc2.dispatchDestroy();
    598             }
    599         });
    600     }
    601 
    602     @Test
    603     public void targetFragmentRestoreLifecycleStateManagerOrder() throws Throwable {
    604         mActivityRule.runOnUiThread(new Runnable() {
    605             @Override
    606             public void run() {
    607                 final FragmentController fc1 = FragmentController.createController(
    608                         new HostCallbacks(mActivityRule.getActivity()));
    609 
    610                 final FragmentManager fm1 = fc1.getFragmentManager();
    611 
    612                 fc1.attachHost(null);
    613                 fc1.dispatchCreate();
    614 
    615                 final Fragment target1 = new TargetFragment();
    616                 final Fragment referrer1 = new ReferrerFragment();
    617                 referrer1.setTargetFragment(target1, 0);
    618 
    619                 fm1.beginTransaction().add(target1, "target1").add(referrer1, "referrer1").commitNow();
    620 
    621                 final Fragment target2 = new TargetFragment();
    622                 final Fragment referrer2 = new ReferrerFragment();
    623                 referrer2.setTargetFragment(target2, 0);
    624 
    625                 // Order shouldn't matter.
    626                 fm1.beginTransaction().add(referrer2, "referrer2").add(target2, "target2").commitNow();
    627 
    628                 fc1.dispatchActivityCreated();
    629                 fc1.noteStateNotSaved();
    630                 fc1.execPendingActions();
    631                 fc1.doLoaderStart();
    632                 fc1.dispatchStart();
    633                 fc1.reportLoaderStart();
    634                 fc1.dispatchResume();
    635                 fc1.execPendingActions();
    636 
    637                 // Bring the state back down to destroyed, simulating an activity restart
    638                 fc1.dispatchPause();
    639                 final Parcelable savedState = fc1.saveAllState();
    640                 final FragmentManagerNonConfig nonconf = fc1.retainNestedNonConfig();
    641                 fc1.dispatchStop();
    642                 fc1.dispatchDestroy();
    643 
    644                 final FragmentController fc2 = FragmentController.createController(
    645                         new HostCallbacks(mActivityRule.getActivity()));
    646 
    647                 fc2.attachHost(null);
    648                 fc2.restoreAllState(savedState, nonconf);
    649                 fc2.dispatchCreate();
    650 
    651                 fc2.dispatchActivityCreated();
    652                 fc2.noteStateNotSaved();
    653                 fc2.execPendingActions();
    654                 fc2.doLoaderStart();
    655                 fc2.dispatchStart();
    656                 fc2.reportLoaderStart();
    657                 fc2.dispatchResume();
    658                 fc2.execPendingActions();
    659 
    660                 // Bring the state back down to destroyed before we finish the test
    661                 fc2.dispatchPause();
    662                 fc2.saveAllState();
    663                 fc2.dispatchStop();
    664                 fc2.dispatchDestroy();
    665             }
    666         });
    667     }
    668 
    669     // Make sure that executing transactions during activity lifecycle events
    670     // is properly prevented.
    671     @Test
    672     public void preventReentrantCalls() throws Throwable {
    673         testLifecycleTransitionFailure(StrictFragment.ATTACHED, StrictFragment.CREATED);
    674         testLifecycleTransitionFailure(StrictFragment.CREATED, StrictFragment.ACTIVITY_CREATED);
    675         testLifecycleTransitionFailure(StrictFragment.ACTIVITY_CREATED, StrictFragment.STARTED);
    676         testLifecycleTransitionFailure(StrictFragment.STARTED, StrictFragment.RESUMED);
    677 
    678         testLifecycleTransitionFailure(StrictFragment.RESUMED, StrictFragment.STARTED);
    679         testLifecycleTransitionFailure(StrictFragment.STARTED, StrictFragment.CREATED);
    680         testLifecycleTransitionFailure(StrictFragment.CREATED, StrictFragment.ATTACHED);
    681         testLifecycleTransitionFailure(StrictFragment.ATTACHED, StrictFragment.DETACHED);
    682     }
    683 
    684     private void testLifecycleTransitionFailure(int fromState, int toState) throws Throwable {
    685         mActivityRule.runOnUiThread(() -> {
    686             final FragmentController fc1 = FragmentController.createController(
    687                     new HostCallbacks(mActivityRule.getActivity()));
    688             FragmentTestUtil.resume(mActivityRule, fc1, null);
    689 
    690             final FragmentManager fm1 = fc1.getFragmentManager();
    691 
    692             final Fragment reentrantFragment = ReentrantFragment.create(fromState, toState);
    693 
    694             fm1.beginTransaction()
    695                     .add(reentrantFragment, "reentrant")
    696                     .commit();
    697             try {
    698                 fm1.executePendingTransactions();
    699             } catch (IllegalStateException e) {
    700                 fail("An exception shouldn't happen when initially adding the fragment");
    701             }
    702 
    703             // Now shut down the fragment controller. When fromState > toState, this should
    704             // result in an exception
    705             Pair<Parcelable, FragmentManagerNonConfig> savedState = null;
    706             try {
    707                 savedState = FragmentTestUtil.destroy(mActivityRule, fc1);
    708                 if (fromState > toState) {
    709                     fail("Expected IllegalStateException when moving from "
    710                             + StrictFragment.stateToString(fromState) + " to "
    711                             + StrictFragment.stateToString(toState));
    712                 }
    713             } catch (IllegalStateException e) {
    714                 if (fromState < toState) {
    715                     fail("Unexpected IllegalStateException when moving from "
    716                             + StrictFragment.stateToString(fromState) + " to "
    717                             + StrictFragment.stateToString(toState));
    718                 }
    719                 return; // test passed!
    720             }
    721 
    722             // now restore from saved state. This will be reached when
    723             // fromState < toState. We want to catch the fragment while it
    724             // is being restored as the fragment controller state is being brought up.
    725 
    726             final FragmentController fc2 = FragmentController.createController(
    727                     new HostCallbacks(mActivityRule.getActivity()));
    728             try {
    729                 FragmentTestUtil.resume(mActivityRule, fc2, savedState);
    730 
    731                 fail("Expected IllegalStateException when moving from "
    732                         + StrictFragment.stateToString(fromState) + " to "
    733                         + StrictFragment.stateToString(toState));
    734             } catch (IllegalStateException e) {
    735                 // expected, so the test passed!
    736             }
    737         });
    738     }
    739 
    740     @Test
    741     public void targetFragmentNoCycles() throws Throwable {
    742         final Fragment one = new Fragment();
    743         final Fragment two = new Fragment();
    744         final Fragment three = new Fragment();
    745 
    746         try {
    747             one.setTargetFragment(two, 0);
    748             two.setTargetFragment(three, 0);
    749             three.setTargetFragment(one, 0);
    750             assertTrue("creating a fragment target cycle did not throw IllegalArgumentException",
    751                     false);
    752         } catch (IllegalArgumentException e) {
    753             // Success!
    754         }
    755     }
    756 
    757     @Test
    758     public void targetFragmentSetClear() throws Throwable {
    759         final Fragment one = new Fragment();
    760         final Fragment two = new Fragment();
    761 
    762         one.setTargetFragment(two, 0);
    763         one.setTargetFragment(null, 0);
    764     }
    765 
    766     /**
    767      * When a fragment is saved in non-config, it should be restored to the same index.
    768      */
    769     @Test
    770     public void restoreNonConfig() throws Throwable {
    771         mActivityRule.runOnUiThread(() -> {
    772             FragmentController fc = FragmentTestUtil.createController(mActivityRule);
    773             FragmentTestUtil.resume(mActivityRule, fc, null);
    774             FragmentManager fm = fc.getFragmentManager();
    775 
    776             Fragment fragment1 = new StrictFragment();
    777             fm.beginTransaction()
    778                     .add(fragment1, "1")
    779                     .addToBackStack(null)
    780                     .commit();
    781             fm.executePendingTransactions();
    782             Fragment fragment2 = new StrictFragment();
    783             fragment2.setRetainInstance(true);
    784             fragment2.setTargetFragment(fragment1, 0);
    785             Fragment fragment3 = new StrictFragment();
    786             fm.beginTransaction()
    787                     .remove(fragment1)
    788                     .add(fragment2, "2")
    789                     .add(fragment3, "3")
    790                     .addToBackStack(null)
    791                     .commit();
    792             fm.executePendingTransactions();
    793 
    794             Pair<Parcelable, FragmentManagerNonConfig> savedState =
    795                     FragmentTestUtil.destroy(mActivityRule, fc);
    796 
    797             fc = FragmentTestUtil.createController(mActivityRule);
    798             FragmentTestUtil.resume(mActivityRule, fc, savedState);
    799             boolean foundFragment2 = false;
    800             for (Fragment fragment : fc.getFragmentManager().getFragments()) {
    801                 if (fragment == fragment2) {
    802                     foundFragment2 = true;
    803                     assertNotNull(fragment.getTargetFragment());
    804                     assertEquals("1", fragment.getTargetFragment().getTag());
    805                 } else {
    806                     assertFalse("2".equals(fragment.getTag()));
    807                 }
    808             }
    809             assertTrue(foundFragment2);
    810         });
    811     }
    812 
    813     /**
    814      * Check that retained fragments in the backstack correctly restored after two "configChanges"
    815      */
    816     @Test
    817     public void retainedFragmentInBackstack() throws Throwable {
    818         mActivityRule.runOnUiThread(() -> {
    819             FragmentController fc = FragmentTestUtil.createController(mActivityRule);
    820             FragmentTestUtil.resume(mActivityRule, fc, null);
    821             FragmentManager fm = fc.getFragmentManager();
    822 
    823             Fragment fragment1 = new StrictFragment();
    824             fm.beginTransaction()
    825                     .add(fragment1, "1")
    826                     .addToBackStack(null)
    827                     .commit();
    828             fm.executePendingTransactions();
    829 
    830             Fragment child = new StrictFragment();
    831             child.setRetainInstance(true);
    832             fragment1.getChildFragmentManager().beginTransaction()
    833                     .add(child, "child").commit();
    834             fragment1.getChildFragmentManager().executePendingTransactions();
    835 
    836             Fragment fragment2 = new StrictFragment();
    837             fm.beginTransaction()
    838                     .remove(fragment1)
    839                     .add(fragment2, "2")
    840                     .addToBackStack(null)
    841                     .commit();
    842             fm.executePendingTransactions();
    843 
    844             Pair<Parcelable, FragmentManagerNonConfig> savedState =
    845                     FragmentTestUtil.destroy(mActivityRule, fc);
    846 
    847             fc = FragmentTestUtil.createController(mActivityRule);
    848             FragmentTestUtil.resume(mActivityRule, fc, savedState);
    849             savedState = FragmentTestUtil.destroy(mActivityRule, fc);
    850             fc = FragmentTestUtil.createController(mActivityRule);
    851             FragmentTestUtil.resume(mActivityRule, fc, savedState);
    852             fm = fc.getFragmentManager();
    853             fm.popBackStackImmediate();
    854             Fragment retainedChild = fm.findFragmentByTag("1")
    855                     .getChildFragmentManager().findFragmentByTag("child");
    856             assertEquals(child, retainedChild);
    857         });
    858     }
    859 
    860     /**
    861      * When a fragment has been optimized out, it state should still be saved during
    862      * save and restore instance state.
    863      */
    864     @Test
    865     public void saveRemovedFragment() throws Throwable {
    866         mActivityRule.runOnUiThread(() -> {
    867             FragmentController fc = FragmentTestUtil.createController(mActivityRule);
    868             FragmentTestUtil.resume(mActivityRule, fc, null);
    869             FragmentManager fm = fc.getFragmentManager();
    870 
    871             SaveStateFragment fragment1 = SaveStateFragment.create(1);
    872             fm.beginTransaction()
    873                     .add(android.R.id.content, fragment1, "1")
    874                     .addToBackStack(null)
    875                     .commit();
    876             SaveStateFragment fragment2 = SaveStateFragment.create(2);
    877             fm.beginTransaction()
    878                     .replace(android.R.id.content, fragment2, "2")
    879                     .addToBackStack(null)
    880                     .commit();
    881             fm.executePendingTransactions();
    882 
    883             Pair<Parcelable, FragmentManagerNonConfig> savedState =
    884                     FragmentTestUtil.destroy(mActivityRule, fc);
    885 
    886             fc = FragmentTestUtil.createController(mActivityRule);
    887             FragmentTestUtil.resume(mActivityRule, fc, savedState);
    888             fm = fc.getFragmentManager();
    889             fragment2 = (SaveStateFragment) fm.findFragmentByTag("2");
    890             assertNotNull(fragment2);
    891             assertEquals(2, fragment2.getValue());
    892             fm.popBackStackImmediate();
    893             fragment1 = (SaveStateFragment) fm.findFragmentByTag("1");
    894             assertNotNull(fragment1);
    895             assertEquals(1, fragment1.getValue());
    896         });
    897     }
    898 
    899     /**
    900      * When there are no retained instance fragments, the FragmentManagerNonConfig should be
    901      * null
    902      */
    903     @Test
    904     public void nullNonConfig() throws Throwable {
    905         mActivityRule.runOnUiThread(() -> {
    906             FragmentController fc = FragmentTestUtil.createController(mActivityRule);
    907             FragmentTestUtil.resume(mActivityRule, fc, null);
    908             FragmentManager fm = fc.getFragmentManager();
    909 
    910             Fragment fragment1 = new StrictFragment();
    911             fm.beginTransaction()
    912                     .add(fragment1, "1")
    913                     .addToBackStack(null)
    914                     .commit();
    915             fm.executePendingTransactions();
    916             Pair<Parcelable, FragmentManagerNonConfig> savedState =
    917                     FragmentTestUtil.destroy(mActivityRule, fc);
    918             assertNull(savedState.second);
    919         });
    920     }
    921 
    922     /**
    923      * When the FragmentManager state changes, the pending transactions should execute.
    924      */
    925     @Test
    926     public void runTransactionsOnChange() throws Throwable {
    927         mActivityRule.runOnUiThread(() -> {
    928             FragmentController fc = FragmentTestUtil.createController(mActivityRule);
    929             FragmentTestUtil.resume(mActivityRule, fc, null);
    930             FragmentManager fm = fc.getFragmentManager();
    931 
    932             RemoveHelloInOnResume fragment1 = new RemoveHelloInOnResume();
    933             StrictFragment fragment2 = new StrictFragment();
    934             fm.beginTransaction()
    935                     .add(fragment1, "1")
    936                     .setReorderingAllowed(false)
    937                     .commit();
    938             fm.beginTransaction()
    939                     .add(fragment2, "Hello")
    940                     .setReorderingAllowed(false)
    941                     .commit();
    942             fm.executePendingTransactions();
    943 
    944             assertEquals(2, fm.getFragments().size());
    945             assertTrue(fm.getFragments().contains(fragment1));
    946             assertTrue(fm.getFragments().contains(fragment2));
    947 
    948             Pair<Parcelable, FragmentManagerNonConfig> savedState =
    949                     FragmentTestUtil.destroy(mActivityRule, fc);
    950             fc = FragmentTestUtil.createController(mActivityRule);
    951             FragmentTestUtil.resume(mActivityRule, fc, savedState);
    952             fm = fc.getFragmentManager();
    953 
    954             assertEquals(1, fm.getFragments().size());
    955             for (Fragment fragment : fm.getFragments()) {
    956                 assertTrue(fragment instanceof RemoveHelloInOnResume);
    957             }
    958         });
    959     }
    960 
    961     @Test
    962     public void optionsMenu() throws Throwable {
    963         mActivityRule.runOnUiThread(() -> {
    964             FragmentController fc = FragmentTestUtil.createController(mActivityRule);
    965             FragmentTestUtil.resume(mActivityRule, fc, null);
    966             FragmentManager fm = fc.getFragmentManager();
    967 
    968             InvalidateOptionFragment fragment = new InvalidateOptionFragment();
    969             fm.beginTransaction()
    970                     .add(android.R.id.content, fragment)
    971                     .commit();
    972             fm.executePendingTransactions();
    973 
    974             Menu menu = mock(Menu.class);
    975             fc.dispatchPrepareOptionsMenu(menu);
    976             assertTrue(fragment.onPrepareOptionsMenuCalled);
    977             fragment.onPrepareOptionsMenuCalled = false;
    978             FragmentTestUtil.destroy(mActivityRule, fc);
    979             fc.dispatchPrepareOptionsMenu(menu);
    980             assertFalse(fragment.onPrepareOptionsMenuCalled);
    981         });
    982     }
    983 
    984 
    985     /**
    986      * When a retained instance fragment is saved while in the back stack, it should go
    987      * through onCreate() when it is popped back.
    988      */
    989     @Test
    990     public void retainInstanceWithOnCreate() throws Throwable {
    991         mActivityRule.runOnUiThread(() -> {
    992             FragmentController fc = FragmentTestUtil.createController(mActivityRule);
    993             FragmentTestUtil.resume(mActivityRule, fc, null);
    994             FragmentManager fm = fc.getFragmentManager();
    995 
    996             OnCreateFragment fragment1 = new OnCreateFragment();
    997 
    998             fm.beginTransaction()
    999                     .add(fragment1, "1")
   1000                     .commit();
   1001             fm.beginTransaction()
   1002                     .remove(fragment1)
   1003                     .addToBackStack(null)
   1004                     .commit();
   1005 
   1006             Pair<Parcelable, FragmentManagerNonConfig> savedState =
   1007                     FragmentTestUtil.destroy(mActivityRule, fc);
   1008             Pair<Parcelable, FragmentManagerNonConfig> restartState =
   1009                     Pair.create(savedState.first, null);
   1010 
   1011             fc = FragmentTestUtil.createController(mActivityRule);
   1012             FragmentTestUtil.resume(mActivityRule, fc, restartState);
   1013 
   1014             // Save again, but keep the state
   1015             savedState = FragmentTestUtil.destroy(mActivityRule, fc);
   1016 
   1017             fc = FragmentTestUtil.createController(mActivityRule);
   1018             FragmentTestUtil.resume(mActivityRule, fc, savedState);
   1019 
   1020             fm = fc.getFragmentManager();
   1021 
   1022             fm.popBackStackImmediate();
   1023             OnCreateFragment fragment2 = (OnCreateFragment) fm.findFragmentByTag("1");
   1024             assertTrue(fragment2.onCreateCalled);
   1025             fm.popBackStackImmediate();
   1026         });
   1027     }
   1028 
   1029     /**
   1030      * A retained instance fragment should go through onCreate() once, even through save and
   1031      * restore.
   1032      */
   1033     @Test
   1034     public void retainInstanceOneOnCreate() throws Throwable {
   1035         mActivityRule.runOnUiThread(() -> {
   1036             FragmentController fc = FragmentTestUtil.createController(mActivityRule);
   1037             FragmentTestUtil.resume(mActivityRule, fc, null);
   1038             FragmentManager fm = fc.getFragmentManager();
   1039 
   1040             OnCreateFragment fragment = new OnCreateFragment();
   1041 
   1042             fm.beginTransaction()
   1043                     .add(fragment, "fragment")
   1044                     .commit();
   1045             fm.executePendingTransactions();
   1046 
   1047             fm.beginTransaction()
   1048                     .remove(fragment)
   1049                     .addToBackStack(null)
   1050                     .commit();
   1051 
   1052             assertTrue(fragment.onCreateCalled);
   1053             fragment.onCreateCalled = false;
   1054 
   1055             Pair<Parcelable, FragmentManagerNonConfig> savedState =
   1056                     FragmentTestUtil.destroy(mActivityRule, fc);
   1057 
   1058             fc = FragmentTestUtil.createController(mActivityRule);
   1059             FragmentTestUtil.resume(mActivityRule, fc, savedState);
   1060             fm = fc.getFragmentManager();
   1061 
   1062             fm.popBackStackImmediate();
   1063             assertFalse(fragment.onCreateCalled);
   1064         });
   1065     }
   1066 
   1067     private void executePendingTransactions(final FragmentManager fm) throws Throwable {
   1068         mActivityRule.runOnUiThread(new Runnable() {
   1069             @Override
   1070             public void run() {
   1071                 fm.executePendingTransactions();
   1072             }
   1073         });
   1074     }
   1075 
   1076     /**
   1077      * This tests a deliberately odd use of a child fragment, added in onCreateView instead
   1078      * of elsewhere. It simulates creating a UI child fragment added to the view hierarchy
   1079      * created by this fragment.
   1080      */
   1081     public static class ChildFragmentManagerFragment extends StrictFragment {
   1082         private FragmentManager mSavedChildFragmentManager;
   1083         private ChildFragmentManagerChildFragment mChildFragment;
   1084 
   1085         @Override
   1086         public void onAttach(Context context) {
   1087             super.onAttach(context);
   1088             mSavedChildFragmentManager = getChildFragmentManager();
   1089         }
   1090 
   1091 
   1092         @Override
   1093         public View onCreateView(LayoutInflater inflater,  ViewGroup container,
   1094                  Bundle savedInstanceState) {
   1095             assertSame("child FragmentManagers not the same instance", mSavedChildFragmentManager,
   1096                     getChildFragmentManager());
   1097             ChildFragmentManagerChildFragment child =
   1098                     (ChildFragmentManagerChildFragment) mSavedChildFragmentManager
   1099                             .findFragmentByTag("tag");
   1100             if (child == null) {
   1101                 child = new ChildFragmentManagerChildFragment("foo");
   1102                 mSavedChildFragmentManager.beginTransaction()
   1103                         .add(child, "tag")
   1104                         .commitNow();
   1105                 assertEquals("argument strings don't match", "foo", child.getString());
   1106             }
   1107             mChildFragment = child;
   1108             return new TextView(container.getContext());
   1109         }
   1110 
   1111 
   1112         public Fragment getChildFragment() {
   1113             return mChildFragment;
   1114         }
   1115     }
   1116 
   1117     public static class ChildFragmentManagerChildFragment extends StrictFragment {
   1118         private String mString;
   1119 
   1120         public ChildFragmentManagerChildFragment() {
   1121         }
   1122 
   1123         public ChildFragmentManagerChildFragment(String arg) {
   1124             final Bundle b = new Bundle();
   1125             b.putString("string", arg);
   1126             setArguments(b);
   1127         }
   1128 
   1129         @Override
   1130         public void onAttach(Context context) {
   1131             super.onAttach(context);
   1132             mString = getArguments().getString("string", "NO VALUE");
   1133         }
   1134 
   1135         public String getString() {
   1136             return mString;
   1137         }
   1138     }
   1139 
   1140     public static class TargetFragment extends Fragment {
   1141         public boolean calledCreate;
   1142 
   1143         @Override
   1144         public void onCreate(Bundle savedInstanceState) {
   1145             super.onCreate(savedInstanceState);
   1146             calledCreate = true;
   1147         }
   1148     }
   1149 
   1150     public static class ReferrerFragment extends Fragment {
   1151         @Override
   1152         public void onCreate(Bundle savedInstanceState) {
   1153             super.onCreate(savedInstanceState);
   1154 
   1155             Fragment target = getTargetFragment();
   1156             assertNotNull("target fragment was null during referrer onCreate", target);
   1157 
   1158             if (!(target instanceof TargetFragment)) {
   1159                 throw new IllegalStateException("target fragment was not a TargetFragment");
   1160             }
   1161 
   1162             assertTrue("target fragment has not yet been created",
   1163                     ((TargetFragment) target).calledCreate);
   1164         }
   1165     }
   1166 
   1167     static class HostCallbacks extends FragmentHostCallback<Activity> {
   1168         private final Activity mActivity;
   1169 
   1170         public HostCallbacks(Activity activity) {
   1171             super(activity, null, 0);
   1172             mActivity = activity;
   1173         }
   1174 
   1175         @Override
   1176         public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
   1177         }
   1178 
   1179         @Override
   1180         public boolean onShouldSaveFragmentState(Fragment fragment) {
   1181             return !mActivity.isFinishing();
   1182         }
   1183 
   1184         @Override
   1185         public LayoutInflater onGetLayoutInflater() {
   1186             return mActivity.getLayoutInflater().cloneInContext(mActivity);
   1187         }
   1188 
   1189         @Override
   1190         public Activity onGetHost() {
   1191             return mActivity;
   1192         }
   1193 
   1194         @Override
   1195         public void onStartActivityFromFragment(
   1196                 Fragment fragment, Intent intent, int requestCode,  Bundle options) {
   1197             mActivity.startActivityFromFragment(fragment, intent, requestCode, options);
   1198         }
   1199 
   1200         @Override
   1201         public void onRequestPermissionsFromFragment( Fragment fragment,
   1202                  String[] permissions, int requestCode) {
   1203             throw new UnsupportedOperationException();
   1204         }
   1205 
   1206         @Override
   1207         public boolean onHasWindowAnimations() {
   1208             return mActivity.getWindow() != null;
   1209         }
   1210 
   1211         @Override
   1212         public int onGetWindowAnimations() {
   1213             final Window w = mActivity.getWindow();
   1214             return (w == null) ? 0 : w.getAttributes().windowAnimations;
   1215         }
   1216 
   1217         @Override
   1218         public void onAttachFragment(Fragment fragment) {
   1219             mActivity.onAttachFragment(fragment);
   1220         }
   1221 
   1222         @Override
   1223         public View onFindViewById(int id) {
   1224             return mActivity.findViewById(id);
   1225         }
   1226 
   1227         @Override
   1228         public boolean onHasView() {
   1229             final Window w = mActivity.getWindow();
   1230             return (w != null && w.peekDecorView() != null);
   1231         }
   1232     }
   1233 
   1234     public static class SaveStateFragment extends Fragment {
   1235         private static final String VALUE_KEY = "SaveStateFragment.mValue";
   1236         private int mValue;
   1237 
   1238         public static SaveStateFragment create(int value) {
   1239             SaveStateFragment saveStateFragment = new SaveStateFragment();
   1240             saveStateFragment.mValue = value;
   1241             return saveStateFragment;
   1242         }
   1243 
   1244         @Override
   1245         public void onSaveInstanceState(Bundle outState) {
   1246             super.onSaveInstanceState(outState);
   1247             outState.putInt(VALUE_KEY, mValue);
   1248         }
   1249 
   1250         @Override
   1251         public void onCreate(Bundle savedInstanceState) {
   1252             super.onCreate(savedInstanceState);
   1253             if (savedInstanceState != null) {
   1254                 mValue = savedInstanceState.getInt(VALUE_KEY, mValue);
   1255             }
   1256         }
   1257 
   1258         public int getValue() {
   1259             return mValue;
   1260         }
   1261     }
   1262 
   1263     public static class RemoveHelloInOnResume extends Fragment {
   1264         @Override
   1265         public void onResume() {
   1266             super.onResume();
   1267             Fragment fragment = getFragmentManager().findFragmentByTag("Hello");
   1268             if (fragment != null) {
   1269                 getFragmentManager().beginTransaction().remove(fragment).commit();
   1270             }
   1271         }
   1272     }
   1273 
   1274     public static class InvalidateOptionFragment extends Fragment {
   1275         public boolean onPrepareOptionsMenuCalled;
   1276 
   1277         public InvalidateOptionFragment() {
   1278             setHasOptionsMenu(true);
   1279         }
   1280 
   1281         @Override
   1282         public void onPrepareOptionsMenu(Menu menu) {
   1283             onPrepareOptionsMenuCalled = true;
   1284             assertNotNull(getContext());
   1285             super.onPrepareOptionsMenu(menu);
   1286         }
   1287     }
   1288 
   1289     public static class OnCreateFragment extends Fragment {
   1290         public boolean onCreateCalled;
   1291 
   1292         public OnCreateFragment() {
   1293             setRetainInstance(true);
   1294         }
   1295 
   1296         @Override
   1297         public void onCreate(Bundle savedInstanceState) {
   1298             super.onCreate(savedInstanceState);
   1299             onCreateCalled = true;
   1300         }
   1301     }
   1302 }
   1303