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.app;
18f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
19dbf4ba12afba115888493caa34937643257e2bb1Ray Chenimport android.os.Handler;
20dbf4ba12afba115888493caa34937643257e2bb1Ray Chenimport android.os.Message;
21d8fb81f601830385a2343d08ad5dd171e4c7bfe0Owen Linimport android.os.Process;
22dbf4ba12afba115888493caa34937643257e2bb1Ray Chen
23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.common.Utils;
24f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.data.ContentListener;
25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.data.MediaItem;
26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.data.MediaObject;
27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.data.MediaSet;
28995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolbimport com.android.gallery3d.data.Path;
29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.ui.SynchronizedHandler;
30f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
31f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.Arrays;
32f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.concurrent.Callable;
33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.concurrent.ExecutionException;
34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.concurrent.FutureTask;
35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
36c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Linpublic class AlbumSetDataLoader {
37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @SuppressWarnings("unused")
38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "AlbumSetDataAdapter";
39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int INDEX_NONE = -1;
41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int MIN_LOAD_COUNT = 4;
43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int MSG_LOAD_START = 1;
45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int MSG_LOAD_FINISH = 2;
46f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int MSG_RUN_OBJECT = 3;
47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
48c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin    public static interface DataListener {
49c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin        public void onContentChanged(int index);
50c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin        public void onSizeChanged(int size);
51c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin    }
52c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin
53f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final MediaSet[] mData;
541a4bd273afe5dd11592f7625c2f19853b6f174e9Owen Lin    private final MediaItem[] mCoverItem;
558ef6c55bdad9a3e835ce56bdc98681434b4ac5b3Owen Lin    private final int[] mTotalCount;
56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final long[] mItemVersion;
57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final long[] mSetVersion;
58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mActiveStart = 0;
60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mActiveEnd = 0;
61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mContentStart = 0;
63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mContentEnd = 0;
64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final MediaSet mSource;
66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private long mSourceVersion = MediaObject.INVALID_DATA_VERSION;
67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mSize;
68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
69c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin    private DataListener mDataListener;
70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private LoadingListener mLoadingListener;
71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private ReloadTask mReloadTask;
72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final Handler mMainHandler;
74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
75dbf4ba12afba115888493caa34937643257e2bb1Ray Chen    private final MySourceListener mSourceListener = new MySourceListener();
76f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
77b21b8e58a604f6c701245d84b141b5b87663192bOwen Lin    public AlbumSetDataLoader(AbstractGalleryActivity activity, MediaSet albumSet, int cacheSize) {
78f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSource = Utils.checkNotNull(albumSet);
791a4bd273afe5dd11592f7625c2f19853b6f174e9Owen Lin        mCoverItem = new MediaItem[cacheSize];
80f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mData = new MediaSet[cacheSize];
818ef6c55bdad9a3e835ce56bdc98681434b4ac5b3Owen Lin        mTotalCount = new int[cacheSize];
82f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mItemVersion = new long[cacheSize];
83f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSetVersion = new long[cacheSize];
84f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Arrays.fill(mItemVersion, MediaObject.INVALID_DATA_VERSION);
85f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Arrays.fill(mSetVersion, MediaObject.INVALID_DATA_VERSION);
86f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mMainHandler = new SynchronizedHandler(activity.getGLRoot()) {
88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            @Override
89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            public void handleMessage(Message message) {
90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                switch (message.what) {
91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    case MSG_RUN_OBJECT:
92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        ((Runnable) message.obj).run();
93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        return;
94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    case MSG_LOAD_START:
95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        if (mLoadingListener != null) mLoadingListener.onLoadingStarted();
96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        return;
97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    case MSG_LOAD_FINISH:
9805da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                        if (mLoadingListener != null) mLoadingListener.onLoadingFinished(false);
99f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        return;
100f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
101f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        };
103f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void pause() {
106f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mReloadTask.terminate();
107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mReloadTask = null;
108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSource.removeContentListener(mSourceListener);
109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void resume() {
112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSource.addContentListener(mSourceListener);
113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mReloadTask = new ReloadTask();
114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mReloadTask.start();
115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1178ef6c55bdad9a3e835ce56bdc98681434b4ac5b3Owen Lin    private void assertIsActive(int index) {
118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (index < mActiveStart && index >= mActiveEnd) {
119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            throw new IllegalArgumentException(String.format(
120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    "%s not in (%s, %s)", index, mActiveStart, mActiveEnd));
121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1228ef6c55bdad9a3e835ce56bdc98681434b4ac5b3Owen Lin    }
1238ef6c55bdad9a3e835ce56bdc98681434b4ac5b3Owen Lin
1248ef6c55bdad9a3e835ce56bdc98681434b4ac5b3Owen Lin    public MediaSet getMediaSet(int index) {
1258ef6c55bdad9a3e835ce56bdc98681434b4ac5b3Owen Lin        assertIsActive(index);
126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mData[index % mData.length];
127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1291a4bd273afe5dd11592f7625c2f19853b6f174e9Owen Lin    public MediaItem getCoverItem(int index) {
1308ef6c55bdad9a3e835ce56bdc98681434b4ac5b3Owen Lin        assertIsActive(index);
1311a4bd273afe5dd11592f7625c2f19853b6f174e9Owen Lin        return mCoverItem[index % mCoverItem.length];
132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1348ef6c55bdad9a3e835ce56bdc98681434b4ac5b3Owen Lin    public int getTotalCount(int index) {
1358ef6c55bdad9a3e835ce56bdc98681434b4ac5b3Owen Lin        assertIsActive(index);
1368ef6c55bdad9a3e835ce56bdc98681434b4ac5b3Owen Lin        return mTotalCount[index % mTotalCount.length];
1378ef6c55bdad9a3e835ce56bdc98681434b4ac5b3Owen Lin    }
1388ef6c55bdad9a3e835ce56bdc98681434b4ac5b3Owen Lin
139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getActiveStart() {
140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mActiveStart;
141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public boolean isActive(int index) {
144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return index >= mActiveStart && index < mActiveEnd;
145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
147f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int size() {
148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mSize;
149f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
151995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb    // Returns the index of the MediaSet with the given path or
152995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb    // -1 if the path is not cached
153995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb    public int findSet(Path id) {
154995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb        int length = mData.length;
155995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb        for (int i = mContentStart; i < mContentEnd; i++) {
156995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb            MediaSet set = mData[i % length];
157995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb            if (set != null && id == set.getPath()) {
158995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb                return i;
159995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb            }
160995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb        }
161995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb        return -1;
162995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb    }
163995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb
164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void clearSlot(int slotIndex) {
165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mData[slotIndex] = null;
1661a4bd273afe5dd11592f7625c2f19853b6f174e9Owen Lin        mCoverItem[slotIndex] = null;
1678ef6c55bdad9a3e835ce56bdc98681434b4ac5b3Owen Lin        mTotalCount[slotIndex] = 0;
168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mItemVersion[slotIndex] = MediaObject.INVALID_DATA_VERSION;
169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSetVersion[slotIndex] = MediaObject.INVALID_DATA_VERSION;
170f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
172f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void setContentWindow(int contentStart, int contentEnd) {
173f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (contentStart == mContentStart && contentEnd == mContentEnd) return;
1741a4bd273afe5dd11592f7625c2f19853b6f174e9Owen Lin        int length = mCoverItem.length;
175f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
176f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int start = this.mContentStart;
177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int end = this.mContentEnd;
178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
179f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mContentStart = contentStart;
180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mContentEnd = contentEnd;
181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (contentStart >= end || start >= contentEnd) {
183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = start, n = end; i < n; ++i) {
184f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                clearSlot(i % length);
185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else {
187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = start; i < contentStart; ++i) {
188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                clearSlot(i % length);
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = contentEnd, n = end; i < n; ++i) {
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                clearSlot(i % length);
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mReloadTask.notifyDirty();
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void setActiveWindow(int start, int end) {
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (start == mActiveStart && end == mActiveEnd) return;
199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Utils.assertTrue(start <= end
2011a4bd273afe5dd11592f7625c2f19853b6f174e9Owen Lin                && end - start <= mCoverItem.length && end <= mSize);
202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mActiveStart = start;
204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mActiveEnd = end;
205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
2061a4bd273afe5dd11592f7625c2f19853b6f174e9Owen Lin        int length = mCoverItem.length;
207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // If no data is visible, keep the cache content
208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (start == end) return;
209f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
210f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int contentStart = Utils.clamp((start + end) / 2 - length / 2,
211f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                0, Math.max(0, mSize - length));
212f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int contentEnd = Math.min(contentStart + length, mSize);
213f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mContentStart > start || mContentEnd < end
214f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                || Math.abs(contentStart - mContentStart) > MIN_LOAD_COUNT) {
215f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            setContentWindow(contentStart, contentEnd);
216f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
217f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
218f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
219f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class MySourceListener implements ContentListener {
2207817979db0c52ffeacb951625b1e821eba303285Ahbong Chang        @Override
221f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onContentDirty() {
222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mReloadTask.notifyDirty();
223f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
224f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
225f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
226c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin    public void setModelListener(DataListener listener) {
227c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin        mDataListener = listener;
228f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
229f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
230f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void setLoadingListener(LoadingListener listener) {
231f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mLoadingListener = listener;
232f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
233f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
234f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static class UpdateInfo {
235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public long version;
236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public int index;
237f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
238f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public int size;
239f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public MediaSet item;
2401a4bd273afe5dd11592f7625c2f19853b6f174e9Owen Lin        public MediaItem cover;
2418ef6c55bdad9a3e835ce56bdc98681434b4ac5b3Owen Lin        public int totalCount;
242f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
243f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
244f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class GetUpdateInfo implements Callable<UpdateInfo> {
245f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
246f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private final long mVersion;
247f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
248f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public GetUpdateInfo(long version) {
249f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mVersion = version;
250f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
251f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
252f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private int getInvalidIndex(long version) {
253f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            long setVersion[] = mSetVersion;
254f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int length = setVersion.length;
255f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = mContentStart, n = mContentEnd; i < n; ++i) {
256f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                int index = i % length;
257f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (setVersion[i % length] != version) return i;
258f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
259f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return INDEX_NONE;
260f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
261f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
262f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
263f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public UpdateInfo call() throws Exception {
264f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int index = getInvalidIndex(mVersion);
265dbf4ba12afba115888493caa34937643257e2bb1Ray Chen            if (index == INDEX_NONE && mSourceVersion == mVersion) return null;
266f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            UpdateInfo info = new UpdateInfo();
267f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            info.version = mSourceVersion;
268f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            info.index = index;
269f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            info.size = mSize;
270f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return info;
271f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
272f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
273f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
274f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class UpdateContent implements Callable<Void> {
275dbf4ba12afba115888493caa34937643257e2bb1Ray Chen        private final UpdateInfo mUpdateInfo;
276f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
277f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public UpdateContent(UpdateInfo info) {
278f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mUpdateInfo = info;
279f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
280f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
2817817979db0c52ffeacb951625b1e821eba303285Ahbong Chang        @Override
282f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public Void call() {
283dbf4ba12afba115888493caa34937643257e2bb1Ray Chen            // Avoid notifying listeners of status change after pause
284dbf4ba12afba115888493caa34937643257e2bb1Ray Chen            // Otherwise gallery will be in inconsistent state after resume.
285dbf4ba12afba115888493caa34937643257e2bb1Ray Chen            if (mReloadTask == null) return null;
286f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            UpdateInfo info = mUpdateInfo;
287f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mSourceVersion = info.version;
288f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mSize != info.size) {
289f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mSize = info.size;
290c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin                if (mDataListener != null) mDataListener.onSizeChanged(mSize);
291f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mContentEnd > mSize) mContentEnd = mSize;
292f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mActiveEnd > mSize) mActiveEnd = mSize;
293f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
294f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // Note: info.index could be INDEX_NONE, i.e., -1
295f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (info.index >= mContentStart && info.index < mContentEnd) {
2961a4bd273afe5dd11592f7625c2f19853b6f174e9Owen Lin                int pos = info.index % mCoverItem.length;
297f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mSetVersion[pos] = info.version;
298f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                long itemVersion = info.item.getDataVersion();
299f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mItemVersion[pos] == itemVersion) return null;
300f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mItemVersion[pos] = itemVersion;
301f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mData[pos] = info.item;
3021a4bd273afe5dd11592f7625c2f19853b6f174e9Owen Lin                mCoverItem[pos] = info.cover;
3038ef6c55bdad9a3e835ce56bdc98681434b4ac5b3Owen Lin                mTotalCount[pos] = info.totalCount;
304c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin                if (mDataListener != null
305f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        && info.index >= mActiveStart && info.index < mActiveEnd) {
306c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin                    mDataListener.onContentChanged(info.index);
307f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
308f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
309f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return null;
310f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
311f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
312f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
313f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private <T> T executeAndWait(Callable<T> callable) {
314f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        FutureTask<T> task = new FutureTask<T>(callable);
315f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mMainHandler.sendMessage(
316f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mMainHandler.obtainMessage(MSG_RUN_OBJECT, task));
317f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        try {
318f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return task.get();
319f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } catch (InterruptedException e) {
320f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return null;
321f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } catch (ExecutionException e) {
322f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            throw new RuntimeException(e);
323f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
324f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
325f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
326f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // TODO: load active range first
327f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class ReloadTask extends Thread {
328f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private volatile boolean mActive = true;
329f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private volatile boolean mDirty = true;
330f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private volatile boolean mIsLoading = false;
331f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
332f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private void updateLoading(boolean loading) {
333f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mIsLoading == loading) return;
334f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mIsLoading = loading;
335f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mMainHandler.sendEmptyMessage(loading ? MSG_LOAD_START : MSG_LOAD_FINISH);
336f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
337f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
338f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
339f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void run() {
340d8fb81f601830385a2343d08ad5dd171e4c7bfe0Owen Lin            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
341d8fb81f601830385a2343d08ad5dd171e4c7bfe0Owen Lin
342f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            boolean updateComplete = false;
343f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            while (mActive) {
344f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                synchronized (this) {
345f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    if (mActive && !mDirty && updateComplete) {
346705dc819661cb8c8e839a04577b0641bd6caad53John Reck                        if (!mSource.isLoading()) updateLoading(false);
347f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        Utils.waitWithoutInterrupt(this);
348f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        continue;
349f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
350f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
351f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mDirty = false;
352f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                updateLoading(true);
353f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
354705dc819661cb8c8e839a04577b0641bd6caad53John Reck                long version = mSource.reload();
355f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                UpdateInfo info = executeAndWait(new GetUpdateInfo(version));
356f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                updateComplete = info == null;
357f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (updateComplete) continue;
358676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                if (info.version != version) {
359676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    info.version = version;
360676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    info.size = mSource.getSubMediaSetCount();
361676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin
362676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    // If the size becomes smaller after reload(), we may
363676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    // receive from GetUpdateInfo an index which is too
364676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    // big. Because the main thread is not aware of the size
365676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    // change until we call UpdateContent.
366676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    if (info.index >= info.size) {
367676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                        info.index = INDEX_NONE;
368f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
369f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
370676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                if (info.index != INDEX_NONE) {
371676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    info.item = mSource.getSubMediaSet(info.index);
372676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    if (info.item == null) continue;
373676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    info.cover = info.item.getCoverMediaItem();
374676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    info.totalCount = info.item.getTotalMediaItemCount();
375676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                }
376f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                executeAndWait(new UpdateContent(info));
377f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
378f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateLoading(false);
379f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
380f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
381f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public synchronized void notifyDirty() {
382f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mDirty = true;
383f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            notifyAll();
384f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
385f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
386f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public synchronized void terminate() {
387f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mActive = false;
388f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            notifyAll();
389f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
390f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
391f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
392f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
393f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
394