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