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
19604f053d040782159b61d3b74ee533bc2c0ccdd0Ray Chenimport android.graphics.Bitmap;
20604f053d040782159b61d3b74ee533bc2c0ccdd0Ray Chen
21f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.app.SlideshowPage.Slide;
22f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.data.ContentListener;
23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.data.MediaItem;
24f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.data.MediaObject;
25604f053d040782159b61d3b74ee533bc2c0ccdd0Ray Chenimport com.android.gallery3d.data.Path;
26f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.util.Future;
27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.util.FutureListener;
28f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.util.ThreadPool;
29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.util.ThreadPool.Job;
30f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.util.ThreadPool.JobContext;
31f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
32f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.LinkedList;
33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport java.util.concurrent.atomic.AtomicBoolean;
34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
35f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpublic class SlideshowDataAdapter implements SlideshowPage.Model {
36f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    @SuppressWarnings("unused")
37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final String TAG = "SlideshowDataAdapter";
38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private static final int IMAGE_QUEUE_CAPACITY = 3;
40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
41f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public interface SlideshowSource {
42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void addContentListener(ContentListener listener);
43f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void removeContentListener(ContentListener listener);
44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public long reload();
45f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public MediaItem getMediaItem(int index);
46604f053d040782159b61d3b74ee533bc2c0ccdd0Ray Chen        public int findItemIndex(Path path, int hint);
47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
49f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final SlideshowSource mSource;
50f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
51f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mLoadIndex = 0;
52f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private int mNextOutput = 0;
53f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private boolean mIsActive = false;
54f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private boolean mNeedReset;
55f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private boolean mDataReady;
56604f053d040782159b61d3b74ee533bc2c0ccdd0Ray Chen    private Path mInitialPath;
57f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
58f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final LinkedList<Slide> mImageQueue = new LinkedList<Slide>();
59f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
60f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private Future<Void> mReloadTask;
61f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final ThreadPool mThreadPool;
62f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
63f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private long mDataVersion = MediaObject.INVALID_DATA_VERSION;
64f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final AtomicBoolean mNeedReload = new AtomicBoolean(false);
65f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private final SourceListener mSourceListener = new SourceListener();
66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
67604f053d040782159b61d3b74ee533bc2c0ccdd0Ray Chen    // The index is just a hint if initialPath is set
68604f053d040782159b61d3b74ee533bc2c0ccdd0Ray Chen    public SlideshowDataAdapter(GalleryContext context, SlideshowSource source, int index,
69604f053d040782159b61d3b74ee533bc2c0ccdd0Ray Chen            Path initialPath) {
70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSource = source;
71604f053d040782159b61d3b74ee533bc2c0ccdd0Ray Chen        mInitialPath = initialPath;
72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mLoadIndex = index;
73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mNextOutput = index;
74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mThreadPool = context.getThreadPool();
75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
76f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
7778979fc21d1e1111a504cc07ae6b0e0bc4d61447Chih-Chung Chang    private MediaItem loadItem() {
78f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mNeedReload.compareAndSet(true, false)) {
79f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            long v = mSource.reload();
80f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            if (v != mDataVersion) {
81f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mDataVersion = v;
82f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mNeedReset = true;
83f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                return null;
84f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
85f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
86604f053d040782159b61d3b74ee533bc2c0ccdd0Ray Chen        int index = mLoadIndex;
87604f053d040782159b61d3b74ee533bc2c0ccdd0Ray Chen        if (mInitialPath != null) {
88604f053d040782159b61d3b74ee533bc2c0ccdd0Ray Chen            index = mSource.findItemIndex(mInitialPath, index);
89604f053d040782159b61d3b74ee533bc2c0ccdd0Ray Chen            mInitialPath = null;
90604f053d040782159b61d3b74ee533bc2c0ccdd0Ray Chen        }
91604f053d040782159b61d3b74ee533bc2c0ccdd0Ray Chen        return mSource.getMediaItem(index);
92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class ReloadTask implements Job<Void> {
957817979db0c52ffeacb951625b1e821eba303285Ahbong Chang        @Override
96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public Void run(JobContext jc) {
97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            while (true) {
98f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                synchronized (SlideshowDataAdapter.this) {
99f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    while (mIsActive && (!mDataReady
100f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            || mImageQueue.size() >= IMAGE_QUEUE_CAPACITY)) {
101f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        try {
102f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            SlideshowDataAdapter.this.wait();
103f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        } catch (InterruptedException ex) {
104f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            // ignored.
105f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        }
106f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        continue;
107f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
108f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
109f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (!mIsActive) return null;
110f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mNeedReset = false;
111f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
112f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                MediaItem item = loadItem();
113f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
114f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (mNeedReset) {
115f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    synchronized (SlideshowDataAdapter.this) {
116f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        mImageQueue.clear();
117f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        mLoadIndex = mNextOutput;
118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
119f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    continue;
120f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
121f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
122f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (item == null) {
123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    synchronized (SlideshowDataAdapter.this) {
124f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        if (!mNeedReload.get()) mDataReady = false;
125f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        SlideshowDataAdapter.this.notifyAll();
126f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    continue;
128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
129f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
130f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                Bitmap bitmap = item
131f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        .requestImage(MediaItem.TYPE_THUMBNAIL)
132f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        .run(jc);
133f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
134f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                if (bitmap != null) {
135f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    synchronized (SlideshowDataAdapter.this) {
136f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        mImageQueue.addLast(
137f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                                new Slide(item, mLoadIndex, bitmap));
138f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        if (mImageQueue.size() == 1) {
139f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                            SlideshowDataAdapter.this.notifyAll();
140f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                        }
141f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                    }
142f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                }
143f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                ++mLoadIndex;
144f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
145f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
146f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
147f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
148f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private class SourceListener implements ContentListener {
1497817979db0c52ffeacb951625b1e821eba303285Ahbong Chang        @Override
150f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        public void onContentDirty() {
151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            synchronized (SlideshowDataAdapter.this) {
152f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mNeedReload.set(true);
153f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                mDataReady = true;
154f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                SlideshowDataAdapter.this.notifyAll();
155f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
156f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
158f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
159f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    private synchronized Slide innerNextBitmap() {
160f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        while (mIsActive && mDataReady && mImageQueue.isEmpty()) {
161f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            try {
162f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                wait();
163f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            } catch (InterruptedException t) {
164f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                throw new AssertionError();
165f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
166f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
167f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        if (mImageQueue.isEmpty()) return null;
168f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mNextOutput++;
169f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        this.notifyAll();
170f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mImageQueue.removeFirst();
171f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
172f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1737817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    @Override
174f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public Future<Slide> nextSlide(FutureListener<Slide> listener) {
175f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        return mThreadPool.submit(new Job<Slide>() {
1767817979db0c52ffeacb951625b1e821eba303285Ahbong Chang            @Override
177f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            public Slide run(JobContext jc) {
178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                jc.setMode(ThreadPool.MODE_NONE);
179f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin                return innerNextBitmap();
180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            }
181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }, listener);
182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1847817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    @Override
185f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public void pause() {
186f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        synchronized (this) {
187f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            mIsActive = false;
188f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin            notifyAll();
189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        }
190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSource.removeContentListener(mSourceListener);
191f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mReloadTask.cancel();
192f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mReloadTask.waitDone();
193f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mReloadTask = null;
194f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
195f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin
1967817979db0c52ffeacb951625b1e821eba303285Ahbong Chang    @Override
197f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    public synchronized void resume() {
198f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mIsActive = true;
199f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mSource.addContentListener(mSourceListener);
200f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mNeedReload.set(true);
201f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mDataReady = true;
202f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin        mReloadTask = mThreadPool.submit(new ReloadTask());
203f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin    }
204f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin}
205