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.ui;
18f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
19dd46fe623b16ba14c70a539b7599cc75c7799612Owen Linimport android.graphics.Bitmap;
20dd46fe623b16ba14c70a539b7599cc75c7799612Owen Linimport android.os.Message;
21dd46fe623b16ba14c70a539b7599cc75c7799612Owen Lin
22f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.app.GalleryActivity;
23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.common.BitmapUtils;
24f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.common.LruCache;
25f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.common.Utils;
26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.data.MediaItem;
27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.data.Path;
28f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.util.Future;
29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.util.FutureListener;
30bc2154199aee16762e3758ad0cfed685323d9a9fChih-Chung Changimport com.android.gallery3d.util.GalleryUtils;
31113bfc77c4468411da9ae1290553c3be89f8df9aOwen Linimport com.android.gallery3d.util.JobLimiter;
32f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.util.ThreadPool.Job;
33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.util.ThreadPool.JobContext;
34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic class AlbumSlidingWindow implements AlbumView.ModelListener {
36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @SuppressWarnings("unused")
37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "AlbumSlidingWindow";
38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int MSG_LOAD_BITMAP_DONE = 0;
40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int MSG_UPDATE_SLOT = 1;
41113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    private static final int JOB_LIMIT = 2;
42da071d27a1435cce080b5c609d0d833555e5a175Chih-Chung Chang    private static final int PLACEHOLDER_COLOR = 0xFF222222;
43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public static interface Listener {
45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onSizeChanged(int size);
46f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onContentInvalidated();
47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onWindowContentChanged(
48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                int slot, DisplayItem old, DisplayItem update);
49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final AlbumView.Model mSource;
52f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mSize;
53f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
54f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mContentStart = 0;
55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mContentEnd = 0;
56f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mActiveStart = 0;
58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mActiveEnd = 0;
59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Listener mListener;
61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mFocusIndex = -1;
62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final AlbumDisplayItem mData[];
64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final ColorTexture mWaitLoadingTexture;
65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private SelectionDrawer mSelectionDrawer;
66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private SynchronizedHandler mHandler;
68113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin    private JobLimiter mThreadPool;
69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mActiveRequestCount = 0;
71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private boolean mIsActive = false;
72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
739201679ed1c485767f2e334aa618bd733024af03Chih-Chung Chang    private int mCacheThumbSize;  // 0: Don't cache the thumbnails
74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private LruCache<Path, Bitmap> mImageCache = new LruCache<Path, Bitmap>(1000);
75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
76f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public AlbumSlidingWindow(GalleryActivity activity,
77f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            AlbumView.Model source, int cacheSize,
789201679ed1c485767f2e334aa618bd733024af03Chih-Chung Chang            int cacheThumbSize) {
79f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        source.setModelListener(this);
80f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSource = source;
81f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mData = new AlbumDisplayItem[cacheSize];
82f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSize = source.size();
83f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
84da071d27a1435cce080b5c609d0d833555e5a175Chih-Chung Chang        mWaitLoadingTexture = new ColorTexture(PLACEHOLDER_COLOR);
85f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mWaitLoadingTexture.setSize(1, 1);
86f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mHandler = new SynchronizedHandler(activity.getGLRoot()) {
88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            @Override
89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            public void handleMessage(Message message) {
90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                switch (message.what) {
91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    case MSG_LOAD_BITMAP_DONE: {
92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        ((AlbumDisplayItem) message.obj).onLoadBitmapDone();
93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        break;
94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    case MSG_UPDATE_SLOT: {
96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        updateSlotContent(message.arg1);
97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        break;
98f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
99f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
100f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
101f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        };
102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
103113bfc77c4468411da9ae1290553c3be89f8df9aOwen Lin        mThreadPool = new JobLimiter(activity.getThreadPool(), JOB_LIMIT);
104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
106f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void setSelectionDrawer(SelectionDrawer drawer) {
107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSelectionDrawer = drawer;
108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void setListener(Listener listener) {
111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mListener = listener;
112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void setFocusIndex(int slotIndex) {
115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mFocusIndex = slotIndex;
116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public DisplayItem get(int slotIndex) {
119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Utils.assertTrue(isActiveSlot(slotIndex),
120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                "invalid slot: %s outsides (%s, %s)",
121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                slotIndex, mActiveStart, mActiveEnd);
122f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mData[slotIndex % mData.length];
123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
124f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public int size() {
126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mSize;
127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
129f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public boolean isActiveSlot(int slotIndex) {
130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return slotIndex >= mActiveStart && slotIndex < mActiveEnd;
131f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void setContentWindow(int contentStart, int contentEnd) {
134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (contentStart == mContentStart && contentEnd == mContentEnd) return;
135f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (!mIsActive) {
137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mContentStart = contentStart;
138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mContentEnd = contentEnd;
139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mSource.setActiveWindow(contentStart, contentEnd);
140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return;
141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (contentStart >= mContentEnd || mContentStart >= contentEnd) {
144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = mContentStart, n = mContentEnd; i < n; ++i) {
145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                freeSlotContent(i);
146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
147f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mSource.setActiveWindow(contentStart, contentEnd);
148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = contentStart; i < contentEnd; ++i) {
149f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                prepareSlotContent(i);
150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else {
152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = mContentStart; i < contentStart; ++i) {
153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                freeSlotContent(i);
154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = contentEnd, n = mContentEnd; i < n; ++i) {
156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                freeSlotContent(i);
157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mSource.setActiveWindow(contentStart, contentEnd);
159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = contentStart, n = mContentStart; i < n; ++i) {
160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                prepareSlotContent(i);
161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            for (int i = mContentEnd; i < contentEnd; ++i) {
163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                prepareSlotContent(i);
164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
166f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
167f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mContentStart = contentStart;
168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mContentEnd = contentEnd;
169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
170f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void setActiveWindow(int start, int end) {
172f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        Utils.assertTrue(start <= end
173f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                && end - start <= mData.length && end <= mSize,
174f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                "%s, %s, %s, %s", start, end, mData.length, mSize);
175f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        DisplayItem data[] = mData;
176f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mActiveStart = start;
178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mActiveEnd = end;
179f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int contentStart = Utils.clamp((start + end) / 2 - data.length / 2,
181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                0, Math.max(0, mSize - data.length));
182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int contentEnd = Math.min(contentStart + data.length, mSize);
183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        setContentWindow(contentStart, contentEnd);
184f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mIsActive) updateAllImageRequests();
185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // We would like to request non active slots in the following order:
188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    // Order:    8 6 4 2                   1 3 5 7
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    //         |---------|---------------|---------|
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    //                   |<-  active  ->|
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    //         |<-------- cached range ----------->|
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void requestNonactiveImages() {
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int range = Math.max(
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                (mContentEnd - mActiveEnd), (mActiveStart - mContentStart));
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0 ;i < range; ++i) {
196f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            requestSlotImage(mActiveEnd + i, false);
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            requestSlotImage(mActiveStart - 1 - i, false);
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
201f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void requestSlotImage(int slotIndex, boolean isActive) {
202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (slotIndex < mContentStart || slotIndex >= mContentEnd) return;
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        AlbumDisplayItem item = mData[slotIndex % mData.length];
204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        item.requestImage();
205f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
206f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
207f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void cancelNonactiveImages() {
208f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int range = Math.max(
209f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                (mContentEnd - mActiveEnd), (mActiveStart - mContentStart));
210f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = 0 ;i < range; ++i) {
211f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            cancelSlotImage(mActiveEnd + i, false);
212f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            cancelSlotImage(mActiveStart - 1 - i, false);
213f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
214f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
215f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
216f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void cancelSlotImage(int slotIndex, boolean isActive) {
217f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (slotIndex < mContentStart || slotIndex >= mContentEnd) return;
218f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        AlbumDisplayItem item = mData[slotIndex % mData.length];
219f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        item.cancelImageRequest();
220f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
221f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
222f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void freeSlotContent(int slotIndex) {
223f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        AlbumDisplayItem data[] = mData;
224f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int index = slotIndex % data.length;
225f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        AlbumDisplayItem original = data[index];
226f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (original != null) {
227f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            original.recycle();
228f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            data[index] = null;
229f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
230f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
231f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
232f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void prepareSlotContent(final int slotIndex) {
233f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mData[slotIndex % mData.length] = new AlbumDisplayItem(
234f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                slotIndex, mSource.get(slotIndex));
235f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
236f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
237f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void updateSlotContent(final int slotIndex) {
238f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        MediaItem item = mSource.get(slotIndex);
239f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        AlbumDisplayItem data[] = mData;
240f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        int index = slotIndex % data.length;
241f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        AlbumDisplayItem original = data[index];
242f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        AlbumDisplayItem update = new AlbumDisplayItem(slotIndex, item);
243f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        data[index] = update;
244f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        boolean isActive = isActiveSlot(slotIndex);
245f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mListener != null && isActive) {
246f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mListener.onWindowContentChanged(slotIndex, original, update);
247f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
248f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (original != null) {
249f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (isActive && original.isRequestInProgress()) {
250f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                --mActiveRequestCount;
251f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
252f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            original.recycle();
253f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
254f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (isActive) {
255f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mActiveRequestCount == 0) cancelNonactiveImages();
256f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            ++mActiveRequestCount;
257f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            update.requestImage();
258f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else {
259f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mActiveRequestCount == 0) update.requestImage();
260f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
261f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
262f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
263f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private void updateAllImageRequests() {
264f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mActiveRequestCount = 0;
265f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        AlbumDisplayItem data[] = mData;
266f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = mActiveStart, n = mActiveEnd; i < n; ++i) {
267f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            AlbumDisplayItem item = data[i % data.length];
268f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            item.requestImage();
269f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (item.isRequestInProgress()) ++mActiveRequestCount;
270f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
271f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mActiveRequestCount == 0) {
272f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            requestNonactiveImages();
273f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        } else {
274f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            cancelNonactiveImages();
275f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
276f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
277f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
278f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class AlbumDisplayItem extends AbstractDisplayItem
279f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            implements FutureListener<Bitmap>, Job<Bitmap> {
280f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private Future<Bitmap> mFuture;
281f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private final int mSlotIndex;
282f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private final int mMediaType;
283f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private Texture mContent;
284bc2154199aee16762e3758ad0cfed685323d9a9fChih-Chung Chang        private boolean mIsPanorama;
2856c1f01e21406a05dc7d3258001aa901bd8628a79Chih-Chung Chang        private boolean mWaitLoadingDisplayed;
286f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
287f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public AlbumDisplayItem(int slotIndex, MediaItem item) {
288f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            super(item);
289f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mMediaType = (item == null)
290f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    ? MediaItem.MEDIA_TYPE_UNKNOWN
291f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    : item.getMediaType();
292f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mSlotIndex = slotIndex;
293bc2154199aee16762e3758ad0cfed685323d9a9fChih-Chung Chang            mIsPanorama = GalleryUtils.isPanorama(item);
294f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateContent(mWaitLoadingTexture);
295f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
296f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
297f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
298f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        protected void onBitmapAvailable(Bitmap bitmap) {
299f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            boolean isActiveSlot = isActiveSlot(mSlotIndex);
300f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (isActiveSlot) {
301f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                --mActiveRequestCount;
302f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mActiveRequestCount == 0) requestNonactiveImages();
303f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
304f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (bitmap != null) {
3051a088db165c138f57d9445ca0b7e50fe90d3ad1dChih-Chung Chang                BitmapTexture texture = new BitmapTexture(bitmap, true);
306f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                texture.setThrottled(true);
3076c1f01e21406a05dc7d3258001aa901bd8628a79Chih-Chung Chang                if (mWaitLoadingDisplayed) {
3086c1f01e21406a05dc7d3258001aa901bd8628a79Chih-Chung Chang                    updateContent(new FadeInTexture(PLACEHOLDER_COLOR, texture));
3096c1f01e21406a05dc7d3258001aa901bd8628a79Chih-Chung Chang                } else {
3106c1f01e21406a05dc7d3258001aa901bd8628a79Chih-Chung Chang                    updateContent(texture);
3116c1f01e21406a05dc7d3258001aa901bd8628a79Chih-Chung Chang                }
312f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mListener != null && isActiveSlot) {
313f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    mListener.onContentInvalidated();
314f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
315f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
316f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
317f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
318f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private void updateContent(Texture content) {
319f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mContent = content;
3209201679ed1c485767f2e334aa618bd733024af03Chih-Chung Chang        }
321f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
3229201679ed1c485767f2e334aa618bd733024af03Chih-Chung Chang        @Override
323da071d27a1435cce080b5c609d0d833555e5a175Chih-Chung Chang        public int render(GLCanvas canvas, int pass) {
3249201679ed1c485767f2e334aa618bd733024af03Chih-Chung Chang            // Fit the content into the box
325f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int width = mContent.getWidth();
326f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            int height = mContent.getHeight();
327f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
3289201679ed1c485767f2e334aa618bd733024af03Chih-Chung Chang            float scalex = mBoxWidth / (float) width;
3299201679ed1c485767f2e334aa618bd733024af03Chih-Chung Chang            float scaley = mBoxHeight / (float) height;
330f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            float scale = Math.min(scalex, scaley);
331f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
332f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            width = (int) Math.floor(width * scale);
333f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            height = (int) Math.floor(height * scale);
334f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
3359201679ed1c485767f2e334aa618bd733024af03Chih-Chung Chang            // Now draw it
336f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (pass == 0) {
337f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                Path path = null;
338f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mMediaItem != null) path = mMediaItem.getPath();
3399201679ed1c485767f2e334aa618bd733024af03Chih-Chung Chang                mSelectionDrawer.draw(canvas, mContent, width, height,
340bc2154199aee16762e3758ad0cfed685323d9a9fChih-Chung Chang                        getRotation(), path, mMediaType, mIsPanorama);
3416c1f01e21406a05dc7d3258001aa901bd8628a79Chih-Chung Chang                if (mContent == mWaitLoadingTexture) {
3426c1f01e21406a05dc7d3258001aa901bd8628a79Chih-Chung Chang                       mWaitLoadingDisplayed = true;
3436c1f01e21406a05dc7d3258001aa901bd8628a79Chih-Chung Chang                }
344da071d27a1435cce080b5c609d0d833555e5a175Chih-Chung Chang                int result = 0;
345da071d27a1435cce080b5c609d0d833555e5a175Chih-Chung Chang                if (mFocusIndex == mSlotIndex) {
346da071d27a1435cce080b5c609d0d833555e5a175Chih-Chung Chang                    result |= RENDER_MORE_PASS;
347da071d27a1435cce080b5c609d0d833555e5a175Chih-Chung Chang                }
3486c1f01e21406a05dc7d3258001aa901bd8628a79Chih-Chung Chang                if ((mContent instanceof FadeInTexture) &&
349da071d27a1435cce080b5c609d0d833555e5a175Chih-Chung Chang                        ((FadeInTexture) mContent).isAnimating()) {
350da071d27a1435cce080b5c609d0d833555e5a175Chih-Chung Chang                    result |= RENDER_MORE_FRAME;
351da071d27a1435cce080b5c609d0d833555e5a175Chih-Chung Chang                }
352da071d27a1435cce080b5c609d0d833555e5a175Chih-Chung Chang                return result;
353f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } else if (pass == 1) {
3549201679ed1c485767f2e334aa618bd733024af03Chih-Chung Chang                mSelectionDrawer.drawFocus(canvas, width, height);
355f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
356da071d27a1435cce080b5c609d0d833555e5a175Chih-Chung Chang            return 0;
357f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
358f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
359f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
360f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void startLoadBitmap() {
3619201679ed1c485767f2e334aa618bd733024af03Chih-Chung Chang            if (mCacheThumbSize > 0) {
362f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                Path path = mMediaItem.getPath();
363f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mImageCache.containsKey(path)) {
364f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    Bitmap bitmap = mImageCache.get(path);
365f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    updateImage(bitmap, false);
366f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    return;
367f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
368f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mFuture = mThreadPool.submit(this, this);
369f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } else {
370f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mFuture = mThreadPool.submit(mMediaItem.requestImage(
371f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        MediaItem.TYPE_MICROTHUMBNAIL), this);
372f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
373f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
374f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
375f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        // This gets the bitmap and scale it down.
376f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public Bitmap run(JobContext jc) {
377f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            Job<Bitmap> job = mMediaItem.requestImage(
378f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    MediaItem.TYPE_MICROTHUMBNAIL);
379f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            Bitmap bitmap = job.run(jc);
380f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (bitmap != null) {
381f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                bitmap = BitmapUtils.resizeDownBySideLength(
3829201679ed1c485767f2e334aa618bd733024af03Chih-Chung Chang                        bitmap, mCacheThumbSize, true);
383f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
384f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return bitmap;
385f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
386f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
387f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
388f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void cancelLoadBitmap() {
389f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mFuture != null) {
390f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mFuture.cancel();
391f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
392f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
393f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
394f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
395f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onFutureDone(Future<Bitmap> bitmap) {
396f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mHandler.sendMessage(mHandler.obtainMessage(MSG_LOAD_BITMAP_DONE, this));
397f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
398f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
399f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        private void onLoadBitmapDone() {
400f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            Future<Bitmap> future = mFuture;
401f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mFuture = null;
402f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            Bitmap bitmap = future.get();
403f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            boolean isCancelled = future.isCancelled();
4049201679ed1c485767f2e334aa618bd733024af03Chih-Chung Chang            if (mCacheThumbSize > 0 && (bitmap != null || !isCancelled)) {
405f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                Path path = mMediaItem.getPath();
406f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mImageCache.put(path, bitmap);
407f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
408f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateImage(bitmap, isCancelled);
409f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
410f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
411f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        @Override
412f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public String toString() {
413f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            return String.format("AlbumDisplayItem[%s]", mSlotIndex);
414f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
415f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
416f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
417f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void onSizeChanged(int size) {
418f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mSize != size) {
419f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mSize = size;
420f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (mListener != null) mListener.onSizeChanged(mSize);
421f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
422f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
423f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
424f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void onWindowContentChanged(int index) {
425f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (index >= mContentStart && index < mContentEnd && mIsActive) {
426f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            updateSlotContent(index);
427f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
428f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
429f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
430f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void resume() {
431f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mIsActive = true;
432f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = mContentStart, n = mContentEnd; i < n; ++i) {
433f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            prepareSlotContent(i);
434f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
435f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        updateAllImageRequests();
436f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
437f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
438f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void pause() {
439f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mIsActive = false;
440f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        for (int i = mContentStart, n = mContentEnd; i < n; ++i) {
441f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            freeSlotContent(i);
442f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
443f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mImageCache.clear();
444f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
445f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
446