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