811a5a4a |
1 | /* |
2 | * Copyright (C) 2011 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.support.v2.app; |
18 | |
19 | import android.app.Activity; |
20 | import android.content.Context; |
21 | import android.content.Intent; |
22 | import android.content.res.Configuration; |
23 | import android.content.res.TypedArray; |
24 | import android.os.Bundle; |
25 | import android.os.Handler; |
26 | import android.os.Message; |
27 | import android.os.Parcelable; |
28 | import android.util.AttributeSet; |
29 | import android.util.Log; |
30 | import android.view.KeyEvent; |
31 | import android.view.Menu; |
32 | import android.view.MenuItem; |
33 | import android.view.View; |
34 | import android.view.Window; |
35 | |
36 | import java.io.FileDescriptor; |
37 | import java.io.PrintWriter; |
38 | import java.util.ArrayList; |
39 | import java.util.HashMap; |
40 | |
41 | /** |
42 | * Base class for activities that want to use the support-based Fragment and |
43 | * Loader APIs. |
44 | * |
45 | * <p>Known limitations:</p> |
46 | * <ul> |
47 | * <li> <p>When using the <fragment> tag, this implementation can not |
48 | * use the parent view's ID as the new fragment's ID. You must explicitly |
49 | * specify an ID (or tag) in the <fragment>.</p> |
50 | * <li> <p>Prior to Honeycomb (3.0), an activity's state was saved before pausing. |
51 | * Fragments are a significant amount of new state, and dynamic enough that one |
52 | * often wants them to change between pausing and stopping. These classes |
53 | * throw an exception if you try to change the fragment state after it has been |
54 | * saved, to avoid accidental loss of UI state. However this is too restrictive |
55 | * prior to Honeycomb, where the state is saved before pausing. To address this, |
56 | * when running on platforms prior to Honeycomb an exception will not be thrown |
57 | * if you change fragments between the state save and the activity being stopped. |
58 | * This means that is some cases if the activity is restored from its last saved |
59 | * state, this may be a snapshot slightly before what the user last saw.</p> |
60 | * </ul> |
61 | */ |
62 | public class FragmentActivity extends Activity { |
63 | private static final String TAG = "FragmentActivity"; |
64 | |
65 | private static final String FRAGMENTS_TAG = "android:support:fragments"; |
66 | |
67 | // This is the SDK API version of Honeycomb (3.0). |
68 | private static final int HONEYCOMB = 11; |
69 | |
70 | static final int MSG_REALLY_STOPPED = 1; |
71 | |
72 | final Handler mHandler = new Handler() { |
73 | @Override |
74 | public void handleMessage(Message msg) { |
75 | switch (msg.what) { |
76 | case MSG_REALLY_STOPPED: |
77 | if (mStopped) { |
78 | doReallyStop(false); |
79 | } |
80 | break; |
81 | default: |
82 | super.handleMessage(msg); |
83 | } |
84 | } |
85 | |
86 | }; |
87 | final FragmentManagerImpl mFragments = new FragmentManagerImpl(); |
88 | |
89 | boolean mResumed; |
90 | boolean mStopped; |
91 | boolean mReallyStopped; |
92 | |
93 | boolean mOptionsMenuInvalidated; |
94 | |
95 | boolean mCheckedForLoaderManager; |
96 | boolean mLoadersStarted; |
97 | HCSparseArray<LoaderManagerImpl> mAllLoaderManagers; |
98 | LoaderManagerImpl mLoaderManager; |
99 | |
100 | static final class NonConfigurationInstances { |
101 | Object activity; |
102 | HashMap<String, Object> children; |
103 | ArrayList<Fragment> fragments; |
104 | HCSparseArray<LoaderManagerImpl> loaders; |
105 | } |
106 | |
107 | static class FragmentTag { |
108 | public static final int[] Fragment = { |
109 | 0x01010003, 0x010100d0, 0x010100d1 |
110 | }; |
111 | public static final int Fragment_id = 1; |
112 | public static final int Fragment_name = 0; |
113 | public static final int Fragment_tag = 2; |
114 | } |
115 | |
116 | // ------------------------------------------------------------------------ |
117 | // HOOKS INTO ACTIVITY |
118 | // ------------------------------------------------------------------------ |
119 | |
120 | /** |
121 | * Dispatch incoming result to the correct fragment. |
122 | */ |
123 | @Override |
124 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { |
125 | int index = requestCode>>16; |
126 | if (index != 0) { |
127 | index--; |
128 | if (mFragments.mActive == null || index < 0 || index >= mFragments.mActive.size()) { |
129 | Log.w(TAG, "Activity result fragment index out of range: 0x" |
130 | + Integer.toHexString(requestCode)); |
131 | return; |
132 | } |
133 | Fragment frag = mFragments.mActive.get(index); |
134 | if (frag == null) { |
135 | Log.w(TAG, "Activity result no fragment exists for index: 0x" |
136 | + Integer.toHexString(requestCode)); |
137 | } |
138 | frag.onActivityResult(requestCode&0xffff, resultCode, data); |
139 | return; |
140 | } |
141 | |
142 | super.onActivityResult(requestCode, resultCode, data); |
143 | } |
144 | |
145 | /** |
146 | * Take care of popping the fragment back stack or finishing the activity |
147 | * as appropriate. |
148 | */ |
149 | public void onBackPressed() { |
150 | if (!mFragments.popBackStackImmediate()) { |
151 | finish(); |
152 | } |
153 | } |
154 | |
155 | /** |
156 | * Dispatch configuration change to all fragments. |
157 | */ |
158 | @Override |
159 | public void onConfigurationChanged(Configuration newConfig) { |
160 | super.onConfigurationChanged(newConfig); |
161 | mFragments.dispatchConfigurationChanged(newConfig); |
162 | } |
163 | |
164 | /** |
165 | * Perform initialization of all fragments and loaders. |
166 | */ |
167 | @Override |
168 | protected void onCreate(Bundle savedInstanceState) { |
169 | mFragments.attachActivity(this); |
170 | // Old versions of the platform didn't do this! |
171 | if (getLayoutInflater().getFactory() == null) { |
172 | getLayoutInflater().setFactory(this); |
173 | } |
174 | |
175 | super.onCreate(savedInstanceState); |
176 | |
177 | NonConfigurationInstances nc = (NonConfigurationInstances) |
178 | getLastNonConfigurationInstance(); |
179 | if (nc != null) { |
180 | mAllLoaderManagers = nc.loaders; |
181 | } |
182 | if (savedInstanceState != null) { |
183 | Parcelable p = savedInstanceState.getParcelable(FRAGMENTS_TAG); |
184 | mFragments.restoreAllState(p, nc != null ? nc.fragments : null); |
185 | } |
186 | mFragments.dispatchCreate(); |
187 | } |
188 | |
189 | /** |
190 | * Dispatch to Fragment.onCreateOptionsMenu(). |
191 | */ |
192 | @Override |
193 | public boolean onCreatePanelMenu(int featureId, Menu menu) { |
194 | if (featureId == Window.FEATURE_OPTIONS_PANEL) { |
195 | boolean show = super.onCreatePanelMenu(featureId, menu); |
196 | show |= mFragments.dispatchCreateOptionsMenu(menu, getMenuInflater()); |
197 | //v4 if (android.os.Build.VERSION.SDK_INT >= HONEYCOMB) { |
198 | if (android.support.v2.os.Build.VERSION.SDK_INT >= HONEYCOMB) { |
199 | return show; |
200 | } |
201 | // Prior to Honeycomb, the framework can't invalidate the options |
202 | // menu, so we must always say we have one in case the app later |
203 | // invalidates it and needs to have it shown. |
204 | return true; |
205 | } |
206 | return super.onCreatePanelMenu(featureId, menu); |
207 | } |
208 | |
209 | /** |
210 | * Add support for inflating the <fragment> tag. |
211 | */ |
212 | @Override |
213 | public View onCreateView(String name, Context context, AttributeSet attrs) { |
214 | if (!"fragment".equals(name)) { |
215 | return super.onCreateView(name, context, attrs); |
216 | } |
217 | |
218 | String fname = attrs.getAttributeValue(null, "class"); |
219 | TypedArray a = context.obtainStyledAttributes(attrs, FragmentTag.Fragment); |
220 | if (fname == null) { |
221 | fname = a.getString(FragmentTag.Fragment_name); |
222 | } |
223 | int id = a.getResourceId(FragmentTag.Fragment_id, View.NO_ID); |
224 | String tag = a.getString(FragmentTag.Fragment_tag); |
225 | a.recycle(); |
226 | |
227 | View parent = null; // NOTE: no way to get parent pre-Honeycomb. |
228 | int containerId = parent != null ? parent.getId() : 0; |
229 | if (containerId == View.NO_ID && id == View.NO_ID && tag == null) { |
230 | throw new IllegalArgumentException(attrs.getPositionDescription() |
231 | + ": Must specify unique android:id, android:tag, or have a parent with an id for " + fname); |
232 | } |
233 | |
234 | // If we restored from a previous state, we may already have |
235 | // instantiated this fragment from the state and should use |
236 | // that instance instead of making a new one. |
237 | Fragment fragment = id != View.NO_ID ? mFragments.findFragmentById(id) : null; |
238 | if (fragment == null && tag != null) { |
239 | fragment = mFragments.findFragmentByTag(tag); |
240 | } |
241 | if (fragment == null && containerId != View.NO_ID) { |
242 | fragment = mFragments.findFragmentById(containerId); |
243 | } |
244 | |
245 | if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x" |
246 | + Integer.toHexString(id) + " fname=" + fname |
247 | + " existing=" + fragment); |
248 | if (fragment == null) { |
249 | fragment = Fragment.instantiate(this, fname); |
250 | fragment.mFromLayout = true; |
251 | fragment.mFragmentId = id != 0 ? id : containerId; |
252 | fragment.mContainerId = containerId; |
253 | fragment.mTag = tag; |
254 | fragment.mInLayout = true; |
255 | fragment.mImmediateActivity = this; |
256 | fragment.mFragmentManager = mFragments; |
257 | fragment.onInflate(this, attrs, fragment.mSavedFragmentState); |
258 | mFragments.addFragment(fragment, true); |
259 | |
260 | } else if (fragment.mInLayout) { |
261 | // A fragment already exists and it is not one we restored from |
262 | // previous state. |
263 | throw new IllegalArgumentException(attrs.getPositionDescription() |
264 | + ": Duplicate id 0x" + Integer.toHexString(id) |
265 | + ", tag " + tag + ", or parent id 0x" + Integer.toHexString(containerId) |
266 | + " with another fragment for " + fname); |
267 | } else { |
268 | // This fragment was retained from a previous instance; get it |
269 | // going now. |
270 | fragment.mInLayout = true; |
271 | fragment.mImmediateActivity = this; |
272 | // If this fragment is newly instantiated (either right now, or |
273 | // from last saved state), then give it the attributes to |
274 | // initialize itself. |
275 | if (!fragment.mRetaining) { |
276 | fragment.onInflate(this, attrs, fragment.mSavedFragmentState); |
277 | } |
278 | mFragments.moveToState(fragment); |
279 | } |
280 | |
281 | if (fragment.mView == null) { |
282 | throw new IllegalStateException("Fragment " + fname |
283 | + " did not create a view."); |
284 | } |
285 | if (id != 0) { |
286 | fragment.mView.setId(id); |
287 | } |
288 | if (fragment.mView.getTag() == null) { |
289 | fragment.mView.setTag(tag); |
290 | } |
291 | return fragment.mView; |
292 | } |
293 | |
294 | /** |
295 | * Destroy all fragments and loaders. |
296 | */ |
297 | @Override |
298 | protected void onDestroy() { |
299 | super.onDestroy(); |
300 | |
301 | doReallyStop(false); |
302 | |
303 | mFragments.dispatchDestroy(); |
304 | if (mLoaderManager != null) { |
305 | mLoaderManager.doDestroy(); |
306 | } |
307 | } |
308 | |
309 | /** |
310 | * Take care of calling onBackPressed() for pre-Eclair platforms. |
311 | */ |
312 | @Override |
313 | public boolean onKeyDown(int keyCode, KeyEvent event) { |
314 | //v4 if (android.os.Build.VERSION.SDK_INT < 5 /* ECLAIR */ |
315 | if (android.support.v2.os.Build.VERSION.SDK_INT < 5 /* ECLAIR */ |
316 | && keyCode == KeyEvent.KEYCODE_BACK |
317 | && event.getRepeatCount() == 0) { |
318 | // Take care of calling this method on earlier versions of |
319 | // the platform where it doesn't exist. |
320 | onBackPressed(); |
321 | return true; |
322 | } |
323 | |
324 | return super.onKeyDown(keyCode, event); |
325 | } |
326 | |
327 | /** |
328 | * Dispatch onLowMemory() to all fragments. |
329 | */ |
330 | @Override |
331 | public void onLowMemory() { |
332 | super.onLowMemory(); |
333 | mFragments.dispatchLowMemory(); |
334 | } |
335 | |
336 | /** |
337 | * Dispatch context and options menu to fragments. |
338 | */ |
339 | @Override |
340 | public boolean onMenuItemSelected(int featureId, MenuItem item) { |
341 | if (super.onMenuItemSelected(featureId, item)) { |
342 | return true; |
343 | } |
344 | |
345 | switch (featureId) { |
346 | case Window.FEATURE_OPTIONS_PANEL: |
347 | return mFragments.dispatchOptionsItemSelected(item); |
348 | |
349 | case Window.FEATURE_CONTEXT_MENU: |
350 | return mFragments.dispatchContextItemSelected(item); |
351 | |
352 | default: |
353 | return false; |
354 | } |
355 | } |
356 | |
357 | /** |
358 | * Call onOptionsMenuClosed() on fragments. |
359 | */ |
360 | @Override |
361 | public void onPanelClosed(int featureId, Menu menu) { |
362 | switch (featureId) { |
363 | case Window.FEATURE_OPTIONS_PANEL: |
364 | mFragments.dispatchOptionsMenuClosed(menu); |
365 | break; |
366 | } |
367 | super.onPanelClosed(featureId, menu); |
368 | } |
369 | |
370 | /** |
371 | * Dispatch onPause() to fragments. |
372 | */ |
373 | @Override |
374 | protected void onPause() { |
375 | super.onPause(); |
376 | mResumed = false; |
377 | mFragments.dispatchPause(); |
378 | } |
379 | |
380 | /** |
381 | * Dispatch onActivityCreated() on fragments. |
382 | */ |
383 | @Override |
384 | protected void onPostCreate(Bundle savedInstanceState) { |
385 | super.onPostCreate(savedInstanceState); |
386 | mFragments.dispatchActivityCreated(); |
387 | } |
388 | |
389 | /** |
390 | * Dispatch onResume() to fragments. |
391 | */ |
392 | @Override |
393 | protected void onPostResume() { |
394 | super.onPostResume(); |
395 | mFragments.dispatchResume(); |
396 | mFragments.execPendingActions(); |
397 | } |
398 | |
399 | /** |
400 | * Dispatch onPrepareOptionsMenu() to fragments. |
401 | */ |
402 | @Override |
403 | public boolean onPreparePanel(int featureId, View view, Menu menu) { |
404 | if (featureId == Window.FEATURE_OPTIONS_PANEL && menu != null) { |
405 | if (mOptionsMenuInvalidated) { |
406 | mOptionsMenuInvalidated = false; |
407 | menu.clear(); |
408 | onCreatePanelMenu(featureId, menu); |
409 | } |
410 | boolean goforit = super.onPreparePanel(featureId, view, menu); |
411 | goforit |= mFragments.dispatchPrepareOptionsMenu(menu); |
412 | return goforit && menu.hasVisibleItems(); |
413 | } |
414 | return super.onPreparePanel(featureId, view, menu); |
415 | } |
416 | |
417 | /** |
418 | * Ensure any outstanding fragment transactions have been committed. |
419 | */ |
420 | @Override |
421 | protected void onResume() { |
422 | super.onResume(); |
423 | mResumed = true; |
424 | mFragments.execPendingActions(); |
425 | } |
426 | |
427 | /** |
428 | * Retain all appropriate fragment and loader state. You can NOT |
429 | * override this yourself! |
430 | */ |
431 | @Override |
432 | public final Object onRetainNonConfigurationInstance() { |
433 | if (mStopped) { |
434 | doReallyStop(true); |
435 | } |
436 | |
437 | ArrayList<Fragment> fragments = mFragments.retainNonConfig(); |
438 | boolean retainLoaders = false; |
439 | if (mAllLoaderManagers != null) { |
440 | // prune out any loader managers that were already stopped and so |
441 | // have nothing useful to retain. |
442 | for (int i=mAllLoaderManagers.size()-1; i>=0; i--) { |
443 | LoaderManagerImpl lm = mAllLoaderManagers.valueAt(i); |
444 | if (lm.mRetaining) { |
445 | retainLoaders = true; |
446 | } else { |
447 | lm.doDestroy(); |
448 | mAllLoaderManagers.removeAt(i); |
449 | } |
450 | } |
451 | } |
452 | if (fragments == null && !retainLoaders) { |
453 | return null; |
454 | } |
455 | |
456 | NonConfigurationInstances nci = new NonConfigurationInstances(); |
457 | nci.activity = null; |
458 | nci.children = null; |
459 | nci.fragments = fragments; |
460 | nci.loaders = mAllLoaderManagers; |
461 | return nci; |
462 | } |
463 | |
464 | /** |
465 | * Save all appropriate fragment state. |
466 | */ |
467 | @Override |
468 | protected void onSaveInstanceState(Bundle outState) { |
469 | super.onSaveInstanceState(outState); |
470 | Parcelable p = mFragments.saveAllState(); |
471 | if (p != null) { |
472 | outState.putParcelable(FRAGMENTS_TAG, p); |
473 | } |
474 | } |
475 | |
476 | /** |
477 | * Dispatch onStart() to all fragments. Ensure any created loaders are |
478 | * now started. |
479 | */ |
480 | @Override |
481 | protected void onStart() { |
482 | super.onStart(); |
483 | |
484 | mStopped = false; |
485 | mHandler.removeMessages(MSG_REALLY_STOPPED); |
486 | |
487 | mFragments.noteStateNotSaved(); |
488 | mFragments.execPendingActions(); |
489 | |
490 | |
491 | if (!mLoadersStarted) { |
492 | mLoadersStarted = true; |
493 | if (mLoaderManager != null) { |
494 | mLoaderManager.doStart(); |
495 | } else if (!mCheckedForLoaderManager) { |
496 | mLoaderManager = getLoaderManager(-1, mLoadersStarted, false); |
497 | } |
498 | mCheckedForLoaderManager = true; |
499 | } |
500 | // NOTE: HC onStart goes here. |
501 | |
502 | mFragments.dispatchStart(); |
503 | if (mAllLoaderManagers != null) { |
504 | for (int i=mAllLoaderManagers.size()-1; i>=0; i--) { |
505 | mAllLoaderManagers.valueAt(i).finishRetain(); |
506 | } |
507 | } |
508 | } |
509 | |
510 | /** |
511 | * Dispatch onStop() to all fragments. Ensure all loaders are stopped. |
512 | */ |
513 | @Override |
514 | protected void onStop() { |
515 | super.onStop(); |
516 | |
517 | mStopped = true; |
518 | mHandler.sendEmptyMessage(MSG_REALLY_STOPPED); |
519 | |
520 | mFragments.dispatchStop(); |
521 | } |
522 | |
523 | // ------------------------------------------------------------------------ |
524 | // NEW METHODS |
525 | // ------------------------------------------------------------------------ |
526 | |
527 | void supportInvalidateOptionsMenu() { |
528 | //v4 if (android.os.Build.VERSION.SDK_INT >= HONEYCOMB) { |
529 | if (android.support.v2.os.Build.VERSION.SDK_INT >= HONEYCOMB) { |
530 | // If we are running on HC or greater, we can use the framework |
531 | // API to invalidate the options menu. |
532 | ActivityCompatHoneycomb.invalidateOptionsMenu(this); |
533 | return; |
534 | } |
535 | |
536 | // Whoops, older platform... we'll use a hack, to manually rebuild |
537 | // the options menu the next time it is prepared. |
538 | mOptionsMenuInvalidated = true; |
539 | } |
540 | |
541 | /** |
542 | * Print the Activity's state into the given stream. This gets invoked if |
543 | * you run "adb shell dumpsys activity <activity_component_name>". |
544 | * |
545 | * @param prefix Desired prefix to prepend at each line of output. |
546 | * @param fd The raw file descriptor that the dump is being sent to. |
547 | * @param writer The PrintWriter to which you should dump your state. This will be |
548 | * closed for you after you return. |
549 | * @param args additional arguments to the dump request. |
550 | */ |
551 | public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) { |
552 | //v4 if (android.os.Build.VERSION.SDK_INT >= HONEYCOMB) { |
553 | if (android.support.v2.os.Build.VERSION.SDK_INT >= HONEYCOMB) { |
554 | // XXX This can only work if we can call the super-class impl. :/ |
555 | //ActivityCompatHoneycomb.dump(this, prefix, fd, writer, args); |
556 | } |
557 | writer.print(prefix); writer.print("Local FragmentActivity "); |
558 | writer.print(Integer.toHexString(System.identityHashCode(this))); |
559 | writer.println(" State:"); |
560 | String innerPrefix = prefix + " "; |
561 | writer.print(innerPrefix); writer.print("mResumed="); |
562 | writer.print(mResumed); writer.print(" mStopped="); |
563 | writer.print(mStopped); writer.print(" mReallyStopped="); |
564 | writer.println(mReallyStopped); |
565 | writer.print(innerPrefix); writer.print("mLoadersStarted="); |
566 | writer.println(mLoadersStarted); |
567 | if (mLoaderManager != null) { |
568 | writer.print(prefix); writer.print("Loader Manager "); |
569 | writer.print(Integer.toHexString(System.identityHashCode(mLoaderManager))); |
570 | writer.println(":"); |
571 | mLoaderManager.dump(prefix + " ", fd, writer, args); |
572 | } |
573 | mFragments.dump(prefix, fd, writer, args); |
574 | } |
575 | |
576 | void doReallyStop(boolean retaining) { |
577 | if (!mReallyStopped) { |
578 | mReallyStopped = true; |
579 | mHandler.removeMessages(MSG_REALLY_STOPPED); |
580 | onReallyStop(retaining); |
581 | } |
582 | } |
583 | |
584 | /** |
585 | * Pre-HC, we didn't have a way to determine whether an activity was |
586 | * being stopped for a config change or not until we saw |
587 | * onRetainNonConfigurationInstance() called after onStop(). However |
588 | * we need to know this, to know whether to retain fragments. This will |
589 | * tell us what we need to know. |
590 | */ |
591 | void onReallyStop(boolean retaining) { |
592 | if (mLoadersStarted) { |
593 | mLoadersStarted = false; |
594 | if (mLoaderManager != null) { |
595 | if (!retaining) { |
596 | mLoaderManager.doStop(); |
597 | } else { |
598 | mLoaderManager.doRetain(); |
599 | } |
600 | } |
601 | } |
602 | |
603 | mFragments.dispatchReallyStop(retaining); |
604 | } |
605 | |
606 | // ------------------------------------------------------------------------ |
607 | // FRAGMENT SUPPORT |
608 | // ------------------------------------------------------------------------ |
609 | |
610 | /** |
611 | * Called when a fragment is attached to the activity. |
612 | */ |
613 | public void onAttachFragment(Fragment fragment) { |
614 | } |
615 | |
616 | /** |
617 | * Return the FragmentManager for interacting with fragments associated |
618 | * with this activity. |
619 | */ |
620 | public FragmentManager getSupportFragmentManager() { |
621 | return mFragments; |
622 | } |
623 | |
624 | /** |
625 | * Modifies the standard behavior to allow results to be delivered to fragments. |
626 | * This imposes a restriction that requestCode be <= 0xffff. |
627 | */ |
628 | @Override |
629 | public void startActivityForResult(Intent intent, int requestCode) { |
630 | if (requestCode != -1 && (requestCode&0xffff0000) != 0) { |
631 | throw new IllegalArgumentException("Can only use lower 16 bits for requestCode"); |
632 | } |
633 | super.startActivityForResult(intent, requestCode); |
634 | } |
635 | |
636 | /** |
637 | * Called by Fragment.startActivityForResult() to implement its behavior. |
638 | */ |
639 | public void startActivityFromFragment(Fragment fragment, Intent intent, |
640 | int requestCode) { |
641 | if (requestCode == -1) { |
642 | super.startActivityForResult(intent, -1); |
643 | return; |
644 | } |
645 | if ((requestCode&0xffff0000) != 0) { |
646 | throw new IllegalArgumentException("Can only use lower 16 bits for requestCode"); |
647 | } |
648 | super.startActivityForResult(intent, ((fragment.mIndex+1)<<16) + (requestCode&0xffff)); |
649 | } |
650 | |
651 | void invalidateSupportFragmentIndex(int index) { |
652 | //Log.v(TAG, "invalidateFragmentIndex: index=" + index); |
653 | if (mAllLoaderManagers != null) { |
654 | LoaderManagerImpl lm = mAllLoaderManagers.get(index); |
655 | if (lm != null) { |
656 | lm.doDestroy(); |
657 | } |
658 | mAllLoaderManagers.remove(index); |
659 | } |
660 | } |
661 | |
662 | // ------------------------------------------------------------------------ |
663 | // LOADER SUPPORT |
664 | // ------------------------------------------------------------------------ |
665 | |
666 | /** |
667 | * Return the LoaderManager for this fragment, creating it if needed. |
668 | */ |
669 | public LoaderManager getSupportLoaderManager() { |
670 | if (mLoaderManager != null) { |
671 | return mLoaderManager; |
672 | } |
673 | mCheckedForLoaderManager = true; |
674 | mLoaderManager = getLoaderManager(-1, mLoadersStarted, true); |
675 | return mLoaderManager; |
676 | } |
677 | |
678 | LoaderManagerImpl getLoaderManager(int index, boolean started, boolean create) { |
679 | if (mAllLoaderManagers == null) { |
680 | mAllLoaderManagers = new HCSparseArray<LoaderManagerImpl>(); |
681 | } |
682 | LoaderManagerImpl lm = mAllLoaderManagers.get(index); |
683 | if (lm == null) { |
684 | if (create) { |
685 | lm = new LoaderManagerImpl(this, started); |
686 | mAllLoaderManagers.put(index, lm); |
687 | } |
688 | } else { |
689 | lm.updateActivity(this); |
690 | } |
691 | return lm; |
692 | } |
693 | } |