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
19fe49be45412f8d0f68343662318e73822da486c8Owen Linimport android.os.Handler;
20fe49be45412f8d0f68343662318e73822da486c8Owen Linimport android.os.Message;
21d8fb81f601830385a2343d08ad5dd171e4c7bfe0Owen Linimport android.os.Process;
22fe49be45412f8d0f68343662318e73822da486c8Owen Lin
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.ArrayList;
32f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.Arrays;
33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.concurrent.Callable;
34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.concurrent.ExecutionException;
35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.concurrent.FutureTask;
36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
37c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Linpublic class AlbumDataLoader {
38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @SuppressWarnings("unused")
39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "AlbumDataAdapter";
40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int DATA_CACHE_SIZE = 1000;
41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int MSG_LOAD_START = 1;
43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int MSG_LOAD_FINISH = 2;
44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int MSG_RUN_OBJECT = 3;
45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
46f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int MIN_LOAD_COUNT = 32;
47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int MAX_LOAD_COUNT = 64;
48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final MediaItem[] mData;
50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final long[] mItemVersion;
51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final long[] mSetVersion;
52f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
53c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin    public static interface DataListener {
54c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin        public void onContentChanged(int index);
55c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin        public void onSizeChanged(int size);
56c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin    }
57c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin
58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mActiveStart = 0;
59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mActiveEnd = 0;
60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mContentStart = 0;
62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mContentEnd = 0;
63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final MediaSet mSource;
65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private long mSourceVersion = MediaObject.INVALID_DATA_VERSION;
66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final Handler mMainHandler;
68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mSize = 0;
69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
70c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin    private DataListener mDataListener;
71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private MySourceListener mSourceListener = new MySourceListener();
72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private LoadingListener mLoadingListener;
73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private ReloadTask mReloadTask;
7505da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan    // the data version on which last loading failed
7605da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan    private long mFailedVersion = MediaObject.INVALID_DATA_VERSION;
77f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
78b21b8e58a604f6c701245d84b141b5b87663192bOwen Lin    public AlbumDataLoader(AbstractGalleryActivity context, MediaSet mediaSet) {
79f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSource = mediaSet;
80f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
81f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mData = new MediaItem[DATA_CACHE_SIZE];
82f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mItemVersion = new long[DATA_CACHE_SIZE];
83f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSetVersion = new long[DATA_CACHE_SIZE];
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(context.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) {
9905da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                            boolean loadingFailed =
10005da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                                    (mFailedVersion != MediaObject.INVALID_DATA_VERSION);
10105da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                            mLoadingListener.onLoadingFinished(loadingFailed);
10205da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                        }
103f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        return;
104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
106f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        };
107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void resume() {
110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSource.addContentListener(mSourceListener);
111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mReloadTask = new ReloadTask();
112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mReloadTask.start();
113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void pause() {
116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mReloadTask.terminate();
117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mReloadTask = null;
118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSource.removeContentListener(mSourceListener);
119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public MediaItem get(int index) {
122f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (!isActive(index)) {
123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            throw new IllegalArgumentException(String.format(
124f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    "%s not in (%s, %s)", index, mActiveStart, mActiveEnd));
125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mData[index % mData.length];
127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
129f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getActiveStart() {
130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mActiveStart;
131f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public boolean isActive(int index) {
134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return index >= mActiveStart && index < mActiveEnd;
135f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int size() {
138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mSize;
139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
141995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb    // Returns the index of the MediaItem with the given path or
142995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb    // -1 if the path is not cached
143995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb    public int findItem(Path id) {
144995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb        for (int i = mContentStart; i < mContentEnd; i++) {
145995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb            MediaItem item = mData[i % DATA_CACHE_SIZE];
146995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb            if (item != null && id == item.getPath()) {
147995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb                return i;
148995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb            }
149995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb        }
150995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb        return -1;
151995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb    }
152995c4566bed7df1aa48ba7e1351f964efa73880bMichael Kolb
153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void clearSlot(int slotIndex) {
154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mData[slotIndex] = null;
155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mItemVersion[slotIndex] = MediaObject.INVALID_DATA_VERSION;
156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSetVersion[slotIndex] = MediaObject.INVALID_DATA_VERSION;
157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void setContentWindow(int contentStart, int contentEnd) {
160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (contentStart == mContentStart && contentEnd == mContentEnd) return;
161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int end = mContentEnd;
162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int start = mContentStart;
163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // We need change the content window before calling reloadData(...)
165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        synchronized (this) {
166f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mContentStart = contentStart;
167f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mContentEnd = contentEnd;
168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        long[] itemVersion = mItemVersion;
170f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        long[] setVersion = mSetVersion;
171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (contentStart >= end || start >= contentEnd) {
172f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = start, n = end; i < n; ++i) {
173f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                clearSlot(i % DATA_CACHE_SIZE);
174f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
175f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else {
176f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = start; i < contentStart; ++i) {
177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                clearSlot(i % DATA_CACHE_SIZE);
178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
179f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = contentEnd, n = end; i < n; ++i) {
180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                clearSlot(i % DATA_CACHE_SIZE);
181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mReloadTask != null) mReloadTask.notifyDirty();
184f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void setActiveWindow(int start, int end) {
187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (start == mActiveStart && end == mActiveEnd) return;
188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Utils.assertTrue(start <= end
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                && end - start <= mData.length && end <= mSize);
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int length = mData.length;
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mActiveStart = start;
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mActiveEnd = end;
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // If no data is visible, keep the cache content
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (start == end) return;
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int contentStart = Utils.clamp((start + end) / 2 - length / 2,
200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                0, Math.max(0, mSize - length));
201f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int contentEnd = Math.min(contentStart + length, mSize);
202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mContentStart > start || mContentEnd < end
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                || Math.abs(contentStart - mContentStart) > MIN_LOAD_COUNT) {
204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            setContentWindow(contentStart, contentEnd);
205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
206f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class MySourceListener implements ContentListener {
2097817979db0c52ffeacb951625b1e821eba303285Ahbong Chang        @Override
210f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onContentDirty() {
211f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mReloadTask != null) mReloadTask.notifyDirty();
212f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
213f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
214f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
215c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin    public void setDataListener(DataListener listener) {
216c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin        mDataListener = listener;
217f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
218f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
219f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void setLoadingListener(LoadingListener listener) {
220f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mLoadingListener = listener;
221f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
223f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private <T> T executeAndWait(Callable<T> callable) {
224f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        FutureTask<T> task = new FutureTask<T>(callable);
225f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mMainHandler.sendMessage(
226f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mMainHandler.obtainMessage(MSG_RUN_OBJECT, task));
227f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        try {
228f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return task.get();
229f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } catch (InterruptedException e) {
230f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return null;
231f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } catch (ExecutionException e) {
232f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            throw new RuntimeException(e);
233f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
234f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static class UpdateInfo {
237f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public long version;
238f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public int reloadStart;
239f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public int reloadCount;
240f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
241f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public int size;
242f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public ArrayList<MediaItem> items;
243f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
244f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
245f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class GetUpdateInfo implements Callable<UpdateInfo> {
246f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private final long mVersion;
247f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
248f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public GetUpdateInfo(long version) {
249f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mVersion = version;
250f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
251f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
2527817979db0c52ffeacb951625b1e821eba303285Ahbong Chang        @Override
253f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public UpdateInfo call() throws Exception {
25405da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan            if (mFailedVersion == mVersion) {
25505da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                // previous loading failed, return null to pause loading
25605da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                return null;
25705da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan            }
258f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            UpdateInfo info = new UpdateInfo();
259f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            long version = mVersion;
260f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            info.version = mSourceVersion;
261f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            info.size = mSize;
262f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            long setVersion[] = mSetVersion;
263f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = mContentStart, n = mContentEnd; i < n; ++i) {
264f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                int index = i % DATA_CACHE_SIZE;
265f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (setVersion[index] != version) {
266f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    info.reloadStart = i;
267f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    info.reloadCount = Math.min(MAX_LOAD_COUNT, n - i);
268f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    return info;
269f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
270f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
271f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return mSourceVersion == mVersion ? null : info;
272f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
273f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
274f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
275f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class UpdateContent implements Callable<Void> {
276f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
277f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private UpdateInfo mUpdateInfo;
278f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
279f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public UpdateContent(UpdateInfo info) {
280f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mUpdateInfo = info;
281f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
282f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
283f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
284f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public Void call() throws Exception {
285f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            UpdateInfo info = mUpdateInfo;
286f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mSourceVersion = info.version;
287f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mSize != info.size) {
288f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mSize = info.size;
289c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin                if (mDataListener != null) mDataListener.onSizeChanged(mSize);
290f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mContentEnd > mSize) mContentEnd = mSize;
291f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mActiveEnd > mSize) mActiveEnd = mSize;
292f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
293f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
294f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ArrayList<MediaItem> items = info.items;
295f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
29605da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan            mFailedVersion = MediaObject.INVALID_DATA_VERSION;
29705da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan            if ((items == null) || items.isEmpty()) {
29805da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                if (info.reloadCount > 0) {
29905da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                    mFailedVersion = info.version;
30005da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                    Log.d(TAG, "loading failed: " + mFailedVersion);
30105da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                }
30205da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                return null;
30305da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan            }
304f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int start = Math.max(info.reloadStart, mContentStart);
305f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int end = Math.min(info.reloadStart + items.size(), mContentEnd);
306f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
307f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = start; i < end; ++i) {
308f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                int index = i % DATA_CACHE_SIZE;
309f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mSetVersion[index] = info.version;
310f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                MediaItem updateItem = items.get(i - info.reloadStart);
311f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                long itemVersion = updateItem.getDataVersion();
312f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mItemVersion[index] != itemVersion) {
313f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mItemVersion[index] = itemVersion;
314f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mData[index] = updateItem;
315c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin                    if (mDataListener != null && i >= mActiveStart && i < mActiveEnd) {
316c3d8ac3b9504346dafc49e006b5f732dd1db21e8Owen Lin                        mDataListener.onContentChanged(i);
317f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
318f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
319f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
320f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return null;
321f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
322f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
323f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
324f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    /*
325f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * The thread model of ReloadTask
326f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *      *
327f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * [Reload Task]       [Main Thread]
328f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *       |                   |
329f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * getUpdateInfo() -->       |           (synchronous call)
330f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *     (wait) <----    getUpdateInfo()
331f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *       |                   |
332f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *   Load Data               |
333f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *       |                   |
334f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     * updateContent() -->       |           (synchronous call)
335f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *     (wait)          updateContent()
336f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *       |                   |
337f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     *       |                   |
338f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin     */
339f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class ReloadTask extends Thread {
340f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
341f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private volatile boolean mActive = true;
342f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private volatile boolean mDirty = true;
343f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private boolean mIsLoading = false;
344f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
345f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private void updateLoading(boolean loading) {
346f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mIsLoading == loading) return;
347f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mIsLoading = loading;
348f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mMainHandler.sendEmptyMessage(loading ? MSG_LOAD_START : MSG_LOAD_FINISH);
349f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
350f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
351f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
352f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void run() {
353d8fb81f601830385a2343d08ad5dd171e4c7bfe0Owen Lin            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
354d8fb81f601830385a2343d08ad5dd171e4c7bfe0Owen Lin
355f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            boolean updateComplete = false;
356f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            while (mActive) {
357f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                synchronized (this) {
358f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    if (mActive && !mDirty && updateComplete) {
359f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        updateLoading(false);
36005da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                        if (mFailedVersion != MediaObject.INVALID_DATA_VERSION) {
36105da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                            Log.d(TAG, "reload pause");
36205da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                        }
363f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        Utils.waitWithoutInterrupt(this);
36405da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                        if (mActive && (mFailedVersion != MediaObject.INVALID_DATA_VERSION)) {
36505da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                            Log.d(TAG, "reload resume");
36605da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                        }
367f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        continue;
368f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
36905da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                    mDirty = false;
370f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
371f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                updateLoading(true);
372676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                long version = mSource.reload();
373f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                UpdateInfo info = executeAndWait(new GetUpdateInfo(version));
374f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                updateComplete = info == null;
375f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (updateComplete) continue;
376676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                if (info.version != version) {
377676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    info.size = mSource.getMediaItemCount();
378676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    info.version = version;
379676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                }
380676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                if (info.reloadCount > 0) {
381676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    info.items = mSource.getMediaItem(info.reloadStart, info.reloadCount);
382f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
383f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                executeAndWait(new UpdateContent(info));
384f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
385f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateLoading(false);
386f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
387f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
388f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public synchronized void notifyDirty() {
389f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mDirty = true;
390f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            notifyAll();
391f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
392f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
393f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public synchronized void terminate() {
394f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mActive = false;
395f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            notifyAll();
396f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
397f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
398f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
399