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.support.v4.app;
18
19import android.content.Context;
20import android.content.res.Configuration;
21import android.os.Parcelable;
22import android.support.annotation.Nullable;
23import android.support.v4.util.SimpleArrayMap;
24import android.util.AttributeSet;
25import android.view.Menu;
26import android.view.MenuInflater;
27import android.view.MenuItem;
28import android.view.View;
29
30import java.io.FileDescriptor;
31import java.io.PrintWriter;
32import java.util.ArrayList;
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 getSupportFragmentManager() {
59        return mHost.getFragmentManagerImpl();
60    }
61
62    /**
63     * Returns a {@link LoaderManager}.
64     */
65    public LoaderManager getSupportLoaderManager() {
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     * Returns the number of active fragments.
79     */
80    public int getActiveFragmentsCount() {
81        final List<Fragment> actives = mHost.mFragmentManager.mActive;
82        return actives == null ? 0 : actives.size();
83    }
84
85    /**
86     * Returns the list of active fragments.
87     */
88    public List<Fragment> getActiveFragments(List<Fragment> actives) {
89        if (mHost.mFragmentManager.mActive == null) {
90            return null;
91        }
92        if (actives == null) {
93            actives = new ArrayList<Fragment>(getActiveFragmentsCount());
94        }
95        actives.addAll(mHost.mFragmentManager.mActive);
96        return actives;
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));
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    public void dispatchReallyStop() {
249        mHost.mFragmentManager.dispatchReallyStop();
250    }
251
252    /**
253     * Moves all Fragments managed by the controller's FragmentManager
254     * into the destroy view state.
255     * <p>Call when the Fragment's views should be destroyed.
256     *
257     * @see Fragment#onDestroyView()
258     */
259    public void dispatchDestroyView() {
260        mHost.mFragmentManager.dispatchDestroyView();
261    }
262
263    /**
264     * Moves all Fragments managed by the controller's FragmentManager
265     * into the destroy state.
266     * <p>Call when Fragments should be destroyed.
267     *
268     * @see Fragment#onDestroy()
269     */
270    public void dispatchDestroy() {
271        mHost.mFragmentManager.dispatchDestroy();
272    }
273
274    /**
275     * Lets all Fragments managed by the controller's FragmentManager know the multi-window mode of
276     * the activity changed.
277     * <p>Call when the multi-window mode of the activity changed.
278     *
279     * @see Fragment#onMultiWindowModeChanged
280     */
281    public void dispatchMultiWindowModeChanged(boolean isInMultiWindowMode) {
282        mHost.mFragmentManager.dispatchMultiWindowModeChanged(isInMultiWindowMode);
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        mHost.mFragmentManager.dispatchPictureInPictureModeChanged(isInPictureInPictureMode);
294    }
295
296    /**
297     * Lets all Fragments managed by the controller's FragmentManager
298     * know a configuration change occurred.
299     * <p>Call when there is a configuration change.
300     *
301     * @see Fragment#onConfigurationChanged(Configuration)
302     */
303    public void dispatchConfigurationChanged(Configuration newConfig) {
304        mHost.mFragmentManager.dispatchConfigurationChanged(newConfig);
305    }
306
307    /**
308     * Lets all Fragments managed by the controller's FragmentManager
309     * know the device is in a low memory condition.
310     * <p>Call when the device is low on memory and Fragment's should trim
311     * their memory usage.
312     *
313     * @see Fragment#onLowMemory()
314     */
315    public void dispatchLowMemory() {
316        mHost.mFragmentManager.dispatchLowMemory();
317    }
318
319    /**
320     * Lets all Fragments managed by the controller's FragmentManager
321     * know they should create an options menu.
322     * <p>Call when the Fragment should create an options menu.
323     *
324     * @return {@code true} if the options menu contains items to display
325     * @see Fragment#onCreateOptionsMenu(Menu, MenuInflater)
326     */
327    public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
328        return mHost.mFragmentManager.dispatchCreateOptionsMenu(menu, inflater);
329    }
330
331    /**
332     * Lets all Fragments managed by the controller's FragmentManager
333     * know they should prepare their options menu for display.
334     * <p>Call immediately before displaying the Fragment's options menu.
335     *
336     * @return {@code true} if the options menu contains items to display
337     * @see Fragment#onPrepareOptionsMenu(Menu)
338     */
339    public boolean dispatchPrepareOptionsMenu(Menu menu) {
340        return mHost.mFragmentManager.dispatchPrepareOptionsMenu(menu);
341    }
342
343    /**
344     * Sends an option item selection event to the Fragments managed by the
345     * controller's FragmentManager. Once the event has been consumed,
346     * no additional handling will be performed.
347     * <p>Call immediately after an options menu item has been selected
348     *
349     * @return {@code true} if the options menu selection event was consumed
350     * @see Fragment#onOptionsItemSelected(MenuItem)
351     */
352    public boolean dispatchOptionsItemSelected(MenuItem item) {
353        return mHost.mFragmentManager.dispatchOptionsItemSelected(item);
354    }
355
356    /**
357     * Sends a context item selection event to the Fragments managed by the
358     * controller's FragmentManager. Once the event has been consumed,
359     * no additional handling will be performed.
360     * <p>Call immediately after an options menu item has been selected
361     *
362     * @return {@code true} if the context menu selection event was consumed
363     * @see Fragment#onContextItemSelected(MenuItem)
364     */
365    public boolean dispatchContextItemSelected(MenuItem item) {
366        return mHost.mFragmentManager.dispatchContextItemSelected(item);
367    }
368
369    /**
370     * Lets all Fragments managed by the controller's FragmentManager
371     * know their options menu has closed.
372     * <p>Call immediately after closing the Fragment's options menu.
373     *
374     * @see Fragment#onOptionsMenuClosed(Menu)
375     */
376    public void dispatchOptionsMenuClosed(Menu menu) {
377        mHost.mFragmentManager.dispatchOptionsMenuClosed(menu);
378    }
379
380    /**
381     * Execute any pending actions for the Fragments managed by the
382     * controller's FragmentManager.
383     * <p>Call when queued actions can be performed [eg when the
384     * Fragment moves into a start or resume state].
385     * @return {@code true} if queued actions were performed
386     */
387    public boolean execPendingActions() {
388        return mHost.mFragmentManager.execPendingActions();
389    }
390
391    /**
392     * Starts the loaders.
393     */
394    public void doLoaderStart() {
395        mHost.doLoaderStart();
396    }
397
398    /**
399     * Stops the loaders, optionally retaining their state. This is useful for keeping the
400     * loader state across configuration changes.
401     *
402     * @param retain When {@code true}, the loaders aren't stopped, but, their instances
403     * are retained in a started state
404     */
405    public void doLoaderStop(boolean retain) {
406        mHost.doLoaderStop(retain);
407    }
408
409    /**
410     * Retains the state of each of the loaders.
411     */
412    public void doLoaderRetain() {
413        mHost.doLoaderRetain();
414    }
415
416    /**
417     * Destroys the loaders and, if their state is not being retained, removes them.
418     */
419    public void doLoaderDestroy() {
420        mHost.doLoaderDestroy();
421    }
422
423    /**
424     * Lets the loaders know the host is ready to receive notifications.
425     */
426    public void reportLoaderStart() {
427        mHost.reportLoaderStart();
428    }
429
430    /**
431     * Returns a list of LoaderManagers that have opted to retain their instance across
432     * configuration changes.
433     */
434    public SimpleArrayMap<String, LoaderManager> retainLoaderNonConfig() {
435        return mHost.retainLoaderNonConfig();
436    }
437
438    /**
439     * Restores the saved state for all LoaderManagers. The given LoaderManager list are
440     * LoaderManager instances retained across configuration changes.
441     *
442     * @see #retainLoaderNonConfig()
443     */
444    public void restoreLoaderNonConfig(SimpleArrayMap<String, LoaderManager> loaderManagers) {
445        mHost.restoreLoaderNonConfig(loaderManagers);
446    }
447
448    /**
449     * Dumps the current state of the loaders.
450     */
451    public void dumpLoaders(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
452        mHost.dumpLoaders(prefix, fd, writer, args);
453    }
454}
455