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