MediaSet.java revision 57cbaa1ef40d2215c6400f5ae4af3a09e67abb2d
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;
2357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyanimport java.util.HashMap;
24f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.WeakHashMap;
25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// MediaSet is a directory-like data structure.
27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// It contains MediaItems and sub-MediaSets.
28f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// The primary interface are:
30f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// getMediaItemCount(), getMediaItem() and
31f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// getSubMediaSetCount(), getSubMediaSet().
32f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// getTotalMediaItemCount() returns the number of all MediaItems, including
34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// those in sub-MediaSets.
35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic abstract class MediaSet extends MediaObject {
36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int MEDIAITEM_BATCH_FETCH_COUNT = 500;
37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static final int INDEX_NOT_FOUND = -1;
38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
3957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    public static final int SYNC_RESULT_SUCCESS = 0;
4057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    public static final int SYNC_RESULT_CANCELLED = 1;
4157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    public static final int SYNC_RESULT_ERROR = 2;
4257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
4357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    /** Listener to be used with requestSync(SyncListener). */
4457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    public static interface SyncListener {
4557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        /**
4657cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan         * Called when the sync task completed. Completion may be due to normal termination,
4757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan         * an exception, or cancellation.
4857cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan         *
4957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan         * @param mediaSet the MediaSet that's done with sync
5057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan         * @param resultCode one of the SYNC_RESULT_* constants
5157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan         */
5257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        void onSyncDone(MediaSet mediaSet, int resultCode);
5357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    }
5457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public MediaSet(Path path, long version) {
56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        super(path, version);
57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getMediaItemCount() {
60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return 0;
61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Returns the media items in the range [start, start + count).
64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    //
65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // The number of media items returned may be less than the specified count
66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // if there are not enough media items available. The number of
67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // media items available may not be consistent with the return value of
68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // getMediaItemCount() because the contents of database may have already
69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // changed.
70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public ArrayList<MediaItem> getMediaItem(int start, int count) {
71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return new ArrayList<MediaItem>();
72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getSubMediaSetCount() {
75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return 0;
76f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
77f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
78f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public MediaSet getSubMediaSet(int index) {
79f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        throw new IndexOutOfBoundsException();
80f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
81f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
82f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public boolean isLeafAlbum() {
83f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return false;
84f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
85f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
86f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getTotalMediaItemCount() {
87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int total = getMediaItemCount();
88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0, n = getSubMediaSetCount(); i < n; i++) {
89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            total += getSubMediaSet(i).getTotalMediaItemCount();
90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return total;
92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // TODO: we should have better implementation of sub classes
95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getIndexOfItem(Path path, int hint) {
96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // hint < 0 is handled below
97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // first, try to find it around the hint
98f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int start = Math.max(0,
99f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                hint - MEDIAITEM_BATCH_FETCH_COUNT / 2);
100f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ArrayList<MediaItem> list = getMediaItem(
101f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                start, MEDIAITEM_BATCH_FETCH_COUNT);
102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int index = getIndexOf(path, list);
103f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (index != INDEX_NOT_FOUND) return start + index;
104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // try to find it globally
106f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        start = start == 0 ? MEDIAITEM_BATCH_FETCH_COUNT : 0;
107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        list = getMediaItem(start, MEDIAITEM_BATCH_FETCH_COUNT);
108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        while (true) {
109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            index = getIndexOf(path, list);
110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (index != INDEX_NOT_FOUND) return start + index;
111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (list.size() < MEDIAITEM_BATCH_FETCH_COUNT) return INDEX_NOT_FOUND;
112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            start += MEDIAITEM_BATCH_FETCH_COUNT;
113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            list = getMediaItem(start, MEDIAITEM_BATCH_FETCH_COUNT);
114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected int getIndexOf(Path path, ArrayList<MediaItem> list) {
118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0, n = list.size(); i < n; ++i) {
119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (list.get(i).mPath == path) return i;
120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return INDEX_NOT_FOUND;
122f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
124f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public abstract String getName();
125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private WeakHashMap<ContentListener, Object> mListeners =
127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            new WeakHashMap<ContentListener, Object>();
128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
129f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // NOTE: The MediaSet only keeps a weak reference to the listener. The
130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // listener is automatically removed when there is no other reference to
131f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // the listener.
132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void addContentListener(ContentListener listener) {
133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mListeners.containsKey(listener)) {
134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            throw new IllegalArgumentException();
135f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mListeners.put(listener, null);
137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void removeContentListener(ContentListener listener) {
140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (!mListeners.containsKey(listener)) {
141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            throw new IllegalArgumentException();
142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mListeners.remove(listener);
144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // This should be called by subclasses when the content is changed.
147f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void notifyContentChanged() {
148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (ContentListener listener : mListeners.keySet()) {
149f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            listener.onContentDirty();
150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Reload the content. Return the current data version. reload() should be called
154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // in the same thread as getMediaItem(int, int) and getSubMediaSet(int).
155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public abstract long reload();
156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public MediaDetails getDetails() {
159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        MediaDetails details = super.getDetails();
160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        details.addDetail(MediaDetails.INDEX_TITLE, getName());
161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return details;
162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Enumerate all media items in this media set (including the ones in sub
165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // media sets), in an efficient order. ItemConsumer.consumer() will be
166f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // called for each media item with its index.
167f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void enumerateMediaItems(ItemConsumer consumer) {
168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        enumerateMediaItems(consumer, 0);
169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
170f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void enumerateTotalMediaItems(ItemConsumer consumer) {
172f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        enumerateTotalMediaItems(consumer, 0);
173f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
174f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
175f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static interface ItemConsumer {
176f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        void consume(int index, MediaItem item);
177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
179f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // The default implementation uses getMediaItem() for enumerateMediaItems().
180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Subclasses may override this and use more efficient implementations.
181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Returns the number of items enumerated.
182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected int enumerateMediaItems(ItemConsumer consumer, int startIndex) {
183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int total = getMediaItemCount();
184f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int start = 0;
185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        while (start < total) {
186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int count = Math.min(MEDIAITEM_BATCH_FETCH_COUNT, total - start);
187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ArrayList<MediaItem> items = getMediaItem(start, count);
188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = 0, n = items.size(); i < n; i++) {
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                MediaItem item = items.get(i);
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                consumer.consume(startIndex + start + i, item);
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            start += count;
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return total;
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Recursively enumerate all media items under this set.
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Returns the number of items enumerated.
199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    protected int enumerateTotalMediaItems(
200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ItemConsumer consumer, int startIndex) {
201f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int start = 0;
202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        start += enumerateMediaItems(consumer, startIndex);
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int m = getSubMediaSetCount();
204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0; i < m; i++) {
205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            start += getSubMediaSet(i).enumerateTotalMediaItems(
206f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    consumer, startIndex + start);
207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return start;
209f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
210f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
21157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    /**
21257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan     * Requests sync on this MediaSet. It returns a Future object that can be used by the caller
21357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan     * to query the status of the sync. The sync result code is one of the SYNC_RESULT_* constants
21457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan     * defined in this class and can be obtained by Future.get().
21557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan     *
21657cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan     * Subclasses should perform sync on a different thread.
21757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan     *
21857cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan     * The default implementation here returns a Future stub that does nothing and returns
21957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan     * SYNC_RESULT_SUCCESS by get().
22057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan     */
22157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    public Future<Integer> requestSync(SyncListener listener) {
222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return FUTURE_STUB;
223f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
224f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
22557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    private static final Future<Integer> FUTURE_STUB = new Future<Integer>() {
226f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
227f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void cancel() {}
228f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
229f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
230f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean isCancelled() {
231f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return false;
232f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
233f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
234f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean isDone() {
236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return true;
237f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
238f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
239f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
24057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        public Integer get() {
24157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            return SYNC_RESULT_SUCCESS;
242f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
243f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
244f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
245f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void waitDone() {}
246f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    };
24757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
24857cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    protected Future<Integer> requestSyncOnEmptySets(MediaSet[] sets, SyncListener listener) {
24957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        MultiSetSyncFuture future = new MultiSetSyncFuture(listener);
25057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        future.requestSyncOnEmptySets(sets);
25157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        return future;
25257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    }
25357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
25457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    private class MultiSetSyncFuture implements Future<Integer>, SyncListener {
25557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        private static final String TAG = "Gallery.MultiSetSync";
25657cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
25757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        private final HashMap<MediaSet, Future<Integer>> mMediaSetMap =
25857cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                new HashMap<MediaSet, Future<Integer>>();
25957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        private final SyncListener mListener;
26057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
26157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        private boolean mIsCancelled = false;
26257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        private int mResult = -1;
26357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
26457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        MultiSetSyncFuture(SyncListener listener) {
26557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            mListener = listener;
26657cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        }
26757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
26857cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        synchronized void requestSyncOnEmptySets(MediaSet[] sets) {
26957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            for (MediaSet set : sets) {
27057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                if ((set.getMediaItemCount() == 0) && !mMediaSetMap.containsKey(set)) {
27157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                    // Sync results are handled in this.onSyncDone().
27257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                    Future<Integer> future = set.requestSync(this);
27357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                    if (!future.isDone()) {
27457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                        mMediaSetMap.put(set, future);
27557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                        Log.d(TAG, "  request sync: " + Utils.maskDebugInfo(set.getName()));
27657cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                    }
27757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                }
27857cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            }
27957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            Log.d(TAG, "requestSyncOnEmptySets actual=" + mMediaSetMap.size());
28057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        }
28157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
28257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        @Override
28357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        public synchronized void cancel() {
28457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            if (mIsCancelled) return;
28557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            mIsCancelled = true;
28657cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            for (Future<Integer> future : mMediaSetMap.values()) future.cancel();
28757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            mMediaSetMap.clear();
28857cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            if (mResult < 0) mResult = SYNC_RESULT_CANCELLED;
28957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        }
29057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
29157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        @Override
29257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        public synchronized boolean isCancelled() {
29357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            return mIsCancelled;
29457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        }
29557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
29657cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        @Override
29757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        public synchronized boolean isDone() {
29857cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            return mMediaSetMap.isEmpty();
29957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        }
30057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
30157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        @Override
30257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        public synchronized Integer get() {
30357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            waitDone();
30457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            return mResult;
30557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        }
30657cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
30757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        @Override
30857cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        public synchronized void waitDone() {
30957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            try {
31057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                while (!isDone()) wait();
31157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            } catch (InterruptedException e) {
31257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                Log.d(TAG, "waitDone() interrupted");
31357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            }
31457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        }
31557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan
31657cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        // SyncListener callback
31757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        @Override
31857cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        public void onSyncDone(MediaSet mediaSet, int resultCode) {
31957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            SyncListener listener = null;
32057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            synchronized (this) {
32157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                if (mMediaSetMap.remove(mediaSet) != null) {
32257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                    Log.d(TAG, "onSyncDone: " + Utils.maskDebugInfo(mediaSet.getName())
32357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                            + " #pending=" + mMediaSetMap.size());
32457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                    if (resultCode == SYNC_RESULT_ERROR) {
32557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                        mResult = SYNC_RESULT_ERROR;
32657cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                    }
32757cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                    if (mMediaSetMap.isEmpty()) {
32857cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                        if (mResult < 0) mResult = SYNC_RESULT_SUCCESS;
32957cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                        notifyAll();
33057cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                        listener = mListener;
33157cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                    }
33257cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan                }
33357cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            }
33457cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan            if (listener != null) listener.onSyncDone(MediaSet.this, mResult);
33557cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan        }
33657cbaa1ef40d2215c6400f5ae4af3a09e67abb2dHung-ying Tyan    }
337f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
338