Home | History | Annotate | Download | only in app
      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 
     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 
     62     FragmentHostCallback(Activity activity) {
     63         this(activity, activity /*context*/, activity.mHandler, 0 /*windowAnimations*/);
     64     }
     65 
     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      */
     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      */
     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      */
    102     public boolean onShouldSaveFragmentState(Fragment fragment) {
    103         return true;
    104     }
    105 
    106     /**
    107      * Return a {@link LayoutInflater}.
    108      * See {@link Activity#getLayoutInflater()}.
    109      */
    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      */
    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
    127     public abstract E onGetHost();
    128 
    129     /**
    130      * Invalidates the activity's options menu.
    131      * See {@link Activity#invalidateOptionsMenu()}
    132      */
    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      */
    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      */
    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      */
    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      */
    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      */
    189     public boolean onHasWindowAnimations() {
    190         return true;
    191     }
    192 
    193     /**
    194      * Return the window animations.
    195      */
    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      */
    205     public void onAttachFragment(Fragment fragment) {
    206     }
    207 
    208     @Nullable
    209     @Override
    210     public <T extends View> T onFindViewById(int id) {
    211         return null;
    212     }
    213 
    214     @Override
    215     public boolean onHasView() {
    216         return true;
    217     }
    218 
    219     boolean getRetainLoaders() {
    220         return mRetainLoaders;
    221     }
    222 
    223     Activity getActivity() {
    224         return mActivity;
    225     }
    226 
    227     Context getContext() {
    228         return mContext;
    229     }
    230 
    231     Handler getHandler() {
    232         return mHandler;
    233     }
    234 
    235     FragmentManagerImpl getFragmentManagerImpl() {
    236         return mFragmentManager;
    237     }
    238 
    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 
    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 
    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 
    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 
    292     void doLoaderRetain() {
    293         if (mLoaderManager == null) {
    294             return;
    295         }
    296         mLoaderManager.doRetain();
    297     }
    298 
    299     void doLoaderDestroy() {
    300         if (mLoaderManager == null) {
    301             return;
    302         }
    303         mLoaderManager.doDestroy();
    304     }
    305 
    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 
    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 
    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 
    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 
    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