[go: nahoru, domu]

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