• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.gallery3d.app;
18 
19 import com.android.gallery3d.app.SlideshowPage.Slide;
20 import com.android.gallery3d.data.ContentListener;
21 import com.android.gallery3d.data.MediaItem;
22 import com.android.gallery3d.data.MediaObject;
23 import com.android.gallery3d.util.Future;
24 import com.android.gallery3d.util.FutureListener;
25 import com.android.gallery3d.util.ThreadPool;
26 import com.android.gallery3d.util.ThreadPool.Job;
27 import com.android.gallery3d.util.ThreadPool.JobContext;
28 
29 import android.graphics.Bitmap;
30 
31 import java.util.LinkedList;
32 import java.util.concurrent.atomic.AtomicBoolean;
33 
34 public 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 {
addContentListener(ContentListener listener)41         public void addContentListener(ContentListener listener);
removeContentListener(ContentListener listener)42         public void removeContentListener(ContentListener listener);
reload()43         public long reload();
getMediaItem(int index)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 
SlideshowDataAdapter(GalleryContext context, SlideshowSource source, int index)64     public SlideshowDataAdapter(GalleryContext context, SlideshowSource source, int index) {
65         mSource = source;
66         mLoadIndex = index;
67         mNextOutput = index;
68         mThreadPool = context.getThreadPool();
69     }
70 
loadItem()71     private 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> {
run(JobContext jc)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 {
onContentDirty()137         public void onContentDirty() {
138             synchronized (SlideshowDataAdapter.this) {
139                 mNeedReload.set(true);
140                 mDataReady = true;
141                 SlideshowDataAdapter.this.notifyAll();
142             }
143         }
144     }
145 
innerNextBitmap()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 
nextSlide(FutureListener<Slide> listener)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