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
192b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport android.graphics.Bitmap;
202b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport android.graphics.BitmapRegionDecoder;
212b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport android.os.Handler;
222b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Linimport android.os.Message;
232b3ee0ea07246b859a5b75d8a6102a7cce7ec838Owen Lin
2422493b29c32142f2588c623f5c78e9167352dddcOwen Linimport com.android.gallery3d.common.BitmapUtils;
25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.common.Utils;
26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.data.ContentListener;
27f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Changimport com.android.gallery3d.data.LocalMediaItem;
28f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.data.MediaItem;
29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.data.MediaObject;
30f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.data.MediaSet;
31f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.data.Path;
32a4eae1abb4f2547dfbda84301ee764ce35464881John Reckimport com.android.gallery3d.glrenderer.TiledTexture;
33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.ui.PhotoView;
3415b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Changimport com.android.gallery3d.ui.ScreenNail;
35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.ui.SynchronizedHandler;
36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.ui.TileImageViewAdapter;
37030f8dad6aefc42d0af39bc1b93f370937d3e2abOwen Linimport com.android.gallery3d.ui.TiledScreenNail;
38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.util.Future;
39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.util.FutureListener;
40f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Changimport com.android.gallery3d.util.MediaSetUtils;
41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.util.ThreadPool;
4222493b29c32142f2588c623f5c78e9167352dddcOwen Linimport com.android.gallery3d.util.ThreadPool.Job;
4322493b29c32142f2588c623f5c78e9167352dddcOwen Linimport com.android.gallery3d.util.ThreadPool.JobContext;
44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.ArrayList;
46f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.Arrays;
47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.HashMap;
48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.HashSet;
49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.concurrent.Callable;
50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.concurrent.ExecutionException;
51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.concurrent.FutureTask;
52f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
53f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic class PhotoDataAdapter implements PhotoPage.Model {
54f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @SuppressWarnings("unused")
55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "PhotoDataAdapter";
56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int MSG_LOAD_START = 1;
58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int MSG_LOAD_FINISH = 2;
59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int MSG_RUN_OBJECT = 3;
60b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang    private static final int MSG_UPDATE_IMAGE_REQUESTS = 4;
61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
62a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static final int MIN_LOAD_COUNT = 16;
63a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private static final int DATA_CACHE_SIZE = 256;
64b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private static final int SCREEN_NAIL_MAX = PhotoView.SCREEN_NAIL_MAX;
65b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private static final int IMAGE_CACHE_SIZE = 2 * SCREEN_NAIL_MAX + 1;
66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int BIT_SCREEN_NAIL = 1;
68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int BIT_FULL_IMAGE = 2;
69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // sImageFetchSeq is the fetching sequence for images.
71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // We want to fetch the current screennail first (offset = 0), the next
72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // screennail (offset = +1), then the previous screennail (offset = -1) etc.
73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // After all the screennail are fetched, we fetch the full images (only some
74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // of them because of we don't want to use too much memory).
75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static ImageFetch[] sImageFetchSeq;
76f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
77f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static class ImageFetch {
78f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int indexOffset;
79f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int imageBit;
80f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public ImageFetch(int offset, int bit) {
81f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            indexOffset = offset;
82f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            imageBit = bit;
83f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
84f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
85f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
86f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    static {
87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int k = 0;
88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        sImageFetchSeq = new ImageFetch[1 + (IMAGE_CACHE_SIZE - 1) * 2 + 3];
89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        sImageFetchSeq[k++] = new ImageFetch(0, BIT_SCREEN_NAIL);
90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 1; i < IMAGE_CACHE_SIZE; ++i) {
92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            sImageFetchSeq[k++] = new ImageFetch(i, BIT_SCREEN_NAIL);
93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            sImageFetchSeq[k++] = new ImageFetch(-i, BIT_SCREEN_NAIL);
94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        sImageFetchSeq[k++] = new ImageFetch(0, BIT_FULL_IMAGE);
97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        sImageFetchSeq[k++] = new ImageFetch(1, BIT_FULL_IMAGE);
98f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        sImageFetchSeq[k++] = new ImageFetch(-1, BIT_FULL_IMAGE);
99f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
100f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
101f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final TileImageViewAdapter mTileProvider = new TileImageViewAdapter();
102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
103f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // PhotoDataAdapter caches MediaItems (data) and ImageEntries (image).
104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    //
105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // The MediaItems are stored in the mData array, which has DATA_CACHE_SIZE
106f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // entries. The valid index range are [mContentStart, mContentEnd). We keep
107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // mContentEnd - mContentStart <= DATA_CACHE_SIZE, so we can use
108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // (i % DATA_CACHE_SIZE) as index to the array.
109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    //
110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // The valid MediaItem window size (mContentEnd - mContentStart) may be
111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // smaller than DATA_CACHE_SIZE because we only update the window and reload
112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // the MediaItems when there are significant changes to the window position
113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // (>= MIN_LOAD_COUNT).
114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final MediaItem mData[] = new MediaItem[DATA_CACHE_SIZE];
115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mContentStart = 0;
116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mContentEnd = 0;
117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
118f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // The ImageCache is a Path-to-ImageEntry map. It only holds the
119f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // ImageEntries in the range of [mActiveStart, mActiveEnd).  We also keep
120f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // mActiveEnd - mActiveStart <= IMAGE_CACHE_SIZE.  Besides, the
121f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // [mActiveStart, mActiveEnd) range must be contained within
122f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // the [mContentStart, mContentEnd) range.
123f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    private HashMap<Path, ImageEntry> mImageCache =
124f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            new HashMap<Path, ImageEntry>();
125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mActiveStart = 0;
126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mActiveEnd = 0;
127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // mCurrentIndex is the "center" image the user is viewing. The change of
129f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // mCurrentIndex triggers the data loading and image loading.
130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mCurrentIndex;
131f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
132214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang    // mChanges keeps the version number (of MediaItem) about the images. If any
133214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang    // of the version number changes, we notify the view. This is used after a
134214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang    // database reload or mCurrentIndex changes.
135b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private final long mChanges[] = new long[IMAGE_CACHE_SIZE];
136214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang    // mPaths keeps the corresponding Path (of MediaItem) for the images. This
137214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang    // is used to determine the item movement.
138214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang    private final Path mPaths[] = new Path[IMAGE_CACHE_SIZE];
139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final Handler mMainHandler;
141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final ThreadPool mThreadPool;
142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final PhotoView mPhotoView;
144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final MediaSet mSource;
145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private ReloadTask mReloadTask;
146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
147f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private long mSourceVersion = MediaObject.INVALID_DATA_VERSION;
148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mSize = 0;
149f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Path mItemPath;
150bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    private int mCameraIndex;
15143a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong    private boolean mIsPanorama;
152dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li    private boolean mIsStaticCamera;
153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private boolean mIsActive;
154b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang    private boolean mNeedFullImage;
1556b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private int mFocusHintDirection = FOCUS_HINT_NEXT;
1566b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    private Path mFocusHintPath = null;
157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public interface DataListener extends LoadingListener {
159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onPhotoChanged(int index, Path item);
160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private DataListener mDataListener;
163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final SourceListener mSourceListener = new SourceListener();
165a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private final TiledTexture.Uploader mUploader;
166f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
167f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // The path of the current viewing item will be stored in mItemPath.
168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // If mItemPath is not null, mCurrentIndex is only a hint for where we
169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // can find the item. If mItemPath is null, then we use the mCurrentIndex to
170214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang    // find the image being viewed. cameraIndex is the index of the camera
171214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang    // preview. If cameraIndex < 0, there is no camera preview.
172b21b8e58a604f6c701245d84b141b5b87663192bOwen Lin    public PhotoDataAdapter(AbstractGalleryActivity activity, PhotoView view,
17343a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong            MediaSet mediaSet, Path itemPath, int indexHint, int cameraIndex,
174dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li            boolean isPanorama, boolean isStaticCamera) {
175f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSource = Utils.checkNotNull(mediaSet);
176f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mPhotoView = Utils.checkNotNull(view);
177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mItemPath = Utils.checkNotNull(itemPath);
178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mCurrentIndex = indexHint;
179bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        mCameraIndex = cameraIndex;
18043a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong        mIsPanorama = isPanorama;
181dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li        mIsStaticCamera = isStaticCamera;
182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mThreadPool = activity.getThreadPool();
183b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang        mNeedFullImage = true;
184f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Arrays.fill(mChanges, MediaObject.INVALID_DATA_VERSION);
186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
187a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        mUploader = new TiledTexture.Uploader(activity.getGLRoot());
188a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mMainHandler = new SynchronizedHandler(activity.getGLRoot()) {
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            @SuppressWarnings("unchecked")
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            @Override
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            public void handleMessage(Message message) {
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                switch (message.what) {
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    case MSG_RUN_OBJECT:
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        ((Runnable) message.obj).run();
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        return;
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    case MSG_LOAD_START: {
198f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                        if (mDataListener != null) {
199f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                            mDataListener.onLoadingStarted();
200f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                        }
201f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        return;
202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    case MSG_LOAD_FINISH: {
204f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                        if (mDataListener != null) {
20505da3f520eacb9219964b6ed57ef37846d889fd7Hung-ying Tyan                            mDataListener.onLoadingFinished(false);
206f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                        }
207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        return;
208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
209b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang                    case MSG_UPDATE_IMAGE_REQUESTS: {
210b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang                        updateImageRequests();
211b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang                        return;
212b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang                    }
213f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    default: throw new AssertionError();
214f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
215f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
216f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        };
217f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
218f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        updateSlidingWindow();
219f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
220f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
221214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang    private MediaItem getItemInternal(int index) {
222214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        if (index < 0 || index >= mSize) return null;
223f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (index >= mContentStart && index < mContentEnd) {
224214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang            return mData[index % DATA_CACHE_SIZE];
225f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
226214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        return null;
227214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang    }
228214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang
229214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang    private long getVersion(int index) {
230214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        MediaItem item = getItemInternal(index);
231214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        if (item == null) return MediaObject.INVALID_DATA_VERSION;
232214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        return item.getDataVersion();
233214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang    }
234214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang
235214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang    private Path getPath(int index) {
236214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        MediaItem item = getItemInternal(index);
237214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        if (item == null) return null;
238214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        return item.getPath();
239f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
240f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
241b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang    private void fireDataChange() {
242214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        // First check if data actually changed.
243214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        boolean changed = false;
244b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; ++i) {
245214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang            long newVersion = getVersion(mCurrentIndex + i);
246214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang            if (mChanges[i + SCREEN_NAIL_MAX] != newVersion) {
247214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang                mChanges[i + SCREEN_NAIL_MAX] = newVersion;
248214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang                changed = true;
249214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang            }
250f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
251214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang
252214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        if (!changed) return;
253214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang
254214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        // Now calculate the fromIndex array. fromIndex represents the item
255214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        // movement. It records the index where the picture come from. The
256214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        // special value Integer.MAX_VALUE means it's a new picture.
257214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        final int N = IMAGE_CACHE_SIZE;
258214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        int fromIndex[] = new int[N];
259214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang
260214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        // Remember the old path array.
261214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        Path oldPaths[] = new Path[N];
262214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        System.arraycopy(mPaths, 0, oldPaths, 0, N);
263214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang
264214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        // Update the mPaths array.
265214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        for (int i = 0; i < N; ++i) {
266214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang            mPaths[i] = getPath(mCurrentIndex + i - SCREEN_NAIL_MAX);
267214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        }
268214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang
269214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        // Calculate the fromIndex array.
270214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        for (int i = 0; i < N; i++) {
271214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang            Path p = mPaths[i];
272214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang            if (p == null) {
273214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang                fromIndex[i] = Integer.MAX_VALUE;
274214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang                continue;
275214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang            }
276214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang
277214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang            // Try to find the same path in the old array
278214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang            int j;
279214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang            for (j = 0; j < N; j++) {
280214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang                if (oldPaths[j] == p) {
281214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang                    break;
282214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang                }
283214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang            }
284214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang            fromIndex[i] = (j < N) ? j - SCREEN_NAIL_MAX : Integer.MAX_VALUE;
285214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        }
286214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang
287214993dc4abf87c386123af50e3c34184ba11cb6Chih-Chung Chang        mPhotoView.notifyDataChange(fromIndex, -mCurrentIndex,
288c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang                mSize - 1 - mCurrentIndex);
289f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
290f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
291f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void setDataListener(DataListener listener) {
292f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mDataListener = listener;
293f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
294f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
295f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    private void updateScreenNail(Path path, Future<ScreenNail> future) {
296f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        ImageEntry entry = mImageCache.get(path);
29715b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang        ScreenNail screenNail = future.get();
29815b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang
299e7327d0566b182c522ca6dfa8e04c649d8f631a0Owen Lin        if (entry == null || entry.screenNailTask != future) {
300b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang            if (screenNail != null) screenNail.recycle();
301f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return;
302f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
303e7327d0566b182c522ca6dfa8e04c649d8f631a0Owen Lin
304f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        entry.screenNailTask = null;
305f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
306f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        // Combine the ScreenNails if we already have a BitmapScreenNail
307030f8dad6aefc42d0af39bc1b93f370937d3e2abOwen Lin        if (entry.screenNail instanceof TiledScreenNail) {
308030f8dad6aefc42d0af39bc1b93f370937d3e2abOwen Lin            TiledScreenNail original = (TiledScreenNail) entry.screenNail;
309f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            screenNail = original.combine(screenNail);
310f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        }
311f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
31215b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang        if (screenNail == null) {
313f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            entry.failToLoad = true;
314f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        } else {
315f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            entry.failToLoad = false;
316f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            entry.screenNail = screenNail;
317a7b78e224b1808895ea2c3d42ae385526dea12aaYuli Huang        }
31815b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang
319b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        for (int i = -SCREEN_NAIL_MAX; i <= SCREEN_NAIL_MAX; ++i) {
320f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            if (path == getPath(mCurrentIndex + i)) {
321a7b78e224b1808895ea2c3d42ae385526dea12aaYuli Huang                if (i == 0) updateTileProvider(entry);
322b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mPhotoView.notifyImageChange(i);
323b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                break;
324f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
325f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
326f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        updateImageRequests();
327a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        updateScreenNailUploadQueue();
328f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
329f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
330f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    private void updateFullImage(Path path, Future<BitmapRegionDecoder> future) {
331f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        ImageEntry entry = mImageCache.get(path);
332e7327d0566b182c522ca6dfa8e04c649d8f631a0Owen Lin        if (entry == null || entry.fullImageTask != future) {
333f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            BitmapRegionDecoder fullImage = future.get();
334f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (fullImage != null) fullImage.recycle();
335f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return;
336f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
337e7327d0566b182c522ca6dfa8e04c649d8f631a0Owen Lin
338f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        entry.fullImageTask = null;
339f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        entry.fullImage = future.get();
340f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (entry.fullImage != null) {
341f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            if (path == getPath(mCurrentIndex)) {
342f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                updateTileProvider(entry);
343b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang                mPhotoView.notifyImageChange(0);
344f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
345f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
346f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        updateImageRequests();
347f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
348f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
3497817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    @Override
350f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void resume() {
351f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mIsActive = true;
352a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        TiledTexture.prepareResources();
353a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
354f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSource.addContentListener(mSourceListener);
355f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        updateImageCache();
356f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        updateImageRequests();
357f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
358f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mReloadTask = new ReloadTask();
359f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mReloadTask.start();
360f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
361b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        fireDataChange();
362f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
363f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
3647817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    @Override
365f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void pause() {
366f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mIsActive = false;
367f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
368f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mReloadTask.terminate();
369f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mReloadTask = null;
370f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
371f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSource.removeContentListener(mSourceListener);
372f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
373f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (ImageEntry entry : mImageCache.values()) {
374f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (entry.fullImageTask != null) entry.fullImageTask.cancel();
375f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (entry.screenNailTask != null) entry.screenNailTask.cancel();
376b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang            if (entry.screenNail != null) entry.screenNail.recycle();
377f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
378f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mImageCache.clear();
379f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mTileProvider.clear();
380a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
381a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        mUploader.clear();
382a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        TiledTexture.freeResources();
383f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
384f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
385c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    private MediaItem getItem(int index) {
386c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        if (index < 0 || index >= mSize || !mIsActive) return null;
387c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        Utils.assertTrue(index >= mActiveStart && index < mActiveEnd);
388c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
389c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        if (index >= mContentStart && index < mContentEnd) {
390c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang            return mData[index % DATA_CACHE_SIZE];
391c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        }
392c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        return null;
393f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
394f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
395f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void updateCurrentIndex(int index) {
396bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        if (mCurrentIndex == index) return;
397f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mCurrentIndex = index;
398f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        updateSlidingWindow();
399f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
400f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        MediaItem item = mData[index % DATA_CACHE_SIZE];
401f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mItemPath = item == null ? null : item.getPath();
402f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
403f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        updateImageCache();
404f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        updateImageRequests();
405f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        updateTileProvider();
406f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
407f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mDataListener != null) {
408f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mDataListener.onPhotoChanged(index, mItemPath);
409f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
410b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang
411b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        fireDataChange();
412f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
413f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
414a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private void uploadScreenNail(int offset) {
415a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        int index = mCurrentIndex + offset;
416a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        if (index < mActiveStart || index >= mActiveEnd) return;
417a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
418a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        MediaItem item = getItem(index);
419a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        if (item == null) return;
420a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
421a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        ImageEntry e = mImageCache.get(item.getPath());
422a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        if (e == null) return;
423a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
424a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        ScreenNail s = e.screenNail;
425030f8dad6aefc42d0af39bc1b93f370937d3e2abOwen Lin        if (s instanceof TiledScreenNail) {
426030f8dad6aefc42d0af39bc1b93f370937d3e2abOwen Lin            TiledTexture t = ((TiledScreenNail) s).getTexture();
427a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            if (t != null && !t.isReady()) mUploader.addTexture(t);
428a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
429a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
430a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
431a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    private void updateScreenNailUploadQueue() {
432a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        mUploader.clear();
433a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        uploadScreenNail(0);
434a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        for (int i = 1; i < IMAGE_CACHE_SIZE; ++i) {
435a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            uploadScreenNail(i);
436a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin            uploadScreenNail(-i);
437a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin        }
438a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin    }
439a8f3473271cb4bfc5b47f520402bad7cddb5d3e8Owen Lin
440c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    @Override
441bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    public void moveTo(int index) {
442bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        updateCurrentIndex(index);
443160e6d776daab93610b3d12413ad9ff2dd867d8bChih-Chung Chang    }
444160e6d776daab93610b3d12413ad9ff2dd867d8bChih-Chung Chang
445160e6d776daab93610b3d12413ad9ff2dd867d8bChih-Chung Chang    @Override
446c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    public ScreenNail getScreenNail(int offset) {
447f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        int index = mCurrentIndex + offset;
448f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        if (index < 0 || index >= mSize || !mIsActive) return null;
449f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        Utils.assertTrue(index >= mActiveStart && index < mActiveEnd);
450f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
451f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        MediaItem item = getItem(index);
452f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        if (item == null) return null;
453f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
454f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        ImageEntry entry = mImageCache.get(item.getPath());
455f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        if (entry == null) return null;
456f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
4576b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // Create a default ScreenNail if the real one is not available yet,
4586b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        // except for camera that a black screen is better than a gray tile.
4596b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        if (entry.screenNail == null && !isCamera(offset)) {
46049affdc4e274098a34e4eb2dbe4a89a750f1ba7fOwen Lin            entry.screenNail = newPlaceholderScreenNail(item);
461f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            if (offset == 0) updateTileProvider(entry);
462f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        }
463f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
464f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        return entry.screenNail;
465c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    }
466c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
467c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    @Override
468c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    public void getImageSize(int offset, PhotoView.Size size) {
469c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        MediaItem item = getItem(mCurrentIndex + offset);
470c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        if (item == null) {
471c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang            size.width = 0;
472c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang            size.height = 0;
473c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        } else {
474c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang            size.width = item.getWidth();
475c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang            size.height = item.getHeight();
476c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        }
477c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    }
478c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
479c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    @Override
480c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    public int getImageRotation(int offset) {
481c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        MediaItem item = getItem(mCurrentIndex + offset);
482c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        return (item == null) ? 0 : item.getFullImageRotation();
483c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    }
484c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
485c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    @Override
486c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    public void setNeedFullImage(boolean enabled) {
487c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        mNeedFullImage = enabled;
488c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang        mMainHandler.sendEmptyMessage(MSG_UPDATE_IMAGE_REQUESTS);
489c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang    }
490c3b2d478f9032a8decf5c6254a238fc49e41b72cChih-Chung Chang
491bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    @Override
492bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    public boolean isCamera(int offset) {
493bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang        return mCurrentIndex + offset == mCameraIndex;
494bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang    }
495bd141b5a51c96f6fcaddfa547f0928ce69cf0755Chih-Chung Chang
496d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang    @Override
49743a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong    public boolean isPanorama(int offset) {
49843a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong        return isCamera(offset) && mIsPanorama;
49943a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong    }
50043a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong
50143a80fd806247cb50e81bacda3ad21d210e9ae88Angus Kong    @Override
502dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li    public boolean isStaticCamera(int offset) {
503dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li        return isCamera(offset) && mIsStaticCamera;
504dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li    }
505dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li
506dbb6acc204ba6f095eb0e49e1298e9ceda2c3096Wu-cheng Li    @Override
507d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang    public boolean isVideo(int offset) {
508d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        MediaItem item = getItem(mCurrentIndex + offset);
509d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang        return (item == null)
510d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                ? false
511d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang                : item.getMediaType() == MediaItem.MEDIA_TYPE_VIDEO;
512d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang    }
513d9355113da391f8bbddef1d2a2126ce6edc72291Chih-Chung Chang
514f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    @Override
5156b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    public boolean isDeletable(int offset) {
5166b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        MediaItem item = getItem(mCurrentIndex + offset);
5176b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        return (item == null)
5186b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                ? false
5196b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                : (item.getSupportedOperations() & MediaItem.SUPPORT_DELETE) != 0;
5206b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    }
5216b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
5226b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    @Override
523f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    public int getLoadingState(int offset) {
524f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        ImageEntry entry = mImageCache.get(getPath(mCurrentIndex + offset));
525f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        if (entry == null) return LOADING_INIT;
526f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        if (entry.failToLoad) return LOADING_FAIL;
527f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        if (entry.screenNail != null) return LOADING_COMPLETE;
528f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        return LOADING_INIT;
529f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    }
530f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
5317817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    @Override
53215b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang    public ScreenNail getScreenNail() {
5336b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        return getScreenNail(0);
534f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
535f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
5367817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    @Override
537f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getImageHeight() {
538f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mTileProvider.getImageHeight();
539f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
540f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
5417817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    @Override
542f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getImageWidth() {
543f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mTileProvider.getImageWidth();
544f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
545f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
5467817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    @Override
547f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getLevelCount() {
548f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mTileProvider.getLevelCount();
549f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
550f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
5517817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    @Override
552f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu    public Bitmap getTile(int level, int x, int y, int tileSize) {
553f52ceba89962829aa12f5caba131580e8da85880Bobby Georgescu        return mTileProvider.getTile(level, x, y, tileSize);
554f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
555f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
5567817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    @Override
557f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public boolean isEmpty() {
558f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mSize == 0;
559f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
560f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
5617817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    @Override
562f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int getCurrentIndex() {
563f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mCurrentIndex;
564f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
565f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
5667817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    @Override
567616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin    public MediaItem getMediaItem(int offset) {
568616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        int index = mCurrentIndex + offset;
569616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        if (index >= mContentStart && index < mContentEnd) {
570616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin            return mData[index % DATA_CACHE_SIZE];
571616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        }
572616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        return null;
573f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
574f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
5757817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    @Override
576f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void setCurrentPhoto(Path path, int indexHint) {
577f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mItemPath == path) return;
578f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mItemPath = path;
579f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mCurrentIndex = indexHint;
580f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        updateSlidingWindow();
581f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        updateImageCache();
582b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang        fireDataChange();
583f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
584f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // We need to reload content if the path doesn't match.
585616a70fdb4473d2fbd7b70772a3a82b908aeae1eOwen Lin        MediaItem item = getMediaItem(0);
586f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (item != null && item.getPath() != path) {
587f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mReloadTask != null) mReloadTask.notifyDirty();
588f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
589f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
590f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
5917817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    @Override
5926b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    public void setFocusHintDirection(int direction) {
5936b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        mFocusHintDirection = direction;
5946b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    }
5956b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
5967817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    @Override
5976b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    public void setFocusHintPath(Path path) {
5986b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        mFocusHintPath = path;
5996b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang    }
6006b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
601f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void updateTileProvider() {
602f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        ImageEntry entry = mImageCache.get(getPath(mCurrentIndex));
603f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (entry == null) { // in loading
604f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mTileProvider.clear();
605f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else {
606f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateTileProvider(entry);
607f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
608f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
609f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
610f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void updateTileProvider(ImageEntry entry) {
61115b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang        ScreenNail screenNail = entry.screenNail;
612f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        BitmapRegionDecoder fullImage = entry.fullImage;
613f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (screenNail != null) {
614f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (fullImage != null) {
61515b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang                mTileProvider.setScreenNail(screenNail,
616f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        fullImage.getWidth(), fullImage.getHeight());
617f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mTileProvider.setRegionDecoder(fullImage);
618f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } else {
619f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                int width = screenNail.getWidth();
620f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                int height = screenNail.getHeight();
62115b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang                mTileProvider.setScreenNail(screenNail, width, height);
622f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
623f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else {
624f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mTileProvider.clear();
625f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
626f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
627f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
628f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void updateSlidingWindow() {
629f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // 1. Update the image window
630f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int start = Utils.clamp(mCurrentIndex - IMAGE_CACHE_SIZE / 2,
631f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                0, Math.max(0, mSize - IMAGE_CACHE_SIZE));
632f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int end = Math.min(mSize, start + IMAGE_CACHE_SIZE);
633f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
634f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mActiveStart == start && mActiveEnd == end) return;
635f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
636f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mActiveStart = start;
637f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mActiveEnd = end;
638f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
639f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // 2. Update the data window
640f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        start = Utils.clamp(mCurrentIndex - DATA_CACHE_SIZE / 2,
641f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                0, Math.max(0, mSize - DATA_CACHE_SIZE));
642f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        end = Math.min(mSize, start + DATA_CACHE_SIZE);
643f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mContentStart > mActiveStart || mContentEnd < mActiveEnd
644f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                || Math.abs(start - mContentStart) > MIN_LOAD_COUNT) {
645f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = mContentStart; i < mContentEnd; ++i) {
646f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (i < start || i >= end) {
647f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mData[i % DATA_CACHE_SIZE] = null;
648f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
649f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
650f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mContentStart = start;
651f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mContentEnd = end;
652f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mReloadTask != null) mReloadTask.notifyDirty();
653f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
654f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
655f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
656f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void updateImageRequests() {
657f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (!mIsActive) return;
658f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
659f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int currentIndex = mCurrentIndex;
660f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        MediaItem item = mData[currentIndex % DATA_CACHE_SIZE];
661f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (item == null || item.getPath() != mItemPath) {
662f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // current item mismatch - don't request image
663f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return;
664f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
665f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
666f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // 1. Find the most wanted request and start it (if not already started).
667f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Future<?> task = null;
668f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0; i < sImageFetchSeq.length; i++) {
669f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int offset = sImageFetchSeq[i].indexOffset;
670f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int bit = sImageFetchSeq[i].imageBit;
671b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang            if (bit == BIT_FULL_IMAGE && !mNeedFullImage) continue;
672f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            task = startTaskIfNeeded(currentIndex + offset, bit);
673f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (task != null) break;
674f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
675f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
676f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // 2. Cancel everything else.
677f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (ImageEntry entry : mImageCache.values()) {
678f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (entry.screenNailTask != null && entry.screenNailTask != task) {
679f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                entry.screenNailTask.cancel();
680f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                entry.screenNailTask = null;
681f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                entry.requestedScreenNail = MediaObject.INVALID_DATA_VERSION;
682f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
683f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (entry.fullImageTask != null && entry.fullImageTask != task) {
684f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                entry.fullImageTask.cancel();
685f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                entry.fullImageTask = null;
686f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                entry.requestedFullImage = MediaObject.INVALID_DATA_VERSION;
687f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
688f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
689f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
690f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
691f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    private class ScreenNailJob implements Job<ScreenNail> {
69222493b29c32142f2588c623f5c78e9167352dddcOwen Lin        private MediaItem mItem;
69322493b29c32142f2588c623f5c78e9167352dddcOwen Lin
69422493b29c32142f2588c623f5c78e9167352dddcOwen Lin        public ScreenNailJob(MediaItem item) {
69522493b29c32142f2588c623f5c78e9167352dddcOwen Lin            mItem = item;
69622493b29c32142f2588c623f5c78e9167352dddcOwen Lin        }
69722493b29c32142f2588c623f5c78e9167352dddcOwen Lin
69822493b29c32142f2588c623f5c78e9167352dddcOwen Lin        @Override
69915b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang        public ScreenNail run(JobContext jc) {
70015b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang            // We try to get a ScreenNail first, if it fails, we fallback to get
70115b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang            // a Bitmap and then wrap it in a BitmapScreenNail instead.
70215b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang            ScreenNail s = mItem.getScreenNail();
70315b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang            if (s != null) return s;
70415b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang
705f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            // If this is a temporary item, don't try to get its bitmap because
706f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            // it won't be available. We will get its bitmap after a data reload.
707f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            if (isTemporaryItem(mItem)) {
70849affdc4e274098a34e4eb2dbe4a89a750f1ba7fOwen Lin                return newPlaceholderScreenNail(mItem);
709f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            }
710f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
71122493b29c32142f2588c623f5c78e9167352dddcOwen Lin            Bitmap bitmap = mItem.requestImage(MediaItem.TYPE_THUMBNAIL).run(jc);
71222493b29c32142f2588c623f5c78e9167352dddcOwen Lin            if (jc.isCancelled()) return null;
71322493b29c32142f2588c623f5c78e9167352dddcOwen Lin            if (bitmap != null) {
71422493b29c32142f2588c623f5c78e9167352dddcOwen Lin                bitmap = BitmapUtils.rotateBitmap(bitmap,
71522493b29c32142f2588c623f5c78e9167352dddcOwen Lin                    mItem.getRotation() - mItem.getFullImageRotation(), true);
71622493b29c32142f2588c623f5c78e9167352dddcOwen Lin            }
717030f8dad6aefc42d0af39bc1b93f370937d3e2abOwen Lin            return bitmap == null ? null : new TiledScreenNail(bitmap);
71822493b29c32142f2588c623f5c78e9167352dddcOwen Lin        }
71922493b29c32142f2588c623f5c78e9167352dddcOwen Lin    }
72022493b29c32142f2588c623f5c78e9167352dddcOwen Lin
721f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    private class FullImageJob implements Job<BitmapRegionDecoder> {
722f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        private MediaItem mItem;
723f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
724f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        public FullImageJob(MediaItem item) {
725f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            mItem = item;
726f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        }
727f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
728f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        @Override
729f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        public BitmapRegionDecoder run(JobContext jc) {
730f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            if (isTemporaryItem(mItem)) {
731f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                return null;
732f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            }
733f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            return mItem.requestLargeImage().run(jc);
734f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        }
735f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    }
736f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
737f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // Returns true if we think this is a temporary item created by Camera. A
738f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // temporary item is an image or a video whose data is still being
739f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // processed, but an incomplete entry is created first in MediaProvider, so
740f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // we can display them (in grey tile) even if they are not saved to disk
741f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // yet. When the image or video data is actually saved, we will get
742f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // notification from MediaProvider, reload data, and show the actual image
743f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // or video data.
744f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    private boolean isTemporaryItem(MediaItem mediaItem) {
745f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        // Must have camera to create a temporary item.
746f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        if (mCameraIndex < 0) return false;
747f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        // Must be an item in camera roll.
748f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        if (!(mediaItem instanceof LocalMediaItem)) return false;
749f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        LocalMediaItem item = (LocalMediaItem) mediaItem;
750f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        if (item.getBucketId() != MediaSetUtils.CAMERA_BUCKET_ID) return false;
751f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        // Must have no size, but must have width and height information
752f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        if (item.getSize() != 0) return false;
753f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        if (item.getWidth() == 0) return false;
754f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        if (item.getHeight() == 0) return false;
755f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        // Must be created in the last 10 seconds.
756f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        if (item.getDateInMs() - System.currentTimeMillis() > 10000) return false;
757f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        return true;
758f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    }
759f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
760f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // Create a default ScreenNail when a ScreenNail is needed, but we don't yet
761f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // have one available (because the image data is still being saved, or the
762f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    // Bitmap is still being loaded.
76349affdc4e274098a34e4eb2dbe4a89a750f1ba7fOwen Lin    private ScreenNail newPlaceholderScreenNail(MediaItem item) {
764f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        int width = item.getWidth();
765f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        int height = item.getHeight();
766030f8dad6aefc42d0af39bc1b93f370937d3e2abOwen Lin        return new TiledScreenNail(width, height);
767f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang    }
768f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang
769f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Returns the task if we started the task or the task is already started.
770f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Future<?> startTaskIfNeeded(int index, int which) {
771f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (index < mActiveStart || index >= mActiveEnd) return null;
772f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
773f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        ImageEntry entry = mImageCache.get(getPath(index));
774f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (entry == null) return null;
775f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        MediaItem item = mData[index % DATA_CACHE_SIZE];
776f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        Utils.assertTrue(item != null);
777f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        long version = item.getDataVersion();
778f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
779f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        if (which == BIT_SCREEN_NAIL && entry.screenNailTask != null
780f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                && entry.requestedScreenNail == version) {
781f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return entry.screenNailTask;
782f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        } else if (which == BIT_FULL_IMAGE && entry.fullImageTask != null
783f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                && entry.requestedFullImage == version) {
784f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return entry.fullImageTask;
785f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
786f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
787f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        if (which == BIT_SCREEN_NAIL && entry.requestedScreenNail != version) {
788f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            entry.requestedScreenNail = version;
789f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            entry.screenNailTask = mThreadPool.submit(
79022493b29c32142f2588c623f5c78e9167352dddcOwen Lin                    new ScreenNailJob(item),
791f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                    new ScreenNailListener(item));
792f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // request screen nail
793f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return entry.screenNailTask;
794f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
795f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        if (which == BIT_FULL_IMAGE && entry.requestedFullImage != version
796f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                && (item.getSupportedOperations()
797f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                & MediaItem.SUPPORT_FULL_IMAGE) != 0) {
798f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            entry.requestedFullImage = version;
799f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            entry.fullImageTask = mThreadPool.submit(
800f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                    new FullImageJob(item),
801f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                    new FullImageListener(item));
802f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // request full image
803f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return entry.fullImageTask;
804f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
805f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return null;
806f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
807f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
808f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void updateImageCache() {
809f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        HashSet<Path> toBeRemoved = new HashSet<Path>(mImageCache.keySet());
810f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = mActiveStart; i < mActiveEnd; ++i) {
811f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            MediaItem item = mData[i % DATA_CACHE_SIZE];
812f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            if (item == null) continue;
813f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            Path path = item.getPath();
814f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            ImageEntry entry = mImageCache.get(path);
815f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            toBeRemoved.remove(path);
816f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (entry != null) {
817f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (Math.abs(i - mCurrentIndex) > 1) {
818f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    if (entry.fullImageTask != null) {
819f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        entry.fullImageTask.cancel();
820f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        entry.fullImageTask = null;
821f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
822f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    entry.fullImage = null;
823f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                    entry.requestedFullImage = MediaObject.INVALID_DATA_VERSION;
824f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                }
825f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                if (entry.requestedScreenNail != item.getDataVersion()) {
826f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                    // This ScreenNail is outdated, we want to update it if it's
827f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                    // still a placeholder.
828030f8dad6aefc42d0af39bc1b93f370937d3e2abOwen Lin                    if (entry.screenNail instanceof TiledScreenNail) {
829030f8dad6aefc42d0af39bc1b93f370937d3e2abOwen Lin                        TiledScreenNail s = (TiledScreenNail) entry.screenNail;
830f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                        s.updatePlaceholderSize(
831f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                                item.getWidth(), item.getHeight());
832f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                    }
833f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
834f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } else {
835f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                entry = new ImageEntry();
836f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang                mImageCache.put(path, entry);
837f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
838f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
839f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
840f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // Clear the data and requests for ImageEntries outside the new window.
841f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        for (Path path : toBeRemoved) {
842f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            ImageEntry entry = mImageCache.remove(path);
843f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (entry.fullImageTask != null) entry.fullImageTask.cancel();
844f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (entry.screenNailTask != null) entry.screenNailTask.cancel();
845b8be1e0ad76b6abc0da7ead39f7a9811195d001eChih-Chung Chang            if (entry.screenNail != null) entry.screenNail.recycle();
846f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
847030f8dad6aefc42d0af39bc1b93f370937d3e2abOwen Lin
848030f8dad6aefc42d0af39bc1b93f370937d3e2abOwen Lin        updateScreenNailUploadQueue();
849f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
850f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
851f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class FullImageListener
852f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            implements Runnable, FutureListener<BitmapRegionDecoder> {
853f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        private final Path mPath;
854f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private Future<BitmapRegionDecoder> mFuture;
855f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
856f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        public FullImageListener(MediaItem item) {
857f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            mPath = item.getPath();
858f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
859f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
860e7327d0566b182c522ca6dfa8e04c649d8f631a0Owen Lin        @Override
861f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onFutureDone(Future<BitmapRegionDecoder> future) {
862f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mFuture = future;
863f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mMainHandler.sendMessage(
864f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mMainHandler.obtainMessage(MSG_RUN_OBJECT, this));
865f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
866f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
867e7327d0566b182c522ca6dfa8e04c649d8f631a0Owen Lin        @Override
868f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void run() {
869f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            updateFullImage(mPath, mFuture);
870f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
871f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
872f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
873f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class ScreenNailListener
87415b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang            implements Runnable, FutureListener<ScreenNail> {
875f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        private final Path mPath;
87615b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang        private Future<ScreenNail> mFuture;
877f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
878f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        public ScreenNailListener(MediaItem item) {
879f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            mPath = item.getPath();
880f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
881f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
882e7327d0566b182c522ca6dfa8e04c649d8f631a0Owen Lin        @Override
88315b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang        public void onFutureDone(Future<ScreenNail> future) {
884f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mFuture = future;
885f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mMainHandler.sendMessage(
886f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mMainHandler.obtainMessage(MSG_RUN_OBJECT, this));
887f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
888f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
889e7327d0566b182c522ca6dfa8e04c649d8f631a0Owen Lin        @Override
890f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void run() {
891f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang            updateScreenNail(mPath, mFuture);
892f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
893f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
894f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
895f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static class ImageEntry {
896f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public BitmapRegionDecoder fullImage;
89715b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang        public ScreenNail screenNail;
89815b351a22d02e89d882fc9fe32b3f4c512080e0aChih-Chung Chang        public Future<ScreenNail> screenNailTask;
899f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public Future<BitmapRegionDecoder> fullImageTask;
900f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        public long requestedScreenNail = MediaObject.INVALID_DATA_VERSION;
901f5ce6aeba448f418c99736465f7a02dacd7715bbChih-Chung Chang        public long requestedFullImage = MediaObject.INVALID_DATA_VERSION;
902f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean failToLoad = false;
903f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
904f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
905f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class SourceListener implements ContentListener {
9067817979db0c52ffeacb951625b1e821eba303285Ahbong Chang        @Override
907f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onContentDirty() {
908f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mReloadTask != null) mReloadTask.notifyDirty();
909f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
910f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
911f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
912f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private <T> T executeAndWait(Callable<T> callable) {
913f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        FutureTask<T> task = new FutureTask<T>(callable);
914f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mMainHandler.sendMessage(
915f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mMainHandler.obtainMessage(MSG_RUN_OBJECT, task));
916f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        try {
917f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return task.get();
918f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } catch (InterruptedException e) {
919f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return null;
920f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } catch (ExecutionException e) {
921f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            throw new RuntimeException(e);
922f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
923f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
924f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
925f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static class UpdateInfo {
926f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public long version;
927f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public boolean reloadContent;
928f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public Path target;
929f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public int indexHint;
930f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public int contentStart;
931f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public int contentEnd;
932f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
933f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public int size;
934f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public ArrayList<MediaItem> items;
935f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
936f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
937f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class GetUpdateInfo implements Callable<UpdateInfo> {
938f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
939f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private boolean needContentReload() {
940f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = mContentStart, n = mContentEnd; i < n; ++i) {
941f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mData[i % DATA_CACHE_SIZE] == null) return true;
942f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
943f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            MediaItem current = mData[mCurrentIndex % DATA_CACHE_SIZE];
944f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return current == null || current.getPath() != mItemPath;
945f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
946f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
947f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
948f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public UpdateInfo call() throws Exception {
949113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin            // TODO: Try to load some data in first update
950f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            UpdateInfo info = new UpdateInfo();
951f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            info.version = mSourceVersion;
952f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            info.reloadContent = needContentReload();
953f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            info.target = mItemPath;
954f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            info.indexHint = mCurrentIndex;
955f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            info.contentStart = mContentStart;
956f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            info.contentEnd = mContentEnd;
957f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            info.size = mSize;
958f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return info;
959f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
960f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
961f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
962f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class UpdateContent implements Callable<Void> {
963f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        UpdateInfo mUpdateInfo;
964f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
965f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public UpdateContent(UpdateInfo updateInfo) {
966f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mUpdateInfo = updateInfo;
967f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
968f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
969f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
970f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public Void call() throws Exception {
971f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            UpdateInfo info = mUpdateInfo;
972f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mSourceVersion = info.version;
973f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
974f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (info.size != mSize) {
975f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mSize = info.size;
976f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mContentEnd > mSize) mContentEnd = mSize;
977f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mActiveEnd > mSize) mActiveEnd = mSize;
978f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
979f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
9806b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mCurrentIndex = info.indexHint;
981f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateSlidingWindow();
982f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
983f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (info.items != null) {
984f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                int start = Math.max(info.contentStart, mContentStart);
985f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                int end = Math.min(info.contentStart + info.items.size(), mContentEnd);
986f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                int dataIndex = start % DATA_CACHE_SIZE;
987f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                for (int i = start; i < end; ++i) {
988f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mData[dataIndex] = info.items.get(i - info.contentStart);
989f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    if (++dataIndex == DATA_CACHE_SIZE) dataIndex = 0;
990f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
991f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
9926b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
9936b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            // update mItemPath
9946b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            MediaItem current = mData[mCurrentIndex % DATA_CACHE_SIZE];
9956b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            mItemPath = current == null ? null : current.getPath();
9966b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
997f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateImageCache();
998f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateTileProvider();
999f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateImageRequests();
1000d61248536b0dc28ca9e5f84b20e63a4c96ac3ff3Bobby Georgescu
1001d61248536b0dc28ca9e5f84b20e63a4c96ac3ff3Bobby Georgescu            if (mDataListener != null) {
1002d61248536b0dc28ca9e5f84b20e63a4c96ac3ff3Bobby Georgescu                mDataListener.onPhotoChanged(mCurrentIndex, mItemPath);
1003d61248536b0dc28ca9e5f84b20e63a4c96ac3ff3Bobby Georgescu            }
1004d61248536b0dc28ca9e5f84b20e63a4c96ac3ff3Bobby Georgescu
1005b7ec5534c7b539be2397c27cfa5e8b992974c12dChih-Chung Chang            fireDataChange();
1006f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return null;
1007f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1008f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1009f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1010f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class ReloadTask extends Thread {
1011f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private volatile boolean mActive = true;
1012f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private volatile boolean mDirty = true;
1013f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1014f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private boolean mIsLoading = false;
1015f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1016f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private void updateLoading(boolean loading) {
1017f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mIsLoading == loading) return;
1018f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mIsLoading = loading;
1019f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mMainHandler.sendEmptyMessage(loading ? MSG_LOAD_START : MSG_LOAD_FINISH);
1020f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1021f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1022f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
1023f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void run() {
1024f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            while (mActive) {
1025f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                synchronized (this) {
1026f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    if (!mDirty && mActive) {
1027f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        updateLoading(false);
1028f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        Utils.waitWithoutInterrupt(this);
1029f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        continue;
1030f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
1031f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
1032f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mDirty = false;
1033f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                UpdateInfo info = executeAndWait(new GetUpdateInfo());
1034676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                updateLoading(true);
1035705dc819661cb8c8e839a04577b0641bd6caad53John Reck                long version = mSource.reload();
1036676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                if (info.version != version) {
1037676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    info.reloadContent = true;
1038676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    info.size = mSource.getMediaItemCount();
1039676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                }
1040676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                if (!info.reloadContent) continue;
1041676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                info.items = mSource.getMediaItem(
1042676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                        info.contentStart, info.contentEnd);
10436b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
1044676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                int index = MediaSet.INDEX_NOT_FOUND;
10456b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
1046676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                // First try to focus on the given hint path if there is one.
1047676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                if (mFocusHintPath != null) {
1048676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    index = findIndexOfPathInCache(info, mFocusHintPath);
1049676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    mFocusHintPath = null;
1050676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                }
10516b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
1052676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                // Otherwise try to see if the currently focused item can be found.
1053676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                if (index == MediaSet.INDEX_NOT_FOUND) {
1054676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    MediaItem item = findCurrentMediaItem(info);
1055676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    if (item != null && item.getPath() == info.target) {
10566b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                        index = info.indexHint;
1057676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    } else {
1058676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                        index = findIndexOfTarget(info);
10596b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    }
1060676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                }
10616b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
1062676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                // The image has been deleted. Focus on the next image (keep
1063676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                // mCurrentIndex unchanged) or the previous image (decrease
1064676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                // mCurrentIndex by 1). In page mode we want to see the next
1065676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                // image, so we focus on the next one. In film mode we want the
1066676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                // later images to shift left to fill the empty space, so we
1067676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                // focus on the previous image (so it will not move). In any
1068676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                // case the index needs to be limited to [0, mSize).
1069676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                if (index == MediaSet.INDEX_NOT_FOUND) {
1070676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    index = info.indexHint;
107141b3cff60905a96ab5b6f8e5f42bcc3bab1eb605Bobby Georgescu                    int focusHintDirection = mFocusHintDirection;
107241b3cff60905a96ab5b6f8e5f42bcc3bab1eb605Bobby Georgescu                    if (index == (mCameraIndex + 1)) {
107341b3cff60905a96ab5b6f8e5f42bcc3bab1eb605Bobby Georgescu                        focusHintDirection = FOCUS_HINT_NEXT;
107441b3cff60905a96ab5b6f8e5f42bcc3bab1eb605Bobby Georgescu                    }
107541b3cff60905a96ab5b6f8e5f42bcc3bab1eb605Bobby Georgescu                    if (focusHintDirection == FOCUS_HINT_PREVIOUS
107641b3cff60905a96ab5b6f8e5f42bcc3bab1eb605Bobby Georgescu                            && index > 0) {
1077676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                        index--;
1078f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
1079676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                }
10803000f70d462f27ddf992a50b57db2ef9c8dcac15Mangesh Ghiware
1081676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                // Don't change index if mSize == 0
1082676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                if (mSize > 0) {
1083676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                    if (index >= mSize) index = mSize - 1;
1084f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
10856b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
1086676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin                info.indexHint = index;
1087676d4762496eddae66930c6f8b0bae22a22b3ef6Owen Lin
1088f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                executeAndWait(new UpdateContent(info));
1089f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
1090f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1091f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1092f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public synchronized void notifyDirty() {
1093f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mDirty = true;
1094f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            notifyAll();
1095f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1096f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1097f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public synchronized void terminate() {
1098f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mActive = false;
1099f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            notifyAll();
1100f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1101f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private MediaItem findCurrentMediaItem(UpdateInfo info) {
1103f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ArrayList<MediaItem> items = info.items;
1104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int index = info.indexHint - info.contentStart;
1105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return index < 0 || index >= items.size() ? null : items.get(index);
1106f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
1107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private int findIndexOfTarget(UpdateInfo info) {
1109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (info.target == null) return info.indexHint;
1110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ArrayList<MediaItem> items = info.items;
1111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // First, try to find the item in the data just loaded
1113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (items != null) {
11146b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                int i = findIndexOfPathInCache(info, info.target);
11156b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                if (i != MediaSet.INDEX_NOT_FOUND) return i;
1116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
1117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            // Not found, find it in mSource.
1119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return mSource.getIndexOfItem(info.target, info.indexHint);
1120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
11216b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang
11226b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        private int findIndexOfPathInCache(UpdateInfo info, Path path) {
11236b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            ArrayList<MediaItem> items = info.items;
11246b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            for (int i = 0, n = items.size(); i < n; ++i) {
11250086dbabb383a9ba5e06a8b49e3e180bfa36a64bChih-Chung Chang                MediaItem item = items.get(i);
11260086dbabb383a9ba5e06a8b49e3e180bfa36a64bChih-Chung Chang                if (item != null && item.getPath() == path) {
11276b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                    return i + info.contentStart;
11286b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang                }
11296b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            }
11306b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang            return MediaSet.INDEX_NOT_FOUND;
11316b891c6a3739f8c49d42f9db6fc76cb92c7c5f25Chih-Chung Chang        }
1132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
1133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
1134