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