Dmitri Plotnikov | 8605395 | 2010-04-29 10:22:01 -0700 | [diff] [blame] | 1 | /* |
| 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 | |
| 17 | package android.app.patterns; |
| 18 | |
| 19 | import android.app.Fragment; |
| 20 | import android.os.Bundle; |
| 21 | |
| 22 | import java.util.HashMap; |
| 23 | |
| 24 | public 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 Plotnikov | b6c7c6f | 2010-05-05 14:11:35 -0700 | [diff] [blame] | 41 | protected Loader<D> startLoading(int id, Bundle args) { |
Dmitri Plotnikov | 8605395 | 2010-04-29 10:22:01 -0700 | [diff] [blame] | 42 | 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 Plotnikov | b6c7c6f | 2010-05-05 14:11:35 -0700 | [diff] [blame] | 59 | return loader; |
Dmitri Plotnikov | 8605395 | 2010-04-29 10:22:01 -0700 | [diff] [blame] | 60 | } |
| 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 Plotnikov | b6170ca | 2010-04-29 18:23:31 -0700 | [diff] [blame] | 139 | loader.stopLoading(); |
Dmitri Plotnikov | 8605395 | 2010-04-29 10:22:01 -0700 | [diff] [blame] | 140 | // } |
| 141 | } |
| 142 | |
| 143 | mStarted = false; |
| 144 | } |
| 145 | |
Dianne Hackborn | 84df2b6 | 2010-05-12 19:04:42 -0700 | [diff] [blame] | 146 | /** TO DO: This needs to be turned into a retained fragment. |
Dmitri Plotnikov | 8605395 | 2010-04-29 10:22:01 -0700 | [diff] [blame] | 147 | @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 Hackborn | 84df2b6 | 2010-05-12 19:04:42 -0700 | [diff] [blame] | 154 | **/ |
Dmitri Plotnikov | 8605395 | 2010-04-29 10:22:01 -0700 | [diff] [blame] | 155 | |
| 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 | } |