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