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