blob: a337b1fa9f1d58a56f319c832b2338b1d29e027b [file] [log] [blame]
Dmitri Plotnikov86053952010-04-29 10:22:01 -07001/*
2 * Copyright (C) 2010 Google Inc.
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.patterns;
18
19import android.app.Fragment;
20import android.os.Bundle;
21
22import java.util.HashMap;
23
24public abstract class LoaderManagingFragment<D> extends Fragment
25 implements Loader.OnLoadCompleteListener<D> {
26 private boolean mStarted = false;
27
28 static final class LoaderInfo<D> {
29 public Bundle args;
30 public Loader<D> loader;
31 }
32 private HashMap<Integer, LoaderInfo<D>> mLoaders;
33 private HashMap<Integer, LoaderInfo<D>> mInactiveLoaders;
34
35 /**
36 * Registers a loader with this activity, registers the callbacks on it, and starts it loading.
37 * If a loader with the same id has previously been started it will automatically be destroyed
38 * when the new loader completes it's work. The callback will be delivered before the old loader
39 * is destroyed.
40 */
Dmitri Plotnikovb6c7c6f2010-05-05 14:11:35 -070041 protected Loader<D> startLoading(int id, Bundle args) {
Dmitri Plotnikov86053952010-04-29 10:22:01 -070042 LoaderInfo<D> info = mLoaders.get(id);
43 if (info != null) {
44 // Keep track of the previous instance of this loader so we can destroy
45 // it when the new one completes.
46 mInactiveLoaders.put(id, info);
47 }
48 info = new LoaderInfo<D>();
49 info.args = args;
50 mLoaders.put(id, info);
51 Loader<D> loader = onCreateLoader(id, args);
52 info.loader = loader;
53 if (mStarted) {
54 // The activity will start all existing loaders in it's onStart(), so only start them
55 // here if we're past that point of the activitiy's life cycle
56 loader.registerListener(id, this);
57 loader.startLoading();
58 }
Dmitri Plotnikovb6c7c6f2010-05-05 14:11:35 -070059 return loader;
Dmitri Plotnikov86053952010-04-29 10:22:01 -070060 }
61
62 protected abstract Loader<D> onCreateLoader(int id, Bundle args);
63 protected abstract void onInitializeLoaders();
64 protected abstract void onLoadFinished(Loader<D> loader, D data);
65
66 public final void onLoadComplete(Loader<D> loader, D data) {
67 // Notify of the new data so the app can switch out the old data before
68 // we try to destroy it.
69 onLoadFinished(loader, data);
70
71 // Look for an inactive loader and destroy it if found
72 int id = loader.getId();
73 LoaderInfo<D> info = mInactiveLoaders.get(id);
74 if (info != null) {
75 Loader<D> oldLoader = info.loader;
76 if (oldLoader != null) {
77 oldLoader.destroy();
78 }
79 mInactiveLoaders.remove(id);
80 }
81 }
82
83 @Override
84 public void onCreate(Bundle savedState) {
85 super.onCreate(savedState);
86
87 if (mLoaders == null) {
88 // Look for a passed along loader and create a new one if it's not there
89// TODO: uncomment once getLastNonConfigurationInstance method is available
90// mLoaders = (HashMap<Integer, LoaderInfo>) getLastNonConfigurationInstance();
91 if (mLoaders == null) {
92 mLoaders = new HashMap<Integer, LoaderInfo<D>>();
93 onInitializeLoaders();
94 }
95 }
96 if (mInactiveLoaders == null) {
97 mInactiveLoaders = new HashMap<Integer, LoaderInfo<D>>();
98 }
99 }
100
101 @Override
102 public void onStart() {
103 super.onStart();
104
105 // Call out to sub classes so they can start their loaders
106 // Let the existing loaders know that we want to be notified when a load is complete
107 for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
108 LoaderInfo<D> info = entry.getValue();
109 Loader<D> loader = info.loader;
110 int id = entry.getKey();
111 if (loader == null) {
112 loader = onCreateLoader(id, info.args);
113 info.loader = loader;
114 }
115 loader.registerListener(id, this);
116 loader.startLoading();
117 }
118
119 mStarted = true;
120 }
121
122 @Override
123 public void onStop() {
124 super.onStop();
125
126 for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
127 LoaderInfo<D> info = entry.getValue();
128 Loader<D> loader = info.loader;
129 if (loader == null) {
130 continue;
131 }
132
133 // Let the loader know we're done with it
134 loader.unregisterListener(this);
135
136 // The loader isn't getting passed along to the next instance so ask it to stop loading
137// TODO: uncomment once isChangingConfig method is available
138// if (!getActivity().isChangingConfigurations()) {
Dmitri Plotnikovb6170ca2010-04-29 18:23:31 -0700139 loader.stopLoading();
Dmitri Plotnikov86053952010-04-29 10:22:01 -0700140// }
141 }
142
143 mStarted = false;
144 }
145
Dianne Hackborn84df2b62010-05-12 19:04:42 -0700146 /** TO DO: This needs to be turned into a retained fragment.
Dmitri Plotnikov86053952010-04-29 10:22:01 -0700147 @Override
148 public Object onRetainNonConfigurationInstance() {
149 // Pass the loader along to the next guy
150 Object result = mLoaders;
151 mLoaders = null;
152 return result;
153 }
Dianne Hackborn84df2b62010-05-12 19:04:42 -0700154 **/
Dmitri Plotnikov86053952010-04-29 10:22:01 -0700155
156 @Override
157 public void onDestroy() {
158 super.onDestroy();
159
160 if (mLoaders != null) {
161 for (HashMap.Entry<Integer, LoaderInfo<D>> entry : mLoaders.entrySet()) {
162 LoaderInfo<D> info = entry.getValue();
163 Loader<D> loader = info.loader;
164 if (loader == null) {
165 continue;
166 }
167 loader.destroy();
168 }
169 }
170 }
171}