SlideshowDataAdapter.java revision f9a0a4306d589b4a4e20554fed512a603426bfa1
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.gallery3d.app;
18
19import com.android.gallery3d.app.SlideshowPage.Slide;
20import com.android.gallery3d.data.ContentListener;
21import com.android.gallery3d.data.MediaItem;
22import com.android.gallery3d.data.MediaObject;
23import com.android.gallery3d.util.Future;
24import com.android.gallery3d.util.FutureListener;
25import com.android.gallery3d.util.ThreadPool;
26import com.android.gallery3d.util.ThreadPool.Job;
27import com.android.gallery3d.util.ThreadPool.JobContext;
28
29import android.graphics.Bitmap;
30
31import java.util.LinkedList;
32import java.util.concurrent.atomic.AtomicBoolean;
33
34public class SlideshowDataAdapter implements SlideshowPage.Model {
35    @SuppressWarnings("unused")
36    private static final String TAG = "SlideshowDataAdapter";
37
38    private static final int IMAGE_QUEUE_CAPACITY = 3;
39
40    public interface SlideshowSource {
41        public void addContentListener(ContentListener listener);
42        public void removeContentListener(ContentListener listener);
43        public long reload();
44        public MediaItem getMediaItem(int index);
45    }
46
47    private final SlideshowSource mSource;
48
49    private int mLoadIndex = 0;
50    private int mNextOutput = 0;
51    private boolean mIsActive = false;
52    private boolean mNeedReset;
53    private boolean mDataReady;
54
55    private final LinkedList<Slide> mImageQueue = new LinkedList<Slide>();
56
57    private Future<Void> mReloadTask;
58    private final ThreadPool mThreadPool;
59
60    private long mDataVersion = MediaObject.INVALID_DATA_VERSION;
61    private final AtomicBoolean mNeedReload = new AtomicBoolean(false);
62    private final SourceListener mSourceListener = new SourceListener();
63
64    public SlideshowDataAdapter(GalleryContext context, SlideshowSource source, int index) {
65        mSource = source;
66        mLoadIndex = index;
67        mNextOutput = index;
68        mThreadPool = context.getThreadPool();
69    }
70
71    public MediaItem loadItem() {
72        if (mNeedReload.compareAndSet(true, false)) {
73            long v = mSource.reload();
74            if (v != mDataVersion) {
75                mDataVersion = v;
76                mNeedReset = true;
77                return null;
78            }
79        }
80        return mSource.getMediaItem(mLoadIndex);
81    }
82
83    private class ReloadTask implements Job<Void> {
84        public Void run(JobContext jc) {
85            while (true) {
86                synchronized (SlideshowDataAdapter.this) {
87                    while (mIsActive && (!mDataReady
88                            || mImageQueue.size() >= IMAGE_QUEUE_CAPACITY)) {
89                        try {
90                            SlideshowDataAdapter.this.wait();
91                        } catch (InterruptedException ex) {
92                            // ignored.
93                        }
94                        continue;
95                    }
96                }
97                if (!mIsActive) return null;
98                mNeedReset = false;
99
100                MediaItem item = loadItem();
101
102                if (mNeedReset) {
103                    synchronized (SlideshowDataAdapter.this) {
104                        mImageQueue.clear();
105                        mLoadIndex = mNextOutput;
106                    }
107                    continue;
108                }
109
110                if (item == null) {
111                    synchronized (SlideshowDataAdapter.this) {
112                        if (!mNeedReload.get()) mDataReady = false;
113                        SlideshowDataAdapter.this.notifyAll();
114                    }
115                    continue;
116                }
117
118                Bitmap bitmap = item
119                        .requestImage(MediaItem.TYPE_THUMBNAIL)
120                        .run(jc);
121
122                if (bitmap != null) {
123                    synchronized (SlideshowDataAdapter.this) {
124                        mImageQueue.addLast(
125                                new Slide(item, mLoadIndex, bitmap));
126                        if (mImageQueue.size() == 1) {
127                            SlideshowDataAdapter.this.notifyAll();
128                        }
129                    }
130                }
131                ++mLoadIndex;
132            }
133        }
134    }
135
136    private class SourceListener implements ContentListener {
137        public void onContentDirty() {
138            synchronized (SlideshowDataAdapter.this) {
139                mNeedReload.set(true);
140                mDataReady = true;
141                SlideshowDataAdapter.this.notifyAll();
142            }
143        }
144    }
145
146    private synchronized Slide innerNextBitmap() {
147        while (mIsActive && mDataReady && mImageQueue.isEmpty()) {
148            try {
149                wait();
150            } catch (InterruptedException t) {
151                throw new AssertionError();
152            }
153        }
154        if (mImageQueue.isEmpty()) return null;
155        mNextOutput++;
156        this.notifyAll();
157        return mImageQueue.removeFirst();
158    }
159
160    public Future<Slide> nextSlide(FutureListener<Slide> listener) {
161        return mThreadPool.submit(new Job<Slide>() {
162            public Slide run(JobContext jc) {
163                jc.setMode(ThreadPool.MODE_NONE);
164                return innerNextBitmap();
165            }
166        }, listener);
167    }
168
169    public void pause() {
170        synchronized (this) {
171            mIsActive = false;
172            notifyAll();
173        }
174        mSource.removeContentListener(mSourceListener);
175        mReloadTask.cancel();
176        mReloadTask.waitDone();
177        mReloadTask = null;
178    }
179
180    public synchronized void resume() {
181        mIsActive = true;
182        mSource.addContentListener(mSourceListener);
183        mNeedReload.set(true);
184        mDataReady = true;
185        mReloadTask = mThreadPool.submit(new ReloadTask());
186    }
187}
188