OSDN Git Service

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