-
Notifications
You must be signed in to change notification settings - Fork 945
/
Fragment.java
3562 lines (3285 loc) · 144 KB
/
Fragment.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.fragment.app;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
import android.animation.Animator;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.Application;
import android.content.ComponentCallbacks;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentSender;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnCreateContextMenuListener;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.widget.AdapterView;
import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultCaller;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.ActivityResultRegistry;
import androidx.activity.result.ActivityResultRegistryOwner;
import androidx.activity.result.contract.ActivityResultContract;
import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions;
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult;
import androidx.activity.result.contract.ActivityResultContracts.StartIntentSenderForResult;
import androidx.annotation.CallSuper;
import androidx.annotation.ContentView;
import androidx.annotation.LayoutRes;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.StringRes;
import androidx.annotation.UiThread;
import androidx.arch.core.util.Function;
import androidx.core.app.ActivityOptionsCompat;
import androidx.core.app.SharedElementCallback;
import androidx.core.view.LayoutInflaterCompat;
import androidx.fragment.app.strictmode.FragmentStrictMode;
import androidx.lifecycle.HasDefaultViewModelProviderFactory;
import androidx.lifecycle.Lifecycle;
import androidx.lifecycle.LifecycleEventObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LifecycleRegistry;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.SavedStateViewModelFactory;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelStore;
import androidx.lifecycle.ViewModelStoreOwner;
import androidx.lifecycle.ViewTreeLifecycleOwner;
import androidx.lifecycle.ViewTreeViewModelStoreOwner;
import androidx.loader.app.LoaderManager;
import androidx.savedstate.SavedStateRegistry;
import androidx.savedstate.SavedStateRegistryController;
import androidx.savedstate.SavedStateRegistryOwner;
import androidx.savedstate.ViewTreeSavedStateRegistryOwner;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/**
* Static library support version of the framework's {@link android.app.Fragment}.
* Used to write apps that run on platforms prior to Android 3.0. When running
* on Android 3.0 or above, this implementation is still used; it does not try
* to switch to the framework's implementation. See the framework {@link android.app.Fragment}
* documentation for a class overview.
*
* <p>The main differences when using this support version instead of the framework version are:
* <ul>
* <li>Your activity must extend {@link FragmentActivity}
* <li>You must call {@link FragmentActivity#getSupportFragmentManager} to get the
* {@link FragmentManager}
* </ul>
*
*/
public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener, LifecycleOwner,
ViewModelStoreOwner, HasDefaultViewModelProviderFactory, SavedStateRegistryOwner,
ActivityResultCaller {
static final Object USE_DEFAULT_TRANSITION = new Object();
static final int INITIALIZING = -1; // Not yet attached.
static final int ATTACHED = 0; // Attached to the host.
static final int CREATED = 1; // Created.
static final int VIEW_CREATED = 2; // View Created.
static final int AWAITING_EXIT_EFFECTS = 3; // Downward state, awaiting exit effects
static final int ACTIVITY_CREATED = 4; // Fully created, not started.
static final int STARTED = 5; // Created and started, not resumed.
static final int AWAITING_ENTER_EFFECTS = 6; // Upward state, awaiting enter effects
static final int RESUMED = 7; // Created started and resumed.
int mState = INITIALIZING;
// When instantiated from saved state, this is the saved state.
Bundle mSavedFragmentState;
SparseArray<Parcelable> mSavedViewState;
Bundle mSavedViewRegistryState;
// If the userVisibleHint is changed before the state is set,
// it is stored here
@Nullable Boolean mSavedUserVisibleHint;
// Internal unique name for this fragment;
@NonNull
String mWho = UUID.randomUUID().toString();
// Construction arguments;
Bundle mArguments;
// Target fragment.
Fragment mTarget;
// For use when retaining a fragment: this is the who of the last mTarget.
String mTargetWho = null;
// Target request code.
int mTargetRequestCode;
// Boolean indicating whether this Fragment is the primary navigation fragment
private Boolean mIsPrimaryNavigationFragment = null;
// True if the fragment is in the list of added fragments.
boolean mAdded;
// If set this fragment is being removed from its activity.
boolean mRemoving;
// Set to true if this fragment was instantiated from a layout file.
boolean mFromLayout;
// Set to true when the view has actually been inflated in its layout.
boolean mInLayout;
// True if this fragment has been restored from previously saved state.
boolean mRestored;
// True if performCreateView has been called and a matching call to performDestroyView
// has not yet happened.
boolean mPerformedCreateView;
// Number of active back stack entries this fragment is in.
int mBackStackNesting;
// The fragment manager we are associated with. Set as soon as the
// fragment is used in a transaction; cleared after it has been removed
// from all transactions.
FragmentManager mFragmentManager;
// Host this fragment is attached to.
FragmentHostCallback<?> mHost;
// Private fragment manager for child fragments inside of this one.
@NonNull
FragmentManager mChildFragmentManager = new FragmentManagerImpl();
// If this Fragment is contained in another Fragment, this is that container.
Fragment mParentFragment;
// The optional identifier for this fragment -- either the container ID if it
// was dynamically added to the view hierarchy, or the ID supplied in
// layout.
int mFragmentId;
// When a fragment is being dynamically added to the view hierarchy, this
// is the identifier of the parent container it is being added to.
int mContainerId;
// The optional named tag for this fragment -- usually used to find
// fragments that are not part of the layout.
String mTag;
// Set to true when the app has requested that this fragment be hidden
// from the user.
boolean mHidden;
// Set to true when the app has requested that this fragment be deactivated.
boolean mDetached;
// If set this fragment would like its instance retained across
// configuration changes.
boolean mRetainInstance;
// If set this fragment changed its mRetainInstance while it was detached
boolean mRetainInstanceChangedWhileDetached;
// If set this fragment has menu items to contribute.
boolean mHasMenu;
// Set to true to allow the fragment's menu to be shown.
boolean mMenuVisible = true;
// Used to verify that subclasses call through to super class.
private boolean mCalled;
// The parent container of the fragment after dynamically added to UI.
ViewGroup mContainer;
// The View generated for this fragment.
View mView;
// Whether this fragment should defer starting until after other fragments
// have been started and their loaders are finished.
boolean mDeferStart;
// Hint provided by the app that this fragment is currently visible to the user.
boolean mUserVisibleHint = true;
// The animation and transition information for the fragment. This will be null
// unless the elements are explicitly accessed and should remain null for Fragments
// without Views.
AnimationInfo mAnimationInfo;
// Runnable that is used to indicate if the Fragment has a postponed transition that is on a
// timeout.
Runnable mPostponedDurationRunnable = new Runnable() {
@Override
public void run() {
startPostponedEnterTransition();
}
};
// True if the View was added, and its animation has yet to be run. This could
// also indicate that the fragment view hasn't been made visible, even if there is no
// animation for this fragment.
boolean mIsNewlyAdded;
// True if mHidden has been changed and the animation should be scheduled.
boolean mHiddenChanged;
// The alpha of the view when the view was added and then postponed. If the value is less
// than zero, this means that the view's add was canceled and should not participate in
// removal animations.
float mPostponedAlpha;
// The cached value from onGetLayoutInflater(Bundle) that will be returned from
// getLayoutInflater()
LayoutInflater mLayoutInflater;
// Keep track of whether or not this Fragment has run performCreate(). Retained instance
// fragments can have mRetaining set to true without going through creation, so we must
// track it separately.
boolean mIsCreated;
// True if the fragment was already added to a FragmentManager, but has since been removed
// again.
boolean mRemoved;
// Max Lifecycle state this Fragment can achieve.
Lifecycle.State mMaxState = Lifecycle.State.RESUMED;
LifecycleRegistry mLifecycleRegistry;
// This is initialized in performCreateView and unavailable outside of the
// onCreateView/onDestroyView lifecycle
@Nullable FragmentViewLifecycleOwner mViewLifecycleOwner;
MutableLiveData<LifecycleOwner> mViewLifecycleOwnerLiveData = new MutableLiveData<>();
private ViewModelProvider.Factory mDefaultFactory;
SavedStateRegistryController mSavedStateRegistryController;
@LayoutRes
private int mContentLayoutId;
private final AtomicInteger mNextLocalRequestCode = new AtomicInteger();
private final ArrayList<OnPreAttachedListener> mOnPreAttachedListeners = new ArrayList<>();
private abstract static class OnPreAttachedListener {
abstract void onPreAttached();
}
/**
* {@inheritDoc}
* <p>
* Overriding this method is no longer supported and this method will be made
* <code>final</code> in a future version of Fragment.
*/
@Override
@NonNull
public Lifecycle getLifecycle() {
return mLifecycleRegistry;
}
/**
* Get a {@link LifecycleOwner} that represents the {@link #getView() Fragment's View}
* lifecycle. In most cases, this mirrors the lifecycle of the Fragment itself, but in cases
* of {@link FragmentTransaction#detach(Fragment) detached} Fragments, the lifecycle of the
* Fragment can be considerably longer than the lifecycle of the View itself.
* <p>
* Namely, the lifecycle of the Fragment's View is:
* <ol>
* <li>{@link Lifecycle.Event#ON_CREATE created} after {@link #onViewStateRestored(Bundle)}</li>
* <li>{@link Lifecycle.Event#ON_START started} after {@link #onStart()}</li>
* <li>{@link Lifecycle.Event#ON_RESUME resumed} after {@link #onResume()}</li>
* <li>{@link Lifecycle.Event#ON_PAUSE paused} before {@link #onPause()}</li>
* <li>{@link Lifecycle.Event#ON_STOP stopped} before {@link #onStop()}</li>
* <li>{@link Lifecycle.Event#ON_DESTROY destroyed} before {@link #onDestroyView()}</li>
* </ol>
*
* The first method where it is safe to access the view lifecycle is
* {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)} under the condition that you must
* return a non-null view (an IllegalStateException will be thrown if you access the view
* lifecycle but don't return a non-null view).
* <p>The view lifecycle remains valid through the call to {@link #onDestroyView()}, after which
* {@link #getView()} will return null, the view lifecycle will be destroyed, and this method
* will throw an IllegalStateException. Consider using
* {@link #getViewLifecycleOwnerLiveData()} or {@link FragmentTransaction#runOnCommit(Runnable)}
* to receive a callback for when the Fragment's view lifecycle is available.
* <p>
* This should only be called on the main thread.
* <p>
* Overriding this method is no longer supported and this method will be made
* <code>final</code> in a future version of Fragment.
*
* @return A {@link LifecycleOwner} that represents the {@link #getView() Fragment's View}
* lifecycle.
* @throws IllegalStateException if the {@link #getView() Fragment's View is null}.
*/
@MainThread
@NonNull
public LifecycleOwner getViewLifecycleOwner() {
if (mViewLifecycleOwner == null) {
throw new IllegalStateException("Can't access the Fragment View's LifecycleOwner when "
+ "getView() is null i.e., before onCreateView() or after onDestroyView()");
}
return mViewLifecycleOwner;
}
/**
* Retrieve a {@link LiveData} which allows you to observe the
* {@link #getViewLifecycleOwner() lifecycle of the Fragment's View}.
* <p>
* This will be set to the new {@link LifecycleOwner} after {@link #onCreateView} returns a
* non-null View and will set to null after {@link #onDestroyView()}.
* <p>
* Overriding this method is no longer supported and this method will be made
* <code>final</code> in a future version of Fragment.
*
* @return A LiveData that changes in sync with {@link #getViewLifecycleOwner()}.
*/
@NonNull
public LiveData<LifecycleOwner> getViewLifecycleOwnerLiveData() {
return mViewLifecycleOwnerLiveData;
}
/**
* Returns the {@link ViewModelStore} associated with this Fragment
* <p>
* Overriding this method is no longer supported and this method will be made
* <code>final</code> in a future version of Fragment.
*
* @return a {@code ViewModelStore}
* @throws IllegalStateException if called before the Fragment is attached i.e., before
* onAttach().
*/
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
if (getMinimumMaxLifecycleState() == Lifecycle.State.INITIALIZED.ordinal()) {
throw new IllegalStateException("Calling getViewModelStore() before a Fragment "
+ "reaches onCreate() when using setMaxLifecycle(INITIALIZED) is not "
+ "supported");
}
return mFragmentManager.getViewModelStore(this);
}
private int getMinimumMaxLifecycleState() {
if (mMaxState == Lifecycle.State.INITIALIZED || mParentFragment == null) {
return mMaxState.ordinal();
}
return Math.min(mMaxState.ordinal(), mParentFragment.getMinimumMaxLifecycleState());
}
/**
* {@inheritDoc}
*
* <p>The {@link #getArguments() Fragment's arguments} when this is first called will be used
* as the defaults to any {@link androidx.lifecycle.SavedStateHandle} passed to a view model
* created using this factory.</p>
*/
@NonNull
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
if (mDefaultFactory == null) {
Application application = null;
Context appContext = requireContext().getApplicationContext();
while (appContext instanceof ContextWrapper) {
if (appContext instanceof Application) {
application = (Application) appContext;
break;
}
appContext = ((ContextWrapper) appContext).getBaseContext();
}
if (application == null && FragmentManager.isLoggingEnabled(Log.DEBUG)) {
Log.d(FragmentManager.TAG, "Could not find Application instance from "
+ "Context " + requireContext().getApplicationContext() + ", you will "
+ "not be able to use AndroidViewModel with the default "
+ "ViewModelProvider.Factory");
}
mDefaultFactory = new SavedStateViewModelFactory(
application,
this,
getArguments());
}
return mDefaultFactory;
}
@NonNull
@Override
public final SavedStateRegistry getSavedStateRegistry() {
return mSavedStateRegistryController.getSavedStateRegistry();
}
/**
* State information that has been retrieved from a fragment instance
* through {@link FragmentManager#saveFragmentInstanceState(Fragment)
* FragmentManager.saveFragmentInstanceState}.
*/
@SuppressLint("BanParcelableUsage, ParcelClassLoader")
public static class SavedState implements Parcelable {
final Bundle mState;
SavedState(Bundle state) {
mState = state;
}
SavedState(@NonNull Parcel in, @Nullable ClassLoader loader) {
mState = in.readBundle();
if (loader != null && mState != null) {
mState.setClassLoader(loader);
}
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeBundle(mState);
}
@NonNull
public static final Parcelable.Creator<SavedState> CREATOR =
new Parcelable.ClassLoaderCreator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in, null);
}
@Override
public SavedState createFromParcel(Parcel in, ClassLoader loader) {
return new SavedState(in, loader);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
/**
* Thrown by {@link FragmentFactory#instantiate(ClassLoader, String)} when
* there is an instantiation failure.
*/
@SuppressWarnings("JavaLangClash")
public static class InstantiationException extends RuntimeException {
public InstantiationException(@NonNull String msg, @Nullable Exception cause) {
super(msg, cause);
}
}
/**
* Constructor used by the default {@link FragmentFactory}. You must
* {@link FragmentManager#setFragmentFactory(FragmentFactory) set a custom FragmentFactory}
* if you want to use a non-default constructor to ensure that your constructor
* is called when the fragment is re-instantiated.
*
* <p>It is strongly recommended to supply arguments with {@link #setArguments}
* and later retrieved by the Fragment with {@link #getArguments}. These arguments
* are automatically saved and restored alongside the Fragment.
*
* <p>Applications should generally not implement a constructor. Prefer
* {@link #onAttach(Context)} instead. It is the first place application code can run where
* the fragment is ready to be used - the point where the fragment is actually associated with
* its context. Some applications may also want to implement {@link #onInflate} to retrieve
* attributes from a layout resource, although note this happens when the fragment is attached.
*/
public Fragment() {
initLifecycle();
}
/**
* Alternate constructor that can be called from your default, no argument constructor to
* provide a default layout that will be inflated by
* {@link #onCreateView(LayoutInflater, ViewGroup, Bundle)}.
*
* <pre class="prettyprint">
* class MyFragment extends Fragment {
* public MyFragment() {
* super(R.layout.fragment_main);
* }
* }
* </pre>
*
* You must
* {@link FragmentManager#setFragmentFactory(FragmentFactory) set a custom FragmentFactory}
* if you want to use a non-default constructor to ensure that your constructor is called
* when the fragment is re-instantiated.
*
* @see #Fragment()
* @see #onCreateView(LayoutInflater, ViewGroup, Bundle)
*/
@ContentView
public Fragment(@LayoutRes int contentLayoutId) {
this();
mContentLayoutId = contentLayoutId;
}
private void initLifecycle() {
mLifecycleRegistry = new LifecycleRegistry(this);
mSavedStateRegistryController = SavedStateRegistryController.create(this);
// The default factory depends on the SavedStateRegistry so it
// needs to be reset when the SavedStateRegistry is reset
mDefaultFactory = null;
}
/**
* Like {@link #instantiate(Context, String, Bundle)} but with a null
* argument Bundle.
* @deprecated Use {@link FragmentManager#getFragmentFactory()} and
* {@link FragmentFactory#instantiate(ClassLoader, String)}
*/
@SuppressWarnings("deprecation")
@Deprecated
@NonNull
public static Fragment instantiate(@NonNull Context context, @NonNull String fname) {
return instantiate(context, fname, null);
}
/**
* Create a new instance of a Fragment with the given class name. This is
* the same as calling its empty constructor, setting the {@link ClassLoader} on the
* supplied arguments, then calling {@link #setArguments(Bundle)}.
*
* @param context The calling context being used to instantiate the fragment.
* This is currently just used to get its ClassLoader.
* @param fname The class name of the fragment to instantiate.
* @param args Bundle of arguments to supply to the fragment, which it
* can retrieve with {@link #getArguments()}. May be null.
* @return Returns a new fragment instance.
* @throws InstantiationException If there is a failure in instantiating
* the given fragment class. This is a runtime exception; it is not
* normally expected to happen.
* @deprecated Use {@link FragmentManager#getFragmentFactory()} and
* {@link FragmentFactory#instantiate(ClassLoader, String)}, manually calling
* {@link #setArguments(Bundle)} on the returned Fragment.
*/
@Deprecated
@NonNull
public static Fragment instantiate(@NonNull Context context, @NonNull String fname,
@Nullable Bundle args) {
try {
Class<? extends Fragment> clazz = FragmentFactory.loadFragmentClass(
context.getClassLoader(), fname);
Fragment f = clazz.getConstructor().newInstance();
if (args != null) {
args.setClassLoader(f.getClass().getClassLoader());
f.setArguments(args);
}
return f;
} catch (java.lang.InstantiationException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (IllegalAccessException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": make sure class name exists, is public, and has an"
+ " empty constructor that is public", e);
} catch (NoSuchMethodException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": could not find Fragment constructor", e);
} catch (InvocationTargetException e) {
throw new InstantiationException("Unable to instantiate fragment " + fname
+ ": calling Fragment constructor caused an exception", e);
}
}
@SuppressWarnings("ConstantConditions")
final void restoreViewState(Bundle savedInstanceState) {
if (mSavedViewState != null) {
mView.restoreHierarchyState(mSavedViewState);
mSavedViewState = null;
}
if (mView != null) {
mViewLifecycleOwner.performRestore(mSavedViewRegistryState);
mSavedViewRegistryState = null;
}
mCalled = false;
onViewStateRestored(savedInstanceState);
if (!mCalled) {
throw new SuperNotCalledException("Fragment " + this
+ " did not call through to super.onViewStateRestored()");
}
if (mView != null) {
mViewLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
}
}
final boolean isInBackStack() {
return mBackStackNesting > 0;
}
/**
* Subclasses can not override equals().
*/
@Override public final boolean equals(@Nullable Object o) {
return super.equals(o);
}
/**
* Subclasses can not override hashCode().
*/
@Override public final int hashCode() {
return super.hashCode();
}
@NonNull
@Override
public String toString() {
StringBuilder sb = new StringBuilder(128);
Class<?> cls = getClass();
sb.append(cls.getSimpleName());
sb.append("{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append("}");
sb.append(" (");
sb.append(mWho);
if (mFragmentId != 0) {
sb.append(" id=0x");
sb.append(Integer.toHexString(mFragmentId));
}
if (mTag != null) {
sb.append(" tag=");
sb.append(mTag);
}
sb.append(")");
return sb.toString();
}
/**
* Return the identifier this fragment is known by. This is either
* the android:id value supplied in a layout or the container view ID
* supplied when adding the fragment.
*/
final public int getId() {
return mFragmentId;
}
/**
* Get the tag name of the fragment, if specified.
*/
@Nullable
final public String getTag() {
return mTag;
}
/**
* Supply the construction arguments for this fragment.
* The arguments supplied here will be retained across fragment destroy and
* creation.
* <p>This method cannot be called if the fragment is added to a FragmentManager and
* if {@link #isStateSaved()} would return true.</p>
*/
public void setArguments(@Nullable Bundle args) {
if (mFragmentManager != null && isStateSaved()) {
throw new IllegalStateException("Fragment already added and state has been saved");
}
mArguments = args;
}
/**
* Return the arguments supplied when the fragment was instantiated,
* if any.
*/
@Nullable
final public Bundle getArguments() {
return mArguments;
}
/**
* Return the arguments supplied when the fragment was instantiated.
*
* @throws IllegalStateException if no arguments were supplied to the Fragment.
* @see #getArguments()
*/
@NonNull
public final Bundle requireArguments() {
Bundle arguments = getArguments();
if (arguments == null) {
throw new IllegalStateException("Fragment " + this + " does not have any arguments.");
}
return arguments;
}
/**
* Returns true if this fragment is added and its state has already been saved
* by its host. Any operations that would change saved state should not be performed
* if this method returns true, and some operations such as {@link #setArguments(Bundle)}
* will fail.
*
* @return true if this fragment's state has already been saved by its host
*/
public final boolean isStateSaved() {
if (mFragmentManager == null) {
return false;
}
return mFragmentManager.isStateSaved();
}
/**
* Set the initial saved state that this Fragment should restore itself
* from when first being constructed, as returned by
* {@link FragmentManager#saveFragmentInstanceState(Fragment)
* FragmentManager.saveFragmentInstanceState}.
*
* @param state The state the fragment should be restored from.
*/
public void setInitialSavedState(@Nullable SavedState state) {
if (mFragmentManager != null) {
throw new IllegalStateException("Fragment already added");
}
mSavedFragmentState = state != null && state.mState != null
? state.mState : null;
}
/**
* Optional target for this fragment. This may be used, for example,
* if this fragment is being started by another, and when done wants to
* give a result back to the first. The target set here is retained
* across instances via {@link FragmentManager#putFragment
* FragmentManager.putFragment()}.
*
* @param fragment The fragment that is the target of this one.
* @param requestCode Optional request code, for convenience if you
* are going to call back with {@link #onActivityResult(int, int, Intent)}.
*
* @deprecated Instead of using a target fragment to pass results, the fragment requesting a
* result should use
* {@link FragmentManager#setFragmentResultListener(String, LifecycleOwner,
* FragmentResultListener)} to register a {@link FragmentResultListener} with a {@code
* requestKey} using its {@link #getParentFragmentManager() parent fragment manager}. The
* fragment delivering a result should then call
* {@link FragmentManager#setFragmentResult(String, Bundle)} using the same {@code requestKey}.
* Consider using {@link #setArguments} to pass the {@code requestKey} if you need to support
* dynamic request keys.
*/
@SuppressWarnings("ReferenceEquality, deprecation")
@Deprecated
public void setTargetFragment(@Nullable Fragment fragment, int requestCode) {
// Don't allow a caller to set a target fragment in another FragmentManager,
// but there's a snag: people do set target fragments before fragments get added.
// We'll have the FragmentManager check that for validity when we move
// the fragments to a valid state.
final FragmentManager mine = mFragmentManager;
final FragmentManager theirs = fragment != null ? fragment.mFragmentManager :
null;
if (mine != null && theirs != null && mine != theirs) {
throw new IllegalArgumentException("Fragment " + fragment
+ " must share the same FragmentManager to be set as a target fragment");
}
// Don't let someone create a cycle.
for (Fragment check = fragment; check != null; check = check.getTargetFragment()) {
if (check.equals(this)) {
throw new IllegalArgumentException("Setting " + fragment + " as the target of "
+ this + " would create a target cycle");
}
}
if (fragment == null) {
mTargetWho = null;
mTarget = null;
} else if (mFragmentManager != null && fragment.mFragmentManager != null) {
// Just save the reference to the Fragment
mTargetWho = fragment.mWho;
mTarget = null;
} else {
// Save the Fragment itself, waiting until we're attached
mTargetWho = null;
mTarget = fragment;
}
mTargetRequestCode = requestCode;
}
/**
* Return the target fragment set by {@link #setTargetFragment}.
*
* @deprecated Instead of using a target fragment to pass results, use
* {@link FragmentManager#setFragmentResult(String, Bundle)} to deliver results to
* {@link FragmentResultListener} instances registered by other fragments via
* {@link FragmentManager#setFragmentResultListener(String, LifecycleOwner,
* FragmentResultListener)}.
*/
@SuppressWarnings("DeprecatedIsStillUsed")
@Nullable
@Deprecated
final public Fragment getTargetFragment() {
if (mTarget != null) {
// Ensure that any Fragment set with setTargetFragment is immediately
// available here
return mTarget;
} else if (mFragmentManager != null && mTargetWho != null) {
// Look up the target Fragment from the FragmentManager
return mFragmentManager.findActiveFragment(mTargetWho);
}
return null;
}
/**
* Return the target request code set by {@link #setTargetFragment}.
*
* @deprecated When using the target fragment replacement of
* {@link FragmentManager#setFragmentResultListener(String, LifecycleOwner,
* FragmentResultListener)} and {@link FragmentManager#setFragmentResult(String, Bundle)},
* consider using {@link #setArguments} to pass a {@code requestKey} if you need to support
* dynamic request keys.
*/
@Deprecated
final public int getTargetRequestCode() {
return mTargetRequestCode;
}
/**
* Return the {@link Context} this fragment is currently associated with.
*
* @see #requireContext()
*/
@Nullable
public Context getContext() {
return mHost == null ? null : mHost.getContext();
}
/**
* Return the {@link Context} this fragment is currently associated with.
*
* @throws IllegalStateException if not currently associated with a context.
* @see #getContext()
*/
@NonNull
public final Context requireContext() {
Context context = getContext();
if (context == null) {
throw new IllegalStateException("Fragment " + this + " not attached to a context.");
}
return context;
}
/**
* Return the {@link FragmentActivity} this fragment is currently associated with.
* May return {@code null} if the fragment is associated with a {@link Context}
* instead.
*
* @see #requireActivity()
*/
@Nullable
final public FragmentActivity getActivity() {
return mHost == null ? null : (FragmentActivity) mHost.getActivity();
}
/**
* Return the {@link FragmentActivity} this fragment is currently associated with.
*
* @throws IllegalStateException if not currently associated with an activity or if associated
* only with a context.
* @see #getActivity()
*/
@NonNull
public final FragmentActivity requireActivity() {
FragmentActivity activity = getActivity();
if (activity == null) {
throw new IllegalStateException("Fragment " + this + " not attached to an activity.");
}
return activity;
}
/**
* Return the host object of this fragment. May return {@code null} if the fragment
* isn't currently being hosted.
*
* @see #requireHost()
*/
@Nullable
final public Object getHost() {
return mHost == null ? null : mHost.onGetHost();
}
/**
* Return the host object of this fragment.
*
* @throws IllegalStateException if not currently associated with a host.
* @see #getHost()
*/
@NonNull
public final Object requireHost() {
Object host = getHost();
if (host == null) {
throw new IllegalStateException("Fragment " + this + " not attached to a host.");
}
return host;
}
/**
* Return <code>requireActivity().getResources()</code>.
*/
@NonNull
final public Resources getResources() {
return requireContext().getResources();
}
/**
* Return a localized, styled CharSequence from the application's package's
* default string table.
*
* @param resId Resource id for the CharSequence text
*/
@NonNull
public final CharSequence getText(@StringRes int resId) {
return getResources().getText(resId);
}
/**
* Return a localized string from the application's package's
* default string table.
*
* @param resId Resource id for the string
*/
@NonNull
public final String getString(@StringRes int resId) {
return getResources().getString(resId);
}
/**
* Return a localized formatted string from the application's package's
* default string table, substituting the format arguments as defined in
* {@link java.util.Formatter} and {@link java.lang.String#format}.
*
* @param resId Resource id for the format string
* @param formatArgs The format arguments that will be used for substitution.