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
1984c220f10a643927c8a2126de8a755d8d7f7ec9eRay Chenimport android.net.Uri;
2084c220f10a643927c8a2126de8a755d8d7f7ec9eRay Chenimport android.provider.MediaStore;
2184c220f10a643927c8a2126de8a755d8d7f7ec9eRay Chen
2290df352389eda7c964e6a43a0b752d27f16e02a7Owen Linimport com.android.gallery3d.common.ApiHelper;
2390df352389eda7c964e6a43a0b752d27f16e02a7Owen Lin
24f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.lang.ref.SoftReference;
25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.ArrayList;
26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.Comparator;
27d87c8624a7ae477a6dea86d63866ade04f8d7e7cBobby Georgescuimport java.util.NoSuchElementException;
28f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.SortedMap;
29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.TreeMap;
30f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
31f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// MergeAlbum merges items from two or more MediaSets. It uses a Comparator to
32f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// determine the order of items. The items are assumed to be sorted in the input
33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// media sets (with the same order that the Comparator uses).
34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin//
35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// This only handles MediaItems, not SubMediaSets.
36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic class LocalMergeAlbum extends MediaSet implements ContentListener {
37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @SuppressWarnings("unused")
38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "LocalMergeAlbum";
39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int PAGE_SIZE = 64;
40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final Comparator<MediaItem> mComparator;
42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final MediaSet[] mSources;
43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private String mName;
45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private FetchCache[] mFetcher;
46f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mSupportedOperation;
4784c220f10a643927c8a2126de8a755d8d7f7ec9eRay Chen    private int mBucketId;
48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // mIndex maps global position to the position of each underlying media sets.
50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private TreeMap<Integer, int[]> mIndex = new TreeMap<Integer, int[]>();
51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
52f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public LocalMergeAlbum(
5384c220f10a643927c8a2126de8a755d8d7f7ec9eRay Chen            Path path, Comparator<MediaItem> comparator, MediaSet[] sources, int bucketId) {
54f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        super(path, INVALID_DATA_VERSION);
55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mComparator = comparator;
56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSources = sources;
57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mName = sources.length == 0 ? "" : sources[0].getName();
5884c220f10a643927c8a2126de8a755d8d7f7ec9eRay Chen        mBucketId = bucketId;
59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (MediaSet set : mSources) {
60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            set.addContentListener(this);
61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
626423defee1d8ccf4d462d40ae4f82c7d73b11ab5Bobby Georgescu        reload();
63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
657eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu    @Override
667eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu    public boolean isCameraRoll() {
677eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu        if (mSources.length == 0) return false;
687eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu        for(MediaSet set : mSources) {
697eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu            if (!set.isCameraRoll()) return false;
707eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu        }
717eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu        return true;
727eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu    }
737eea4d3ac59aa88d327fc0d58f5e4052f43c54c9Bobby Georgescu
74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void updateData() {
75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ArrayList<MediaSet> matches = new ArrayList<MediaSet>();
76f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int supported = mSources.length == 0 ? 0 : MediaItem.SUPPORT_ALL;
77f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mFetcher = new FetchCache[mSources.length];
78f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0, n = mSources.length; i < n; ++i) {
79f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mFetcher[i] = new FetchCache(mSources[i]);
80f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            supported &= mSources[i].getSupportedOperations();
81f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
82f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSupportedOperation = supported;
83f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mIndex.clear();
84f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mIndex.put(0, new int[mSources.length]);
85f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mName = mSources.length == 0 ? "" : mSources[0].getName();
86f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void invalidateCache() {
89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0, n = mSources.length; i < n; i++) {
90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mFetcher[i].invalidate();
91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mIndex.clear();
93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mIndex.put(0, new int[mSources.length]);
94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
9784c220f10a643927c8a2126de8a755d8d7f7ec9eRay Chen    public Uri getContentUri() {
9890df352389eda7c964e6a43a0b752d27f16e02a7Owen Lin        String bucketId = String.valueOf(mBucketId);
9990df352389eda7c964e6a43a0b752d27f16e02a7Owen Lin        if (ApiHelper.HAS_MEDIA_PROVIDER_FILES_TABLE) {
10090df352389eda7c964e6a43a0b752d27f16e02a7Owen Lin            return MediaStore.Files.getContentUri("external").buildUpon()
10190df352389eda7c964e6a43a0b752d27f16e02a7Owen Lin                    .appendQueryParameter(LocalSource.KEY_BUCKET_ID, bucketId)
10290df352389eda7c964e6a43a0b752d27f16e02a7Owen Lin                    .build();
10390df352389eda7c964e6a43a0b752d27f16e02a7Owen Lin        } else {
10490df352389eda7c964e6a43a0b752d27f16e02a7Owen Lin            // We don't have a single URL for a merged image before ICS
10590df352389eda7c964e6a43a0b752d27f16e02a7Owen Lin            // So we used the image's URL as a substitute.
10690df352389eda7c964e6a43a0b752d27f16e02a7Owen Lin            return MediaStore.Images.Media.EXTERNAL_CONTENT_URI.buildUpon()
10790df352389eda7c964e6a43a0b752d27f16e02a7Owen Lin                    .appendQueryParameter(LocalSource.KEY_BUCKET_ID, bucketId)
10890df352389eda7c964e6a43a0b752d27f16e02a7Owen Lin                    .build();
10990df352389eda7c964e6a43a0b752d27f16e02a7Owen Lin        }
11084c220f10a643927c8a2126de8a755d8d7f7ec9eRay Chen    }
11184c220f10a643927c8a2126de8a755d8d7f7ec9eRay Chen
11284c220f10a643927c8a2126de8a755d8d7f7ec9eRay Chen    @Override
113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public String getName() {
114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mName;
115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getMediaItemCount() {
119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return getTotalMediaItemCount();
120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
122f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public ArrayList<MediaItem> getMediaItem(int start, int count) {
124f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // First find the nearest mark position <= start.
126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        SortedMap<Integer, int[]> head = mIndex.headMap(start + 1);
1276423defee1d8ccf4d462d40ae4f82c7d73b11ab5Bobby Georgescu        int markPos = head.lastKey();
128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int[] subPos = head.get(markPos).clone();
129f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        MediaItem[] slot = new MediaItem[mSources.length];
130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
131f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int size = mSources.length;
132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // fill all slots
134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0; i < size; i++) {
135f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            slot[i] = mFetcher[i].getItem(subPos[i]);
136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        ArrayList<MediaItem> result = new ArrayList<MediaItem>();
139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = markPos; i < start + count; i++) {
141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int k = -1;  // k points to the best slot up to now.
142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int j = 0; j < size; j++) {
143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (slot[j] != null) {
144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    if (k == -1 || mComparator.compare(slot[j], slot[k]) < 0) {
145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        k = j;
146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
147f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
149f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // If we don't have anything, all streams are exhausted.
151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (k == -1) break;
152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // Pick the best slot and refill it.
154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            subPos[k]++;
155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (i >= start) {
156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                result.add(slot[k]);
157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            slot[k] = mFetcher[k].getItem(subPos[k]);
159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // Periodically leave a mark in the index, so we can come back later.
161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if ((i + 1) % PAGE_SIZE == 0) {
162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mIndex.put(i + 1, subPos.clone());
163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
166f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return result;
167f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
170f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getTotalMediaItemCount() {
171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int count = 0;
172f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (MediaSet set : mSources) {
173f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            count += set.getTotalMediaItemCount();
174f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
175f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return count;
176f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
179f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public long reload() {
180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        boolean changed = false;
181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0, n = mSources.length; i < n; ++i) {
182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mSources[i].reload() > mDataVersion) changed = true;
183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
184f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (changed) {
185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mDataVersion = nextVersionNumber();
186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateData();
187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            invalidateCache();
188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mDataVersion;
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void onContentDirty() {
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        notifyContentChanged();
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getSupportedOperations() {
199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mSupportedOperation;
200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
201f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void delete() {
204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (MediaSet set : mSources) {
205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            set.delete();
206f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
209f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
210f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void rotate(int degrees) {
211f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (MediaSet set : mSources) {
212f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            set.rotate(degrees);
213f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
214f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
215f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
216f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static class FetchCache {
217f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private MediaSet mBaseSet;
218f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private SoftReference<ArrayList<MediaItem>> mCacheRef;
219f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private int mStartPos;
220f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
221f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public FetchCache(MediaSet baseSet) {
222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mBaseSet = baseSet;
223f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
224f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
225f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void invalidate() {
226f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mCacheRef = null;
227f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
228f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
229f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public MediaItem getItem(int index) {
230f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            boolean needLoading = false;
231f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ArrayList<MediaItem> cache = null;
232f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mCacheRef == null
233f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    || index < mStartPos || index >= mStartPos + PAGE_SIZE) {
234f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                needLoading = true;
235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } else {
236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                cache = mCacheRef.get();
237f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (cache == null) {
238f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    needLoading = true;
239f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
240f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
241f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
242f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (needLoading) {
243f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                cache = mBaseSet.getMediaItem(index, PAGE_SIZE);
244f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mCacheRef = new SoftReference<ArrayList<MediaItem>>(cache);
245f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mStartPos = index;
246f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
247f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
248f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (index < mStartPos || index >= mStartPos + cache.size()) {
249f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                return null;
250f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
251f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
252f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return cache.get(index - mStartPos);
253f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
254f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
255f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
256f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @Override
257f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public boolean isLeafAlbum() {
258f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return true;
259f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
260f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
261