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