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