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 FragmentHostCallback(Context context, Handler handler, int windowAnimations)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 FragmentHostCallback(Activity activity)62 FragmentHostCallback(Activity activity) { 63 this(activity, activity /*context*/, activity.mHandler, 0 /*windowAnimations*/); 64 } 65 FragmentHostCallback(Activity activity, Context context, Handler handler, int windowAnimations)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 */ chooseHandler(Context context, Handler handler)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 */ onDump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)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 */ onShouldSaveFragmentState(Fragment fragment)102 public boolean onShouldSaveFragmentState(Fragment fragment) { 103 return true; 104 } 105 106 /** 107 * Return a {@link LayoutInflater}. 108 * See {@link Activity#getLayoutInflater()}. 109 */ onGetLayoutInflater()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 */ onUseFragmentManagerInflaterFactory()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 onGetHost()127 public abstract E onGetHost(); 128 129 /** 130 * Invalidates the activity's options menu. 131 * See {@link Activity#invalidateOptionsMenu()} 132 */ onInvalidateOptionsMenu()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 */ onStartActivityFromFragment(Fragment fragment, Intent intent, int requestCode, Bundle options)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 */ onStartActivityAsUserFromFragment(Fragment fragment, Intent intent, int requestCode, Bundle options, UserHandle userHandle)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 */ onStartIntentSenderFromFragment(Fragment fragment, IntentSender intent, int requestCode, @Nullable Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags, Bundle options)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 */ onRequestPermissionsFromFragment(@onNull Fragment fragment, @NonNull String[] permissions, int requestCode)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 */ onHasWindowAnimations()189 public boolean onHasWindowAnimations() { 190 return true; 191 } 192 193 /** 194 * Return the window animations. 195 */ onGetWindowAnimations()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 */ onAttachFragment(Fragment fragment)205 public void onAttachFragment(Fragment fragment) { 206 } 207 208 @Nullable 209 @Override onFindViewById(int id)210 public <T extends View> T onFindViewById(int id) { 211 return null; 212 } 213 214 @Override onHasView()215 public boolean onHasView() { 216 return true; 217 } 218 getRetainLoaders()219 boolean getRetainLoaders() { 220 return mRetainLoaders; 221 } 222 getActivity()223 Activity getActivity() { 224 return mActivity; 225 } 226 getContext()227 Context getContext() { 228 return mContext; 229 } 230 getHandler()231 Handler getHandler() { 232 return mHandler; 233 } 234 getFragmentManagerImpl()235 FragmentManagerImpl getFragmentManagerImpl() { 236 return mFragmentManager; 237 } 238 getLoaderManagerImpl()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 inactivateFragment(String who)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 doLoaderStart()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 doLoaderStop(boolean retain)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 doLoaderRetain()292 void doLoaderRetain() { 293 if (mLoaderManager == null) { 294 return; 295 } 296 mLoaderManager.doRetain(); 297 } 298 doLoaderDestroy()299 void doLoaderDestroy() { 300 if (mLoaderManager == null) { 301 return; 302 } 303 mLoaderManager.doDestroy(); 304 } 305 reportLoaderStart()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 getLoaderManager(String who, boolean started, boolean create)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 retainLoaderNonConfig()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 restoreLoaderNonConfig(ArrayMap<String, LoaderManager> loaderManagers)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 dumpLoaders(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)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