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