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
192b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport android.database.ContentObserver;
202b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport android.net.Uri;
212b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport android.os.Handler;
222b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Lin
23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.app.GalleryApp;
244b4dbd225685502f4249c2bf25bf74f7ce526645George Mountimport com.android.gallery3d.app.StitchingChangeListener;
2504ebde7418493debda790f3eae933b5f6c38e897Chih-Chung Changimport com.android.gallery3d.common.ApiHelper;
26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.common.Utils;
274b4dbd225685502f4249c2bf25bf74f7ce526645George Mountimport com.android.gallery3d.data.MediaObject.PanoramaSupportCallback;
28f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.data.MediaSet.ItemConsumer;
29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.data.MediaSource.PathId;
30f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.picasasource.PicasaSource;
31f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
32f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.ArrayList;
33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.Comparator;
34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.HashMap;
35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.LinkedHashMap;
36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.Map.Entry;
37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.WeakHashMap;
38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// DataManager manages all media sets and media items in the system.
40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// Each MediaSet and MediaItem has a unique 64 bits id. The most significant
42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// 32 bits represents its parent, and the least significant 32 bits represents
43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// the self id. For MediaSet the self id is is globally unique, but for
44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// MediaItem it's unique only relative to its parent.
45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
46f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// To make sure the id is the same when the MediaSet is re-created, a child key
47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// is provided to obtainSetId() to make sure the same self id will be used as
48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// when the parent and key are the same. A sequence of child keys is called a
49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// path. And it's used to identify a specific media set even if the process is
50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// killed and re-created, so child keys should be stable identifiers.
51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
524b4dbd225685502f4249c2bf25bf74f7ce526645George Mountpublic class DataManager implements StitchingChangeListener {
53f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int INCLUDE_IMAGE = 1;
54f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int INCLUDE_VIDEO = 2;
55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int INCLUDE_ALL = INCLUDE_IMAGE | INCLUDE_VIDEO;
56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int INCLUDE_LOCAL_ONLY = 4;
57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int INCLUDE_LOCAL_IMAGE_ONLY =
58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            INCLUDE_LOCAL_ONLY | INCLUDE_IMAGE;
59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int INCLUDE_LOCAL_VIDEO_ONLY =
60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            INCLUDE_LOCAL_ONLY | INCLUDE_VIDEO;
61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int INCLUDE_LOCAL_ALL_ONLY =
62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            INCLUDE_LOCAL_ONLY | INCLUDE_IMAGE | INCLUDE_VIDEO;
63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Any one who would like to access data should require this lock
65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // to prevent concurrency issue.
66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final Object LOCK = new Object();
67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "DataManager";
69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // This is the path for the media set seen by the user at top level.
71b200e27b998df8856bc060a9f418e662864458b2Owen Lin    private static final String TOP_SET_PATH = ApiHelper.HAS_MTP
72b200e27b998df8856bc060a9f418e662864458b2Owen Lin            ? "/combo/{/mtp,/local/all,/picasa/all}"
73b200e27b998df8856bc060a9f418e662864458b2Owen Lin            : "/combo/{/local/all,/picasa/all}";
74b200e27b998df8856bc060a9f418e662864458b2Owen Lin
75b200e27b998df8856bc060a9f418e662864458b2Owen Lin    private static final String TOP_IMAGE_SET_PATH = ApiHelper.HAS_MTP
76b200e27b998df8856bc060a9f418e662864458b2Owen Lin            ? "/combo/{/mtp,/local/image,/picasa/image}"
77b200e27b998df8856bc060a9f418e662864458b2Owen Lin            : "/combo/{/local/image,/picasa/image}";
78b200e27b998df8856bc060a9f418e662864458b2Owen Lin
79f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TOP_VIDEO_SET_PATH =
80f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            "/combo/{/local/video,/picasa/video}";
81b200e27b998df8856bc060a9f418e662864458b2Owen Lin
82b200e27b998df8856bc060a9f418e662864458b2Owen Lin    private static final String TOP_LOCAL_SET_PATH = "/local/all";
83b200e27b998df8856bc060a9f418e662864458b2Owen Lin
84b200e27b998df8856bc060a9f418e662864458b2Owen Lin    private static final String TOP_LOCAL_IMAGE_SET_PATH = "/local/image";
85b200e27b998df8856bc060a9f418e662864458b2Owen Lin
86b200e27b998df8856bc060a9f418e662864458b2Owen Lin    private static final String TOP_LOCAL_VIDEO_SET_PATH = "/local/video";
87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final Comparator<MediaItem> sDateTakenComparator =
89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            new DateTakenComparator();
90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static class DateTakenComparator implements Comparator<MediaItem> {
927817979db0c52ffeacb951625b1e821eba303285Ahbong Chang        @Override
93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public int compare(MediaItem item1, MediaItem item2) {
94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return -Utils.compare(item1.getDateInMs(), item2.getDateInMs());
95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
98f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final Handler mDefaultMainHandler;
99f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
100f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private GalleryApp mApplication;
101f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mActiveCount = 0;
102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
103f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private HashMap<Uri, NotifyBroker> mNotifierMap =
104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            new HashMap<Uri, NotifyBroker>();
105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
106f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private HashMap<String, MediaSource> mSourceMap =
108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            new LinkedHashMap<String, MediaSource>();
109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public DataManager(GalleryApp application) {
111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mApplication = application;
112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mDefaultMainHandler = new Handler(application.getMainLooper());
113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public synchronized void initializeSourceMap() {
116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (!mSourceMap.isEmpty()) return;
117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // the order matters, the UriSource must come last
119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        addSource(new LocalSource(mApplication));
120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        addSource(new PicasaSource(mApplication));
12104ebde7418493debda790f3eae933b5f6c38e897Chih-Chung Chang        if (ApiHelper.HAS_MTP) {
12204ebde7418493debda790f3eae933b5f6c38e897Chih-Chung Chang            addSource(new MtpSource(mApplication));
12304ebde7418493debda790f3eae933b5f6c38e897Chih-Chung Chang        }
124f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        addSource(new ComboSource(mApplication));
125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        addSource(new ClusterSource(mApplication));
126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        addSource(new FilterSource(mApplication));
127a0ce682e40c39ce046c48d8a7f989fff01bb9f2cWu-cheng Li        addSource(new SecureSource(mApplication));
128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        addSource(new UriSource(mApplication));
12915b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang        addSource(new SnailSource(mApplication));
130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
131f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mActiveCount > 0) {
132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (MediaSource source : mSourceMap.values()) {
133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                source.resume();
134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
135f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public String getTopSetPath(int typeBits) {
139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        switch (typeBits) {
141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case INCLUDE_IMAGE: return TOP_IMAGE_SET_PATH;
142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case INCLUDE_VIDEO: return TOP_VIDEO_SET_PATH;
143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case INCLUDE_ALL: return TOP_SET_PATH;
144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case INCLUDE_LOCAL_IMAGE_ONLY: return TOP_LOCAL_IMAGE_SET_PATH;
145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case INCLUDE_LOCAL_VIDEO_ONLY: return TOP_LOCAL_VIDEO_SET_PATH;
146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            case INCLUDE_LOCAL_ALL_ONLY: return TOP_LOCAL_SET_PATH;
147f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            default: throw new IllegalArgumentException();
148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
149f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // open for debug
152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    void addSource(MediaSource source) {
153c64d127c976f8ef647552063ff14ead4388ce699Angus Kong        if (source == null) return;
154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSourceMap.put(source.getPrefix(), source);
155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
157676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin    // A common usage of this method is:
158676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin    // synchronized (DataManager.LOCK) {
159676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin    //     MediaObject object = peekMediaObject(path);
160676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin    //     if (object == null) {
161676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin    //         object = createMediaObject(...);
162676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin    //     }
163676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin    // }
164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public MediaObject peekMediaObject(Path path) {
165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return path.getObject();
166f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
167f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public MediaObject getMediaObject(Path path) {
169676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin        synchronized (LOCK) {
170676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin            MediaObject obj = path.getObject();
171676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin            if (obj != null) return obj;
172676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin
173676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin            MediaSource source = mSourceMap.get(path.getPrefix());
174676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin            if (source == null) {
175676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                Log.w(TAG, "cannot find media source for path: " + path);
176676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                return null;
177676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin            }
178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
179676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin            try {
180676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                MediaObject object = source.createMediaObject(path);
181676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                if (object == null) {
182676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    Log.w(TAG, "cannot create media object: " + path);
183676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                }
184676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                return object;
185676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin            } catch (Throwable t) {
186676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                Log.w(TAG, "exception in creating media object: " + path, t);
187676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                return null;
188f31954e5b5f227b07fe377545fb05327d7e7ffe6Pin Ting            }
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public MediaObject getMediaObject(String s) {
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return getMediaObject(Path.fromString(s));
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public MediaSet getMediaSet(Path path) {
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return (MediaSet) getMediaObject(path);
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public MediaSet getMediaSet(String s) {
201f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return (MediaSet) getMediaObject(s);
202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public MediaSet[] getMediaSetsFromString(String segment) {
205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        String[] seq = Path.splitSequence(segment);
206f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int n = seq.length;
207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        MediaSet[] sets = new MediaSet[n];
208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0; i < n; i++) {
209f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            sets[i] = getMediaSet(seq[i]);
210f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
211f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return sets;
212f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
213f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
214f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Maps a list of Paths to MediaItems, and invoke consumer.consume()
215f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // for each MediaItem (may not be in the same order as the input list).
216f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // An index number is also passed to consumer.consume() to identify
217f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // the original position in the input list of the corresponding Path (plus
218f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // startIndex).
219f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void mapMediaItems(ArrayList<Path> list, ItemConsumer consumer,
220f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int startIndex) {
221f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        HashMap<String, ArrayList<PathId>> map =
222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                new HashMap<String, ArrayList<PathId>>();
223f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
224f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Group the path by the prefix.
225f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int n = list.size();
226f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0; i < n; i++) {
227f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            Path path = list.get(i);
228f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            String prefix = path.getPrefix();
229f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ArrayList<PathId> group = map.get(prefix);
230f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (group == null) {
231f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                group = new ArrayList<PathId>();
232f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                map.put(prefix, group);
233f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
234f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            group.add(new PathId(path, i + startIndex));
235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
237f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // For each group, ask the corresponding media source to map it.
238f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (Entry<String, ArrayList<PathId>> entry : map.entrySet()) {
239f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            String prefix = entry.getKey();
240f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            MediaSource source = mSourceMap.get(prefix);
241f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            source.mapMediaItems(entry.getValue(), consumer);
242f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
243f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
244f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
245f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // The following methods forward the request to the proper object.
246f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getSupportedOperations(Path path) {
247f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return getMediaObject(path).getSupportedOperations();
248f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
249f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
2504b4dbd225685502f4249c2bf25bf74f7ce526645George Mount    public void getPanoramaSupport(Path path, PanoramaSupportCallback callback) {
2514b4dbd225685502f4249c2bf25bf74f7ce526645George Mount        getMediaObject(path).getPanoramaSupport(callback);
252825afb1b3e7d15b5c258d664ee008dc68da1c407Mangesh Ghiware    }
253825afb1b3e7d15b5c258d664ee008dc68da1c407Mangesh Ghiware
254f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void delete(Path path) {
255f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        getMediaObject(path).delete();
256f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
257f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
258f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void rotate(Path path, int degrees) {
259f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        getMediaObject(path).rotate(degrees);
260f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
261f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
262f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public Uri getContentUri(Path path) {
263f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return getMediaObject(path).getContentUri();
264f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
265f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
266f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getMediaType(Path path) {
267f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return getMediaObject(path).getMediaType();
268f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
269f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
27021a412c93c9b0401b039f4eb86d4950752e5bb84Owen Lin    public Path findPathByUri(Uri uri, String type) {
271f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (uri == null) return null;
272f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (MediaSource source : mSourceMap.values()) {
27321a412c93c9b0401b039f4eb86d4950752e5bb84Owen Lin            Path path = source.findPathByUri(uri, type);
274f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (path != null) return path;
275f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
276f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return null;
277f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
278f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
279f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public Path getDefaultSetOf(Path item) {
280f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        MediaSource source = mSourceMap.get(item.getPrefix());
281f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return source == null ? null : source.getDefaultSetOf(item);
282f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
283f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
284f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Returns number of bytes used by cached pictures currently downloaded.
285f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public long getTotalUsedCacheSize() {
286f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        long sum = 0;
287f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (MediaSource source : mSourceMap.values()) {
288f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            sum += source.getTotalUsedCacheSize();
289f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
290f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return sum;
291f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
292f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
293f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Returns number of bytes used by cached pictures if all pending
294f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // downloads and removals are completed.
295f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public long getTotalTargetCacheSize() {
296f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        long sum = 0;
297f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (MediaSource source : mSourceMap.values()) {
298f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            sum += source.getTotalTargetCacheSize();
299f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
300f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return sum;
301f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
302f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
303f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void registerChangeNotifier(Uri uri, ChangeNotifier notifier) {
304f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        NotifyBroker broker = null;
305f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        synchronized (mNotifierMap) {
306f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            broker = mNotifierMap.get(uri);
307f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (broker == null) {
308f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                broker = new NotifyBroker(mDefaultMainHandler);
309f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mApplication.getContentResolver()
310f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        .registerContentObserver(uri, true, broker);
311f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mNotifierMap.put(uri, broker);
312f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
313f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
314f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        broker.registerNotifier(notifier);
315f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
316f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
317f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void resume() {
318f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (++mActiveCount == 1) {
319f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (MediaSource source : mSourceMap.values()) {
320f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                source.resume();
321f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
322f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
323f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
324f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
325f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void pause() {
326f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (--mActiveCount == 0) {
327f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (MediaSource source : mSourceMap.values()) {
328f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                source.pause();
329f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
330f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
331f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
332f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
333f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static class NotifyBroker extends ContentObserver {
334f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private WeakHashMap<ChangeNotifier, Object> mNotifiers =
335f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                new WeakHashMap<ChangeNotifier, Object>();
336f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
337f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public NotifyBroker(Handler handler) {
338f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            super(handler);
339f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
340f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
341f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public synchronized void registerNotifier(ChangeNotifier notifier) {
342f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mNotifiers.put(notifier, null);
343f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
344f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
345f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
346f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public synchronized void onChange(boolean selfChange) {
347f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for(ChangeNotifier notifier : mNotifiers.keySet()) {
348f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                notifier.onChange(selfChange);
349f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
350f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
351f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
3524b4dbd225685502f4249c2bf25bf74f7ce526645George Mount
3534b4dbd225685502f4249c2bf25bf74f7ce526645George Mount    @Override
3544b4dbd225685502f4249c2bf25bf74f7ce526645George Mount    public void onStitchingQueued(Uri uri) {
3554b4dbd225685502f4249c2bf25bf74f7ce526645George Mount        // Do nothing.
3564b4dbd225685502f4249c2bf25bf74f7ce526645George Mount    }
3574b4dbd225685502f4249c2bf25bf74f7ce526645George Mount
3584b4dbd225685502f4249c2bf25bf74f7ce526645George Mount    @Override
3594b4dbd225685502f4249c2bf25bf74f7ce526645George Mount    public void onStitchingResult(Uri uri) {
3604b4dbd225685502f4249c2bf25bf74f7ce526645George Mount        Path path = findPathByUri(uri, null);
3614b4dbd225685502f4249c2bf25bf74f7ce526645George Mount        if (path != null) {
3624b4dbd225685502f4249c2bf25bf74f7ce526645George Mount            MediaObject mediaObject = getMediaObject(path);
3634b4dbd225685502f4249c2bf25bf74f7ce526645George Mount            if (mediaObject != null) {
3644b4dbd225685502f4249c2bf25bf74f7ce526645George Mount                mediaObject.clearCachedPanoramaSupport();
3654b4dbd225685502f4249c2bf25bf74f7ce526645George Mount            }
3664b4dbd225685502f4249c2bf25bf74f7ce526645George Mount        }
3674b4dbd225685502f4249c2bf25bf74f7ce526645George Mount    }
3684b4dbd225685502f4249c2bf25bf74f7ce526645George Mount
3694b4dbd225685502f4249c2bf25bf74f7ce526645George Mount    @Override
3704b4dbd225685502f4249c2bf25bf74f7ce526645George Mount    public void onStitchingProgress(Uri uri, int progress) {
3714b4dbd225685502f4249c2bf25bf74f7ce526645George Mount        // Do nothing.
3724b4dbd225685502f4249c2bf25bf74f7ce526645George Mount    }
373f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
374