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
1957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyanimport com.android.gallery3d.common.Utils;
20f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.util.Future;
21f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
22f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.ArrayList;
23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.WeakHashMap;
24f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// MediaSet is a directory-like data structure.
26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// It contains MediaItems and sub-MediaSets.
27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
28f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// The primary interface are:
29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// getMediaItemCount(), getMediaItem() and
30f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// getSubMediaSetCount(), getSubMediaSet().
31f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
32f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// getTotalMediaItemCount() returns the number of all MediaItems, including
33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// those in sub-MediaSets.
34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic abstract class MediaSet extends MediaObject {
357817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    @SuppressWarnings("unused")
36fe49be45412f8d0f68343662318e73822da486c8Owen Lin    private static final String TAG = "MediaSet";
37fe49be45412f8d0f68343662318e73822da486c8Owen Lin
38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int MEDIAITEM_BATCH_FETCH_COUNT = 500;
39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int INDEX_NOT_FOUND = -1;
40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
4157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    public static final int SYNC_RESULT_SUCCESS = 0;
4257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    public static final int SYNC_RESULT_CANCELLED = 1;
4357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    public static final int SYNC_RESULT_ERROR = 2;
4457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
4557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    /** Listener to be used with requestSync(SyncListener). */
4657cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    public static interface SyncListener {
4757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        /**
4857cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan         * Called when the sync task completed. Completion may be due to normal termination,
4957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan         * an exception, or cancellation.
5057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan         *
5157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan         * @param mediaSet the MediaSet that's done with sync
5257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan         * @param resultCode one of the SYNC_RESULT_* constants
5357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan         */
5457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        void onSyncDone(MediaSet mediaSet, int resultCode);
5557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    }
5657cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public MediaSet(Path path, long version) {
58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        super(path, version);
59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getMediaItemCount() {
62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return 0;
63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Returns the media items in the range [start, start + count).
66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    //
67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // The number of media items returned may be less than the specified count
68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // if there are not enough media items available. The number of
69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // media items available may not be consistent with the return value of
70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // getMediaItemCount() because the contents of database may have already
71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // changed.
72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public ArrayList<MediaItem> getMediaItem(int start, int count) {
73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return new ArrayList<MediaItem>();
74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
769ce0381a975f65ec7794a2d3436b0424d0030f39Owen Lin    public MediaItem getCoverMediaItem() {
779ce0381a975f65ec7794a2d3436b0424d0030f39Owen Lin        ArrayList<MediaItem> items = getMediaItem(0, 1);
789ce0381a975f65ec7794a2d3436b0424d0030f39Owen Lin        if (items.size() > 0) return items.get(0);
799ce0381a975f65ec7794a2d3436b0424d0030f39Owen Lin        for (int i = 0, n = getSubMediaSetCount(); i < n; i++) {
809ce0381a975f65ec7794a2d3436b0424d0030f39Owen Lin            MediaItem cover = getSubMediaSet(i).getCoverMediaItem();
819ce0381a975f65ec7794a2d3436b0424d0030f39Owen Lin            if (cover != null) return cover;
829ce0381a975f65ec7794a2d3436b0424d0030f39Owen Lin        }
839ce0381a975f65ec7794a2d3436b0424d0030f39Owen Lin        return null;
849ce0381a975f65ec7794a2d3436b0424d0030f39Owen Lin    }
859ce0381a975f65ec7794a2d3436b0424d0030f39Owen Lin
86f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getSubMediaSetCount() {
87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return 0;
88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public MediaSet getSubMediaSet(int index) {
91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        throw new IndexOutOfBoundsException();
92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public boolean isLeafAlbum() {
95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return false;
96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
987eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu    public boolean isCameraRoll() {
997eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu        return false;
1007eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu    }
1017eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu
102e8c1e69f85efb8673d0606f3aca729a366038753Ray Chen    /**
103e8c1e69f85efb8673d0606f3aca729a366038753Ray Chen     * Method {@link #reload()} may process the loading task in background, this method tells
104e8c1e69f85efb8673d0606f3aca729a366038753Ray Chen     * its client whether the loading is still in process or not.
105e8c1e69f85efb8673d0606f3aca729a366038753Ray Chen     */
106e8c1e69f85efb8673d0606f3aca729a366038753Ray Chen    public boolean isLoading() {
107e8c1e69f85efb8673d0606f3aca729a366038753Ray Chen        return false;
108e8c1e69f85efb8673d0606f3aca729a366038753Ray Chen    }
109e8c1e69f85efb8673d0606f3aca729a366038753Ray Chen
110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getTotalMediaItemCount() {
111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int total = getMediaItemCount();
112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0, n = getSubMediaSetCount(); i < n; i++) {
113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            total += getSubMediaSet(i).getTotalMediaItemCount();
114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return total;
116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // TODO: we should have better implementation of sub classes
119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getIndexOfItem(Path path, int hint) {
120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // hint < 0 is handled below
121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // first, try to find it around the hint
122f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int start = Math.max(0,
123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                hint - MEDIAITEM_BATCH_FETCH_COUNT / 2);
124f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ArrayList<MediaItem> list = getMediaItem(
125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                start, MEDIAITEM_BATCH_FETCH_COUNT);
126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int index = getIndexOf(path, list);
127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (index != INDEX_NOT_FOUND) return start + index;
128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
129f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // try to find it globally
130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        start = start == 0 ? MEDIAITEM_BATCH_FETCH_COUNT : 0;
131f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        list = getMediaItem(start, MEDIAITEM_BATCH_FETCH_COUNT);
132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        while (true) {
133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            index = getIndexOf(path, list);
134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (index != INDEX_NOT_FOUND) return start + index;
135f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (list.size() < MEDIAITEM_BATCH_FETCH_COUNT) return INDEX_NOT_FOUND;
136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            start += MEDIAITEM_BATCH_FETCH_COUNT;
137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            list = getMediaItem(start, MEDIAITEM_BATCH_FETCH_COUNT);
138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected int getIndexOf(Path path, ArrayList<MediaItem> list) {
142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0, n = list.size(); i < n; ++i) {
143b9e41109f413768d6f8bb5ec7c14ca31fbc0c098Owen Lin            // item could be null only in ClusterAlbum
144b9e41109f413768d6f8bb5ec7c14ca31fbc0c098Owen Lin            MediaObject item = list.get(i);
145b9e41109f413768d6f8bb5ec7c14ca31fbc0c098Owen Lin            if (item != null && item.mPath == path) return i;
146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
147f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return INDEX_NOT_FOUND;
148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
149f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public abstract String getName();
151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private WeakHashMap<ContentListener, Object> mListeners =
153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            new WeakHashMap<ContentListener, Object>();
154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // NOTE: The MediaSet only keeps a weak reference to the listener. The
156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // listener is automatically removed when there is no other reference to
157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // the listener.
158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void addContentListener(ContentListener listener) {
159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mListeners.containsKey(listener)) {
160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            throw new IllegalArgumentException();
161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mListeners.put(listener, null);
163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void removeContentListener(ContentListener listener) {
166f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (!mListeners.containsKey(listener)) {
167f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            throw new IllegalArgumentException();
168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mListeners.remove(listener);
170f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
172f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // This should be called by subclasses when the content is changed.
173f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void notifyContentChanged() {
174f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (ContentListener listener : mListeners.keySet()) {
175f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            listener.onContentDirty();
176f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
179f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Reload the content. Return the current data version. reload() should be called
180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // in the same thread as getMediaItem(int, int) and getSubMediaSet(int).
181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public abstract long reload();
182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
184f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public MediaDetails getDetails() {
185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        MediaDetails details = super.getDetails();
186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        details.addDetail(MediaDetails.INDEX_TITLE, getName());
187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return details;
188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Enumerate all media items in this media set (including the ones in sub
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // media sets), in an efficient order. ItemConsumer.consumer() will be
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // called for each media item with its index.
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void enumerateMediaItems(ItemConsumer consumer) {
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        enumerateMediaItems(consumer, 0);
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void enumerateTotalMediaItems(ItemConsumer consumer) {
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        enumerateTotalMediaItems(consumer, 0);
199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
201f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static interface ItemConsumer {
202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        void consume(int index, MediaItem item);
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // The default implementation uses getMediaItem() for enumerateMediaItems().
206f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Subclasses may override this and use more efficient implementations.
207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Returns the number of items enumerated.
208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected int enumerateMediaItems(ItemConsumer consumer, int startIndex) {
209f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int total = getMediaItemCount();
210f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int start = 0;
211f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        while (start < total) {
212f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int count = Math.min(MEDIAITEM_BATCH_FETCH_COUNT, total - start);
213f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ArrayList<MediaItem> items = getMediaItem(start, count);
214f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = 0, n = items.size(); i < n; i++) {
215f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                MediaItem item = items.get(i);
216f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                consumer.consume(startIndex + start + i, item);
217f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
218f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            start += count;
219f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
220f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return total;
221f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
223f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Recursively enumerate all media items under this set.
224f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Returns the number of items enumerated.
225f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected int enumerateTotalMediaItems(
226f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ItemConsumer consumer, int startIndex) {
227f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int start = 0;
228f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        start += enumerateMediaItems(consumer, startIndex);
229f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int m = getSubMediaSetCount();
230f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0; i < m; i++) {
231f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            start += getSubMediaSet(i).enumerateTotalMediaItems(
232f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    consumer, startIndex + start);
233f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
234f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return start;
235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
23757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    /**
23857cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan     * Requests sync on this MediaSet. It returns a Future object that can be used by the caller
23957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan     * to query the status of the sync. The sync result code is one of the SYNC_RESULT_* constants
24057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan     * defined in this class and can be obtained by Future.get().
24157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan     *
24257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan     * Subclasses should perform sync on a different thread.
24357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan     *
24457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan     * The default implementation here returns a Future stub that does nothing and returns
24557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan     * SYNC_RESULT_SUCCESS by get().
24657cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan     */
24757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    public Future<Integer> requestSync(SyncListener listener) {
248fe49be45412f8d0f68343662318e73822da486c8Owen Lin        listener.onSyncDone(this, SYNC_RESULT_SUCCESS);
249f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return FUTURE_STUB;
250f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
251f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
25257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    private static final Future<Integer> FUTURE_STUB = new Future<Integer>() {
253f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
254f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void cancel() {}
255f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
256f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
257f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean isCancelled() {
258f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return false;
259f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
260f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
261f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
262f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean isDone() {
263f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
264f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
265f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
266f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
26757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        public Integer get() {
26857cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            return SYNC_RESULT_SUCCESS;
269f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
270f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
271f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
272f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void waitDone() {}
273f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    };
27457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
275fe49be45412f8d0f68343662318e73822da486c8Owen Lin    protected Future<Integer> requestSyncOnMultipleSets(MediaSet[] sets, SyncListener listener) {
276fe49be45412f8d0f68343662318e73822da486c8Owen Lin        return new MultiSetSyncFuture(sets, listener);
27757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    }
27857cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
27957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    private class MultiSetSyncFuture implements Future<Integer>, SyncListener {
2807817979db0c52ffeacb951625b1e821eba303285Ahbong Chang        @SuppressWarnings("hiding")
28157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        private static final String TAG = "Gallery.MultiSetSync";
28257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
28357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        private final SyncListener mListener;
284fe49be45412f8d0f68343662318e73822da486c8Owen Lin        private final Future<Integer> mFutures[];
28557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
28657cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        private boolean mIsCancelled = false;
28757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        private int mResult = -1;
288fe49be45412f8d0f68343662318e73822da486c8Owen Lin        private int mPendingCount;
28957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
290fe49be45412f8d0f68343662318e73822da486c8Owen Lin        @SuppressWarnings("unchecked")
291fe49be45412f8d0f68343662318e73822da486c8Owen Lin        MultiSetSyncFuture(MediaSet[] sets, SyncListener listener) {
29257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            mListener = listener;
293fe49be45412f8d0f68343662318e73822da486c8Owen Lin            mPendingCount = sets.length;
294fe49be45412f8d0f68343662318e73822da486c8Owen Lin            mFutures = new Future[sets.length];
29557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
296fe49be45412f8d0f68343662318e73822da486c8Owen Lin            synchronized (this) {
297fe49be45412f8d0f68343662318e73822da486c8Owen Lin                for (int i = 0, n = sets.length; i < n; ++i) {
298fe49be45412f8d0f68343662318e73822da486c8Owen Lin                    mFutures[i] = sets[i].requestSync(this);
299fe49be45412f8d0f68343662318e73822da486c8Owen Lin                    Log.d(TAG, "  request sync: " + Utils.maskDebugInfo(sets[i].getName()));
30057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                }
30157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            }
30257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        }
30357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
30457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        @Override
30557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        public synchronized void cancel() {
30657cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            if (mIsCancelled) return;
30757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            mIsCancelled = true;
308fe49be45412f8d0f68343662318e73822da486c8Owen Lin            for (Future<Integer> future : mFutures) future.cancel();
30957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            if (mResult < 0) mResult = SYNC_RESULT_CANCELLED;
31057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        }
31157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
31257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        @Override
31357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        public synchronized boolean isCancelled() {
31457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            return mIsCancelled;
31557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        }
31657cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
31757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        @Override
31857cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        public synchronized boolean isDone() {
319fe49be45412f8d0f68343662318e73822da486c8Owen Lin            return mPendingCount == 0;
32057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        }
32157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
32257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        @Override
32357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        public synchronized Integer get() {
32457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            waitDone();
32557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            return mResult;
32657cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        }
32757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
32857cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        @Override
32957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        public synchronized void waitDone() {
33057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            try {
33157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                while (!isDone()) wait();
33257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            } catch (InterruptedException e) {
33357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                Log.d(TAG, "waitDone() interrupted");
33457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            }
33557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        }
33657cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
33757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        // SyncListener callback
33857cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        @Override
33957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        public void onSyncDone(MediaSet mediaSet, int resultCode) {
34057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            SyncListener listener = null;
34157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            synchronized (this) {
342fe49be45412f8d0f68343662318e73822da486c8Owen Lin                if (resultCode == SYNC_RESULT_ERROR) mResult = SYNC_RESULT_ERROR;
343fe49be45412f8d0f68343662318e73822da486c8Owen Lin                --mPendingCount;
344fe49be45412f8d0f68343662318e73822da486c8Owen Lin                if (mPendingCount == 0) {
345fe49be45412f8d0f68343662318e73822da486c8Owen Lin                    listener = mListener;
346fe49be45412f8d0f68343662318e73822da486c8Owen Lin                    notifyAll();
34757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                }
348fe49be45412f8d0f68343662318e73822da486c8Owen Lin                Log.d(TAG, "onSyncDone: " + Utils.maskDebugInfo(mediaSet.getName())
349fe49be45412f8d0f68343662318e73822da486c8Owen Lin                        + " #pending=" + mPendingCount);
35057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            }
35157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            if (listener != null) listener.onSyncDone(MediaSet.this, mResult);
35257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        }
35357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    }
354f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
355