DataManager.java revision f31954e5b5f227b07fe377545fb05327d7e7ffe6
1/* 2 * Copyright (C) 2010 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 com.android.gallery3d.data; 18 19import com.android.gallery3d.app.GalleryApp; 20import com.android.gallery3d.common.Utils; 21import com.android.gallery3d.data.MediaSet.ItemConsumer; 22import com.android.gallery3d.data.MediaSource.PathId; 23import com.android.gallery3d.picasasource.PicasaSource; 24 25import android.database.ContentObserver; 26import android.net.Uri; 27import android.os.Handler; 28 29import java.util.ArrayList; 30import java.util.Comparator; 31import java.util.HashMap; 32import java.util.LinkedHashMap; 33import java.util.Map.Entry; 34import java.util.WeakHashMap; 35 36// DataManager manages all media sets and media items in the system. 37// 38// Each MediaSet and MediaItem has a unique 64 bits id. The most significant 39// 32 bits represents its parent, and the least significant 32 bits represents 40// the self id. For MediaSet the self id is is globally unique, but for 41// MediaItem it's unique only relative to its parent. 42// 43// To make sure the id is the same when the MediaSet is re-created, a child key 44// is provided to obtainSetId() to make sure the same self id will be used as 45// when the parent and key are the same. A sequence of child keys is called a 46// path. And it's used to identify a specific media set even if the process is 47// killed and re-created, so child keys should be stable identifiers. 48 49public class DataManager { 50 public static final int INCLUDE_IMAGE = 1; 51 public static final int INCLUDE_VIDEO = 2; 52 public static final int INCLUDE_ALL = INCLUDE_IMAGE | INCLUDE_VIDEO; 53 public static final int INCLUDE_LOCAL_ONLY = 4; 54 public static final int INCLUDE_LOCAL_IMAGE_ONLY = 55 INCLUDE_LOCAL_ONLY | INCLUDE_IMAGE; 56 public static final int INCLUDE_LOCAL_VIDEO_ONLY = 57 INCLUDE_LOCAL_ONLY | INCLUDE_VIDEO; 58 public static final int INCLUDE_LOCAL_ALL_ONLY = 59 INCLUDE_LOCAL_ONLY | INCLUDE_IMAGE | INCLUDE_VIDEO; 60 61 // Any one who would like to access data should require this lock 62 // to prevent concurrency issue. 63 public static final Object LOCK = new Object(); 64 65 private static final String TAG = "DataManager"; 66 67 // This is the path for the media set seen by the user at top level. 68 private static final String TOP_SET_PATH = 69 "/combo/{/mtp,/local/all,/picasa/all}"; 70 private static final String TOP_IMAGE_SET_PATH = 71 "/combo/{/mtp,/local/image,/picasa/image}"; 72 private static final String TOP_VIDEO_SET_PATH = 73 "/combo/{/local/video,/picasa/video}"; 74 private static final String TOP_LOCAL_SET_PATH = 75 "/local/all"; 76 private static final String TOP_LOCAL_IMAGE_SET_PATH = 77 "/local/image"; 78 private static final String TOP_LOCAL_VIDEO_SET_PATH = 79 "/local/video"; 80 81 public static final Comparator<MediaItem> sDateTakenComparator = 82 new DateTakenComparator(); 83 84 private static class DateTakenComparator implements Comparator<MediaItem> { 85 public int compare(MediaItem item1, MediaItem item2) { 86 return -Utils.compare(item1.getDateInMs(), item2.getDateInMs()); 87 } 88 } 89 90 private final Handler mDefaultMainHandler; 91 92 private GalleryApp mApplication; 93 private int mActiveCount = 0; 94 95 private HashMap<Uri, NotifyBroker> mNotifierMap = 96 new HashMap<Uri, NotifyBroker>(); 97 98 99 private HashMap<String, MediaSource> mSourceMap = 100 new LinkedHashMap<String, MediaSource>(); 101 102 public DataManager(GalleryApp application) { 103 mApplication = application; 104 mDefaultMainHandler = new Handler(application.getMainLooper()); 105 } 106 107 public synchronized void initializeSourceMap() { 108 if (!mSourceMap.isEmpty()) return; 109 110 // the order matters, the UriSource must come last 111 addSource(new LocalSource(mApplication)); 112 addSource(new PicasaSource(mApplication)); 113 addSource(new MtpSource(mApplication)); 114 addSource(new ComboSource(mApplication)); 115 addSource(new ClusterSource(mApplication)); 116 addSource(new FilterSource(mApplication)); 117 addSource(new UriSource(mApplication)); 118 119 if (mActiveCount > 0) { 120 for (MediaSource source : mSourceMap.values()) { 121 source.resume(); 122 } 123 } 124 } 125 126 public String getTopSetPath(int typeBits) { 127 128 switch (typeBits) { 129 case INCLUDE_IMAGE: return TOP_IMAGE_SET_PATH; 130 case INCLUDE_VIDEO: return TOP_VIDEO_SET_PATH; 131 case INCLUDE_ALL: return TOP_SET_PATH; 132 case INCLUDE_LOCAL_IMAGE_ONLY: return TOP_LOCAL_IMAGE_SET_PATH; 133 case INCLUDE_LOCAL_VIDEO_ONLY: return TOP_LOCAL_VIDEO_SET_PATH; 134 case INCLUDE_LOCAL_ALL_ONLY: return TOP_LOCAL_SET_PATH; 135 default: throw new IllegalArgumentException(); 136 } 137 } 138 139 // open for debug 140 void addSource(MediaSource source) { 141 mSourceMap.put(source.getPrefix(), source); 142 } 143 144 public MediaObject peekMediaObject(Path path) { 145 return path.getObject(); 146 } 147 148 public MediaSet peekMediaSet(Path path) { 149 return (MediaSet) path.getObject(); 150 } 151 152 public MediaObject getMediaObject(Path path) { 153 MediaObject obj = path.getObject(); 154 if (obj != null) return obj; 155 156 MediaSource source = mSourceMap.get(path.getPrefix()); 157 if (source == null) { 158 Log.w(TAG, "cannot find media source for path: " + path); 159 return null; 160 } 161 162 try { 163 MediaObject object = source.createMediaObject(path); 164 if (object == null) { 165 Log.w(TAG, "cannot create media object: " + path); 166 } 167 return object; 168 } catch (Throwable t) { 169 Log.w(TAG, "exception in creating media object: " + path, t); 170 return null; 171 } 172 } 173 174 public MediaObject getMediaObject(String s) { 175 return getMediaObject(Path.fromString(s)); 176 } 177 178 public MediaSet getMediaSet(Path path) { 179 return (MediaSet) getMediaObject(path); 180 } 181 182 public MediaSet getMediaSet(String s) { 183 return (MediaSet) getMediaObject(s); 184 } 185 186 public MediaSet[] getMediaSetsFromString(String segment) { 187 String[] seq = Path.splitSequence(segment); 188 int n = seq.length; 189 MediaSet[] sets = new MediaSet[n]; 190 for (int i = 0; i < n; i++) { 191 sets[i] = getMediaSet(seq[i]); 192 } 193 return sets; 194 } 195 196 // Maps a list of Paths to MediaItems, and invoke consumer.consume() 197 // for each MediaItem (may not be in the same order as the input list). 198 // An index number is also passed to consumer.consume() to identify 199 // the original position in the input list of the corresponding Path (plus 200 // startIndex). 201 public void mapMediaItems(ArrayList<Path> list, ItemConsumer consumer, 202 int startIndex) { 203 HashMap<String, ArrayList<PathId>> map = 204 new HashMap<String, ArrayList<PathId>>(); 205 206 // Group the path by the prefix. 207 int n = list.size(); 208 for (int i = 0; i < n; i++) { 209 Path path = list.get(i); 210 String prefix = path.getPrefix(); 211 ArrayList<PathId> group = map.get(prefix); 212 if (group == null) { 213 group = new ArrayList<PathId>(); 214 map.put(prefix, group); 215 } 216 group.add(new PathId(path, i + startIndex)); 217 } 218 219 // For each group, ask the corresponding media source to map it. 220 for (Entry<String, ArrayList<PathId>> entry : map.entrySet()) { 221 String prefix = entry.getKey(); 222 MediaSource source = mSourceMap.get(prefix); 223 source.mapMediaItems(entry.getValue(), consumer); 224 } 225 } 226 227 // The following methods forward the request to the proper object. 228 public int getSupportedOperations(Path path) { 229 return getMediaObject(path).getSupportedOperations(); 230 } 231 232 public void delete(Path path) { 233 getMediaObject(path).delete(); 234 } 235 236 public void rotate(Path path, int degrees) { 237 getMediaObject(path).rotate(degrees); 238 } 239 240 public Uri getContentUri(Path path) { 241 return getMediaObject(path).getContentUri(); 242 } 243 244 public int getMediaType(Path path) { 245 return getMediaObject(path).getMediaType(); 246 } 247 248 public MediaDetails getDetails(Path path) { 249 return getMediaObject(path).getDetails(); 250 } 251 252 public void cache(Path path, int flag) { 253 getMediaObject(path).cache(flag); 254 } 255 256 public Path findPathByUri(Uri uri) { 257 if (uri == null) return null; 258 for (MediaSource source : mSourceMap.values()) { 259 Path path = source.findPathByUri(uri); 260 if (path != null) return path; 261 } 262 return null; 263 } 264 265 public Path getDefaultSetOf(Path item) { 266 MediaSource source = mSourceMap.get(item.getPrefix()); 267 return source == null ? null : source.getDefaultSetOf(item); 268 } 269 270 // Returns number of bytes used by cached pictures currently downloaded. 271 public long getTotalUsedCacheSize() { 272 long sum = 0; 273 for (MediaSource source : mSourceMap.values()) { 274 sum += source.getTotalUsedCacheSize(); 275 } 276 return sum; 277 } 278 279 // Returns number of bytes used by cached pictures if all pending 280 // downloads and removals are completed. 281 public long getTotalTargetCacheSize() { 282 long sum = 0; 283 for (MediaSource source : mSourceMap.values()) { 284 sum += source.getTotalTargetCacheSize(); 285 } 286 return sum; 287 } 288 289 public void registerChangeNotifier(Uri uri, ChangeNotifier notifier) { 290 NotifyBroker broker = null; 291 synchronized (mNotifierMap) { 292 broker = mNotifierMap.get(uri); 293 if (broker == null) { 294 broker = new NotifyBroker(mDefaultMainHandler); 295 mApplication.getContentResolver() 296 .registerContentObserver(uri, true, broker); 297 mNotifierMap.put(uri, broker); 298 } 299 } 300 broker.registerNotifier(notifier); 301 } 302 303 public void resume() { 304 if (++mActiveCount == 1) { 305 for (MediaSource source : mSourceMap.values()) { 306 source.resume(); 307 } 308 } 309 } 310 311 public void pause() { 312 if (--mActiveCount == 0) { 313 for (MediaSource source : mSourceMap.values()) { 314 source.pause(); 315 } 316 } 317 } 318 319 private static class NotifyBroker extends ContentObserver { 320 private WeakHashMap<ChangeNotifier, Object> mNotifiers = 321 new WeakHashMap<ChangeNotifier, Object>(); 322 323 public NotifyBroker(Handler handler) { 324 super(handler); 325 } 326 327 public synchronized void registerNotifier(ChangeNotifier notifier) { 328 mNotifiers.put(notifier, null); 329 } 330 331 @Override 332 public synchronized void onChange(boolean selfChange) { 333 for(ChangeNotifier notifier : mNotifiers.keySet()) { 334 notifier.onChange(selfChange); 335 } 336 } 337 } 338} 339