• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.app;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentSender;
24 import android.os.Bundle;
25 import android.os.Handler;
26 import android.os.UserHandle;
27 import android.util.ArrayMap;
28 import android.view.LayoutInflater;
29 import android.view.View;
30 
31 import java.io.FileDescriptor;
32 import java.io.PrintWriter;
33 
34 /**
35  * Integration points with the Fragment host.
36  * <p>
37  * Fragments may be hosted by any object; such as an {@link Activity}. In order to
38  * host fragments, implement {@link FragmentHostCallback}, overriding the methods
39  * applicable to the host.
40  */
41 public abstract class FragmentHostCallback<E> extends FragmentContainer {
42     private final Activity mActivity;
43     final Context mContext;
44     private final Handler mHandler;
45     final int mWindowAnimations;
46     final FragmentManagerImpl mFragmentManager = new FragmentManagerImpl();
47     /** The loader managers for individual fragments [i.e. Fragment#getLoaderManager()] */
48     private ArrayMap<String, LoaderManager> mAllLoaderManagers;
49     /** Whether or not fragment loaders should retain their state */
50     private boolean mRetainLoaders;
51     /** The loader manger for the fragment host [i.e. Activity#getLoaderManager()] */
52     private LoaderManagerImpl mLoaderManager;
53     private boolean mCheckedForLoaderManager;
54     /** Whether or not the fragment host loader manager was started */
55     private boolean mLoadersStarted;
56 
FragmentHostCallback(Context context, Handler handler, int windowAnimations)57     public FragmentHostCallback(Context context, Handler handler, int windowAnimations) {
58         this((context instanceof Activity) ? (Activity)context : null, context,
59                 chooseHandler(context, handler), windowAnimations);
60     }
61 
FragmentHostCallback(Activity activity)62     FragmentHostCallback(Activity activity) {
63         this(activity, activity /*context*/, activity.mHandler, 0 /*windowAnimations*/);
64     }
65 
FragmentHostCallback(Activity activity, Context context, Handler handler, int windowAnimations)66     FragmentHostCallback(Activity activity, Context context, Handler handler,
67             int windowAnimations) {
68         mActivity = activity;
69         mContext = context;
70         mHandler = handler;
71         mWindowAnimations = windowAnimations;
72     }
73 
74     /**
75      * Used internally in {@link #FragmentHostCallback(Context, Handler, int)} to choose
76      * the Activity's handler or the provided handler.
77      */
chooseHandler(Context context, Handler handler)78     private static Handler chooseHandler(Context context, Handler handler) {
79         if (handler == null && context instanceof Activity) {
80             Activity activity = (Activity) context;
81             return activity.mHandler;
82         } else {
83             return handler;
84         }
85     }
86 
87     /**
88      * Print internal state into the given stream.
89      *
90      * @param prefix Desired prefix to prepend at each line of output.
91      * @param fd The raw file descriptor that the dump is being sent to.
92      * @param writer The PrintWriter to which you should dump your state. This will be closed
93      *                  for you after you return.
94      * @param args additional arguments to the dump request.
95      */
onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)96     public void onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
97     }
98 
99     /**
100      * Return {@code true} if the fragment's state needs to be saved.
101      */
onShouldSaveFragmentState(Fragment fragment)102     public boolean onShouldSaveFragmentState(Fragment fragment) {
103         return true;
104     }
105 
106     /**
107      * Return a {@link LayoutInflater}.
108      * See {@link Activity#getLayoutInflater()}.
109      */
onGetLayoutInflater()110     public LayoutInflater onGetLayoutInflater() {
111         return (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
112     }
113 
114     /**
115      * Return {@code true} if the FragmentManager's LayoutInflaterFactory should be used.
116      */
onUseFragmentManagerInflaterFactory()117     public boolean onUseFragmentManagerInflaterFactory() {
118         return false;
119     }
120 
121     /**
122      * Return the object that's currently hosting the fragment. If a {@link Fragment}
123      * is hosted by a {@link Activity}, the object returned here should be the same
124      * object returned from {@link Fragment#getActivity()}.
125      */
126     @Nullable
onGetHost()127     public abstract E onGetHost();
128 
129     /**
130      * Invalidates the activity's options menu.
131      * See {@link Activity#invalidateOptionsMenu()}
132      */
onInvalidateOptionsMenu()133     public void onInvalidateOptionsMenu() {
134     }
135 
136     /**
137      * Starts a new {@link Activity} from the given fragment.
138      * See {@link Activity#startActivityForResult(Intent, int)}.
139      */
onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode, Bundle options)140     public void onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode,
141             Bundle options) {
142         if (requestCode != -1) {
143             throw new IllegalStateException(
144                     "Starting activity with a requestCode requires a FragmentActivity host");
145         }
146         mContext.startActivity(intent);
147     }
148 
149     /**
150      * @hide
151      * Starts a new {@link Activity} from the given fragment.
152      * See {@link Activity#startActivityForResult(Intent, int)}.
153      */
onStartActivityAsUserFromFragment(Fragment fragment, Intent intent, int requestCode, Bundle options, UserHandle userHandle)154     public void onStartActivityAsUserFromFragment(Fragment fragment, Intent intent, int requestCode,
155             Bundle options, UserHandle userHandle) {
156         if (requestCode != -1) {
157             throw new IllegalStateException(
158                     "Starting activity with a requestCode requires a FragmentActivity host");
159         }
160         mContext.startActivityAsUser(intent, userHandle);
161     }
162 
163     /**
164      * Starts a new {@link IntentSender} from the given fragment.
165      * See {@link Activity#startIntentSender(IntentSender, Intent, int, int, int, Bundle)}.
166      */
onStartIntentSenderFromFragment(Fragment fragment, IntentSender intent, int requestCode, @Nullable Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options)167     public void onStartIntentSenderFromFragment(Fragment fragment, IntentSender intent,
168             int requestCode, @Nullable Intent fillInIntent, int flagsMask, int flagsValues,
169             int extraFlags, Bundle options) throws IntentSender.SendIntentException {
170         if (requestCode != -1) {
171             throw new IllegalStateException(
172                     "Starting intent sender with a requestCode requires a FragmentActivity host");
173         }
174         mContext.startIntentSender(intent, fillInIntent, flagsMask, flagsValues, extraFlags,
175                 options);
176     }
177 
178     /**
179      * Requests permissions from the given fragment.
180      * See {@link Activity#requestPermissions(String[], int)}
181      */
onRequestPermissionsFromFragment(@onNull Fragment fragment, @NonNull String[] permissions, int requestCode)182     public void onRequestPermissionsFromFragment(@NonNull Fragment fragment,
183             @NonNull String[] permissions, int requestCode) {
184     }
185 
186     /**
187      * Return {@code true} if there are window animations.
188      */
onHasWindowAnimations()189     public boolean onHasWindowAnimations() {
190         return true;
191     }
192 
193     /**
194      * Return the window animations.
195      */
onGetWindowAnimations()196     public int onGetWindowAnimations() {
197         return mWindowAnimations;
198     }
199 
200     /**
201      * Called when a {@link Fragment} is being attached to this host, immediately
202      * after the call to its {@link Fragment#onAttach(Context)} method and before
203      * {@link Fragment#onCreate(Bundle)}.
204      */
onAttachFragment(Fragment fragment)205     public void onAttachFragment(Fragment fragment) {
206     }
207 
208     @Nullable
209     @Override
onFindViewById(int id)210     public <T extends View> T onFindViewById(int id) {
211         return null;
212     }
213 
214     @Override
onHasView()215     public boolean onHasView() {
216         return true;
217     }
218 
getRetainLoaders()219     boolean getRetainLoaders() {
220         return mRetainLoaders;
221     }
222 
getActivity()223     Activity getActivity() {
224         return mActivity;
225     }
226 
getContext()227     Context getContext() {
228         return mContext;
229     }
230 
getHandler()231     Handler getHandler() {
232         return mHandler;
233     }
234 
getFragmentManagerImpl()235     FragmentManagerImpl getFragmentManagerImpl() {
236         return mFragmentManager;
237     }
238 
getLoaderManagerImpl()239     LoaderManagerImpl getLoaderManagerImpl() {
240         if (mLoaderManager != null) {
241             return mLoaderManager;
242         }
243         mCheckedForLoaderManager = true;
244         mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true /*create*/);
245         return mLoaderManager;
246     }
247 
inactivateFragment(String who)248     void inactivateFragment(String who) {
249         //Log.v(TAG, "invalidateSupportFragment: who=" + who);
250         if (mAllLoaderManagers != null) {
251             LoaderManagerImpl lm = (LoaderManagerImpl) mAllLoaderManagers.get(who);
252             if (lm != null && !lm.mRetaining) {
253                 lm.doDestroy();
254                 mAllLoaderManagers.remove(who);
255             }
256         }
257     }
258 
doLoaderStart()259     void doLoaderStart() {
260         if (mLoadersStarted) {
261             return;
262         }
263         mLoadersStarted = true;
264 
265         if (mLoaderManager != null) {
266             mLoaderManager.doStart();
267         } else if (!mCheckedForLoaderManager) {
268             mLoaderManager = getLoaderManager("(root)", mLoadersStarted, false);
269         }
270         mCheckedForLoaderManager = true;
271     }
272 
doLoaderStop(boolean retain)273     void doLoaderStop(boolean retain) {
274         mRetainLoaders = retain;
275 
276         if (mLoaderManager == null) {
277             return;
278         }
279 
280         if (!mLoadersStarted) {
281             return;
282         }
283         mLoadersStarted = false;
284 
285         if (retain) {
286             mLoaderManager.doRetain();
287         } else {
288             mLoaderManager.doStop();
289         }
290     }
291 
doLoaderRetain()292     void doLoaderRetain() {
293         if (mLoaderManager == null) {
294             return;
295         }
296         mLoaderManager.doRetain();
297     }
298 
doLoaderDestroy()299     void doLoaderDestroy() {
300         if (mLoaderManager == null) {
301             return;
302         }
303         mLoaderManager.doDestroy();
304     }
305 
reportLoaderStart()306     void reportLoaderStart() {
307         if (mAllLoaderManagers != null) {
308             final int N = mAllLoaderManagers.size();
309             LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
310             for (int i=N-1; i>=0; i--) {
311                 loaders[i] = (LoaderManagerImpl) mAllLoaderManagers.valueAt(i);
312             }
313             for (int i=0; i<N; i++) {
314                 LoaderManagerImpl lm = loaders[i];
315                 lm.finishRetain();
316                 lm.doReportStart();
317             }
318         }
319     }
320 
getLoaderManager(String who, boolean started, boolean create)321     LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
322         if (mAllLoaderManagers == null) {
323             mAllLoaderManagers = new ArrayMap<String, LoaderManager>();
324         }
325         LoaderManagerImpl lm = (LoaderManagerImpl) mAllLoaderManagers.get(who);
326         if (lm == null && create) {
327             lm = new LoaderManagerImpl(who, this, started);
328             mAllLoaderManagers.put(who, lm);
329         } else if (started && lm != null && !lm.mStarted){
330             lm.doStart();
331         }
332         return lm;
333     }
334 
retainLoaderNonConfig()335     ArrayMap<String, LoaderManager> retainLoaderNonConfig() {
336         boolean retainLoaders = false;
337         if (mAllLoaderManagers != null) {
338             // Restart any loader managers that were already stopped so that they
339             // will be ready to retain
340             final int N = mAllLoaderManagers.size();
341             LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
342             for (int i=N-1; i>=0; i--) {
343                 loaders[i] = (LoaderManagerImpl) mAllLoaderManagers.valueAt(i);
344             }
345             final boolean doRetainLoaders = getRetainLoaders();
346             for (int i=0; i<N; i++) {
347                 LoaderManagerImpl lm = loaders[i];
348                 if (!lm.mRetaining && doRetainLoaders) {
349                     if (!lm.mStarted) {
350                         lm.doStart();
351                     }
352                     lm.doRetain();
353                 }
354                 if (lm.mRetaining) {
355                     retainLoaders = true;
356                 } else {
357                     lm.doDestroy();
358                     mAllLoaderManagers.remove(lm.mWho);
359                 }
360             }
361         }
362 
363         if (retainLoaders) {
364             return mAllLoaderManagers;
365         }
366         return null;
367     }
368 
restoreLoaderNonConfig(ArrayMap<String, LoaderManager> loaderManagers)369     void restoreLoaderNonConfig(ArrayMap<String, LoaderManager> loaderManagers) {
370         if (loaderManagers != null) {
371             for (int i = 0, N = loaderManagers.size(); i < N; i++) {
372                 ((LoaderManagerImpl) loaderManagers.valueAt(i)).updateHostController(this);
373             }
374         }
375         mAllLoaderManagers = loaderManagers;
376     }
377 
dumpLoaders(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)378     void dumpLoaders(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
379         writer.print(prefix); writer.print("mLoadersStarted=");
380         writer.println(mLoadersStarted);
381         if (mLoaderManager != null) {
382             writer.print(prefix); writer.print("Loader Manager ");
383             writer.print(Integer.toHexString(System.identityHashCode(mLoaderManager)));
384             writer.println(":");
385             mLoaderManager.dump(prefix + "  ", fd, writer, args);
386         }
387     }
388 }
389