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