1/*
2 * Copyright 2018 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
17package androidx.fragment.app;
18
19import android.content.Context;
20import android.content.res.Configuration;
21import android.os.Parcelable;
22import android.util.AttributeSet;
23import android.view.Menu;
24import android.view.MenuInflater;
25import android.view.MenuItem;
26import android.view.View;
27
28import androidx.annotation.Nullable;
29import androidx.collection.SimpleArrayMap;
30import androidx.loader.app.LoaderManager;
31
32import java.io.FileDescriptor;
33import java.io.PrintWriter;
34import java.util.List;
35
36/**
37 * Provides integration points with a {@link FragmentManager} for a fragment host.
38 * <p>
39 * It is the responsibility of the host to take care of the Fragment's lifecycle.
40 * The methods provided by {@link FragmentController} are for that purpose.
41 */
42public class FragmentController {
43    private final FragmentHostCallback<?> mHost;
44
45    /**
46     * Returns a {@link FragmentController}.
47     */
48    public static FragmentController createController(FragmentHostCallback<?> callbacks) {
49        return new FragmentController(callbacks);
50    }
51
52    private FragmentController(FragmentHostCallback<?> callbacks) {
53        mHost = callbacks;
54    }
55
56    /**
57     * Returns a {@link FragmentManager} for this controller.
58     */
59    public FragmentManager getSupportFragmentManager() {
60        return mHost.getFragmentManagerImpl();
61    }
62
63    /**
64     * Returns a {@link LoaderManager}.
65     *
66     * @deprecated Loaders are managed separately from FragmentController and this now throws an
67     * {@link UnsupportedOperationException}. Use {@link LoaderManager#getInstance} to obtain a
68     * LoaderManager.
69     * @see LoaderManager#getInstance
70     */
71    @Deprecated
72    public LoaderManager getSupportLoaderManager() {
73        throw new UnsupportedOperationException("Loaders are managed separately from "
74                + "FragmentController, use LoaderManager.getInstance() to obtain a LoaderManager.");
75    }
76
77    /**
78     * Returns a fragment with the given identifier.
79     */
80    @Nullable
81    public Fragment findFragmentByWho(String who) {
82        return mHost.mFragmentManager.findFragmentByWho(who);
83    }
84
85    /**
86     * Returns the number of active fragments.
87     */
88    public int getActiveFragmentsCount() {
89        return mHost.mFragmentManager.getActiveFragmentCount();
90    }
91
92    /**
93     * Returns the list of active fragments.
94     */
95    public List<Fragment> getActiveFragments(List<Fragment> actives) {
96        return mHost.mFragmentManager.getActiveFragments();
97    }
98
99    /**
100     * Attaches the host to the FragmentManager for this controller. The host must be
101     * attached before the FragmentManager can be used to manage Fragments.
102     */
103    public void attachHost(Fragment parent) {
104        mHost.mFragmentManager.attachController(
105                mHost, mHost /*container*/, parent);
106    }
107
108    /**
109     * Instantiates a Fragment's view.
110     *
111     * @param parent The parent that the created view will be placed
112     * in; <em>note that this may be null</em>.
113     * @param name Tag name to be inflated.
114     * @param context The context the view is being created in.
115     * @param attrs Inflation attributes as specified in XML file.
116     *
117     * @return view the newly created view
118     */
119    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
120        return mHost.mFragmentManager.onCreateView(parent, name, context, attrs);
121    }
122
123    /**
124     * Marks the fragment state as unsaved. This allows for "state loss" detection.
125     */
126    public void noteStateNotSaved() {
127        mHost.mFragmentManager.noteStateNotSaved();
128    }
129
130    /**
131     * Saves the state for all Fragments.
132     */
133    public Parcelable saveAllState() {
134        return mHost.mFragmentManager.saveAllState();
135    }
136
137    /**
138     * Restores the saved state for all Fragments. The given Fragment list are Fragment
139     * instances retained across configuration changes.
140     *
141     * @see #retainNonConfig()
142     *
143     * @deprecated use {@link #restoreAllState(Parcelable, FragmentManagerNonConfig)}
144     */
145    @Deprecated
146    public void restoreAllState(Parcelable state, List<Fragment> nonConfigList) {
147        mHost.mFragmentManager.restoreAllState(state,
148                new FragmentManagerNonConfig(nonConfigList, null, null));
149    }
150
151    /**
152     * Restores the saved state for all Fragments. The given FragmentManagerNonConfig are Fragment
153     * instances retained across configuration changes, including nested fragments
154     *
155     * @see #retainNestedNonConfig()
156     */
157    public void restoreAllState(Parcelable state, FragmentManagerNonConfig nonConfig) {
158        mHost.mFragmentManager.restoreAllState(state, nonConfig);
159    }
160
161    /**
162     * Returns a list of Fragments that have opted to retain their instance across
163     * configuration changes.
164     *
165     * @deprecated use {@link #retainNestedNonConfig()} to also track retained
166     *             nested child fragments
167     */
168    @Deprecated
169    public List<Fragment> retainNonConfig() {
170        FragmentManagerNonConfig nonconf = mHost.mFragmentManager.retainNonConfig();
171        return nonconf != null ? nonconf.getFragments() : null;
172    }
173
174    /**
175     * Returns a nested tree of Fragments that have opted to retain their instance across
176     * configuration changes.
177     */
178    public FragmentManagerNonConfig retainNestedNonConfig() {
179        return mHost.mFragmentManager.retainNonConfig();
180    }
181
182    /**
183     * Moves all Fragments managed by the controller's FragmentManager
184     * into the create state.
185     * <p>Call when Fragments should be created.
186     *
187     * @see Fragment#onCreate(Bundle)
188     */
189    public void dispatchCreate() {
190        mHost.mFragmentManager.dispatchCreate();
191    }
192
193    /**
194     * Moves all Fragments managed by the controller's FragmentManager
195     * into the activity created state.
196     * <p>Call when Fragments should be informed their host has been created.
197     *
198     * @see Fragment#onActivityCreated(Bundle)
199     */
200    public void dispatchActivityCreated() {
201        mHost.mFragmentManager.dispatchActivityCreated();
202    }
203
204    /**
205     * Moves all Fragments managed by the controller's FragmentManager
206     * into the start state.
207     * <p>Call when Fragments should be started.
208     *
209     * @see Fragment#onStart()
210     */
211    public void dispatchStart() {
212        mHost.mFragmentManager.dispatchStart();
213    }
214
215    /**
216     * Moves all Fragments managed by the controller's FragmentManager
217     * into the resume state.
218     * <p>Call when Fragments should be resumed.
219     *
220     * @see Fragment#onResume()
221     */
222    public void dispatchResume() {
223        mHost.mFragmentManager.dispatchResume();
224    }
225
226    /**
227     * Moves all Fragments managed by the controller's FragmentManager
228     * into the pause state.
229     * <p>Call when Fragments should be paused.
230     *
231     * @see Fragment#onPause()
232     */
233    public void dispatchPause() {
234        mHost.mFragmentManager.dispatchPause();
235    }
236
237    /**
238     * Moves all Fragments managed by the controller's FragmentManager
239     * into the stop state.
240     * <p>Call when Fragments should be stopped.
241     *
242     * @see Fragment#onStop()
243     */
244    public void dispatchStop() {
245        mHost.mFragmentManager.dispatchStop();
246    }
247
248    /**
249     * @deprecated This functionality has been rolled into {@link #dispatchStop()}.
250     */
251    @Deprecated
252    public void dispatchReallyStop() {
253    }
254
255    /**
256     * Moves all Fragments managed by the controller's FragmentManager
257     * into the destroy view state.
258     * <p>Call when the Fragment's views should be destroyed.
259     *
260     * @see Fragment#onDestroyView()
261     */
262    public void dispatchDestroyView() {
263        mHost.mFragmentManager.dispatchDestroyView();
264    }
265
266    /**
267     * Moves all Fragments managed by the controller's FragmentManager
268     * into the destroy state.
269     * <p>Call when Fragments should be destroyed.
270     *
271     * @see Fragment#onDestroy()
272     */
273    public void dispatchDestroy() {
274        mHost.mFragmentManager.dispatchDestroy();
275    }
276
277    /**
278     * Lets all Fragments managed by the controller's FragmentManager know the multi-window mode of
279     * the activity changed.
280     * <p>Call when the multi-window mode of the activity changed.
281     *
282     * @see Fragment#onMultiWindowModeChanged
283     */
284    public void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode) {
285        mHost.mFragmentManager.dispatchMultiWindowModeChanged(isInMultiWindowMode);
286    }
287
288    /**
289     * Lets all Fragments managed by the controller's FragmentManager know the picture-in-picture
290     * mode of the activity changed.
291     * <p>Call when the picture-in-picture mode of the activity changed.
292     *
293     * @see Fragment#onPictureInPictureModeChanged
294     */
295    public void dispatchPictureInPictureModeChanged(boolean isInPictureInPictureMode) {
296        mHost.mFragmentManager.dispatchPictureInPictureModeChanged(isInPictureInPictureMode);
297    }
298
299    /**
300     * Lets all Fragments managed by the controller's FragmentManager
301     * know a configuration change occurred.
302     * <p>Call when there is a configuration change.
303     *
304     * @see Fragment#onConfigurationChanged(Configuration)
305     */
306    public void dispatchConfigurationChanged(Configuration newConfig) {
307        mHost.mFragmentManager.dispatchConfigurationChanged(newConfig);
308    }
309
310    /**
311     * Lets all Fragments managed by the controller's FragmentManager
312     * know the device is in a low memory condition.
313     * <p>Call when the device is low on memory and Fragment's should trim
314     * their memory usage.
315     *
316     * @see Fragment#onLowMemory()
317     */
318    public void dispatchLowMemory() {
319        mHost.mFragmentManager.dispatchLowMemory();
320    }
321
322    /**
323     * Lets all Fragments managed by the controller's FragmentManager
324     * know they should create an options menu.
325     * <p>Call when the Fragment should create an options menu.
326     *
327     * @return {@code true} if the options menu contains items to display
328     * @see Fragment#onCreateOptionsMenu(Menu, MenuInflater)
329     */
330    public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
331        return mHost.mFragmentManager.dispatchCreateOptionsMenu(menu, inflater);
332    }
333
334    /**
335     * Lets all Fragments managed by the controller's FragmentManager
336     * know they should prepare their options menu for display.
337     * <p>Call immediately before displaying the Fragment's options menu.
338     *
339     * @return {@code true} if the options menu contains items to display
340     * @see Fragment#onPrepareOptionsMenu(Menu)
341     */
342    public boolean dispatchPrepareOptionsMenu(Menu menu) {
343        return mHost.mFragmentManager.dispatchPrepareOptionsMenu(menu);
344    }
345
346    /**
347     * Sends an option item selection event to the Fragments managed by the
348     * controller's FragmentManager. Once the event has been consumed,
349     * no additional handling will be performed.
350     * <p>Call immediately after an options menu item has been selected
351     *
352     * @return {@code true} if the options menu selection event was consumed
353     * @see Fragment#onOptionsItemSelected(MenuItem)
354     */
355    public boolean dispatchOptionsItemSelected(MenuItem item) {
356        return mHost.mFragmentManager.dispatchOptionsItemSelected(item);
357    }
358
359    /**
360     * Sends a context item selection event to the Fragments managed by the
361     * controller's FragmentManager. Once the event has been consumed,
362     * no additional handling will be performed.
363     * <p>Call immediately after an options menu item has been selected
364     *
365     * @return {@code true} if the context menu selection event was consumed
366     * @see Fragment#onContextItemSelected(MenuItem)
367     */
368    public boolean dispatchContextItemSelected(MenuItem item) {
369        return mHost.mFragmentManager.dispatchContextItemSelected(item);
370    }
371
372    /**
373     * Lets all Fragments managed by the controller's FragmentManager
374     * know their options menu has closed.
375     * <p>Call immediately after closing the Fragment's options menu.
376     *
377     * @see Fragment#onOptionsMenuClosed(Menu)
378     */
379    public void dispatchOptionsMenuClosed(Menu menu) {
380        mHost.mFragmentManager.dispatchOptionsMenuClosed(menu);
381    }
382
383    /**
384     * Execute any pending actions for the Fragments managed by the
385     * controller's FragmentManager.
386     * <p>Call when queued actions can be performed [eg when the
387     * Fragment moves into a start or resume state].
388     * @return {@code true} if queued actions were performed
389     */
390    public boolean execPendingActions() {
391        return mHost.mFragmentManager.execPendingActions();
392    }
393
394    /**
395     * Starts the loaders.
396     *
397     * @deprecated Loaders are managed separately from FragmentController
398     */
399    @Deprecated
400    public void doLoaderStart() {
401    }
402
403    /**
404     * Stops the loaders, optionally retaining their state. This is useful for keeping the
405     * loader state across configuration changes.
406     *
407     * @param retain When {@code true}, the loaders aren't stopped, but, their instances
408     * are retained in a started state
409     *
410     * @deprecated Loaders are managed separately from FragmentController
411     */
412    @Deprecated
413    public void doLoaderStop(boolean retain) {
414    }
415
416    /**
417     * Retains the state of each of the loaders.
418     *
419     * @deprecated Loaders are managed separately from FragmentController
420     */
421    @Deprecated
422    public void doLoaderRetain() {
423    }
424
425    /**
426     * Destroys the loaders and, if their state is not being retained, removes them.
427     *
428     * @deprecated Loaders are managed separately from FragmentController
429     */
430    @Deprecated
431    public void doLoaderDestroy() {
432    }
433
434    /**
435     * Lets the loaders know the host is ready to receive notifications.
436     *
437     * @deprecated Loaders are managed separately from FragmentController
438     */
439    @Deprecated
440    public void reportLoaderStart() {
441    }
442
443    /**
444     * Returns a list of LoaderManagers that have opted to retain their instance across
445     * configuration changes.
446     *
447     * @deprecated Loaders are managed separately from FragmentController
448     */
449    @Deprecated
450    public SimpleArrayMap<String, LoaderManager> retainLoaderNonConfig() {
451        return null;
452    }
453
454    /**
455     * Restores the saved state for all LoaderManagers. The given LoaderManager list are
456     * LoaderManager instances retained across configuration changes.
457     *
458     * @see #retainLoaderNonConfig()
459     *
460     * @deprecated Loaders are managed separately from FragmentController
461     */
462    @Deprecated
463    public void restoreLoaderNonConfig(SimpleArrayMap<String, LoaderManager> loaderManagers) {
464    }
465
466    /**
467     * Dumps the current state of the loaders.
468     *
469     * @deprecated Loaders are managed separately from FragmentController
470     */
471    @Deprecated
472    public void dumpLoaders(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
473    }
474}
475