• 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.common.Utils;
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.data.MediaSet;
24 import com.android.gallery3d.ui.GLCanvas;
25 import com.android.gallery3d.ui.GLView;
26 import com.android.gallery3d.ui.SlideshowView;
27 import com.android.gallery3d.ui.SynchronizedHandler;
28 import com.android.gallery3d.util.Future;
29 import com.android.gallery3d.util.FutureListener;
30 
31 import android.app.Activity;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.graphics.Bitmap;
35 import android.os.Bundle;
36 import android.os.Handler;
37 import android.os.Message;
38 import android.os.PowerManager;
39 import android.os.PowerManager.WakeLock;
40 import android.view.MotionEvent;
41 
42 import java.util.ArrayList;
43 import java.util.Random;
44 
45 public class SlideshowPage extends ActivityState {
46     private static final String TAG = "SlideshowPage";
47 
48     public static final String KEY_SET_PATH = "media-set-path";
49     public static final String KEY_ITEM_PATH = "media-item-path";
50     public static final String KEY_PHOTO_INDEX = "photo-index";
51     public static final String KEY_RANDOM_ORDER = "random-order";
52     public static final String KEY_REPEAT = "repeat";
53 
54     private static final long SLIDESHOW_DELAY = 3000; // 3 seconds
55 
56     private static final int MSG_LOAD_NEXT_BITMAP = 1;
57     private static final int MSG_SHOW_PENDING_BITMAP = 2;
58 
59     public static interface Model {
pause()60         public void pause();
resume()61         public void resume();
nextSlide(FutureListener<Slide> listener)62         public Future<Slide> nextSlide(FutureListener<Slide> listener);
63     }
64 
65     public static class Slide {
66         public Bitmap bitmap;
67         public MediaItem item;
68         public int index;
69 
Slide(MediaItem item, int index, Bitmap bitmap)70         public Slide(MediaItem item, int index, Bitmap bitmap) {
71             this.bitmap = bitmap;
72             this.item = item;
73             this.index = index;
74         }
75     }
76 
77     private Handler mHandler;
78     private Model mModel;
79     private SlideshowView mSlideshowView;
80 
81     private Slide mPendingSlide = null;
82     private boolean mIsActive = false;
83     private WakeLock mWakeLock;
84     private Intent mResultIntent = new Intent();
85 
86     private GLView mRootPane = new GLView() {
87         @Override
88         protected void onLayout(
89                 boolean changed, int left, int top, int right, int bottom) {
90             mSlideshowView.layout(0, 0, right - left, bottom - top);
91         }
92 
93         @Override
94         protected boolean onTouch(MotionEvent event) {
95             if (event.getAction() == MotionEvent.ACTION_UP) {
96                 onBackPressed();
97             }
98             return true;
99         }
100 
101         @Override
102         protected void renderBackground(GLCanvas canvas) {
103             canvas.clearBuffer();
104         }
105     };
106 
107     @Override
onCreate(Bundle data, Bundle restoreState)108     public void onCreate(Bundle data, Bundle restoreState) {
109         mFlags |= (FLAG_HIDE_ACTION_BAR | FLAG_HIDE_STATUS_BAR);
110 
111         PowerManager pm = (PowerManager) mActivity.getAndroidContext()
112                 .getSystemService(Context.POWER_SERVICE);
113         mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK
114                 | PowerManager.ON_AFTER_RELEASE, TAG);
115 
116         mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
117             @Override
118             public void handleMessage(Message message) {
119                 switch (message.what) {
120                     case MSG_SHOW_PENDING_BITMAP:
121                         showPendingBitmap();
122                         break;
123                     case MSG_LOAD_NEXT_BITMAP:
124                         loadNextBitmap();
125                         break;
126                     default: throw new AssertionError();
127                 }
128             }
129         };
130         initializeViews();
131         initializeData(data);
132     }
133 
loadNextBitmap()134     private void loadNextBitmap() {
135         mModel.nextSlide(new FutureListener<Slide>() {
136             public void onFutureDone(Future<Slide> future) {
137                 mPendingSlide = future.get();
138                 mHandler.sendEmptyMessage(MSG_SHOW_PENDING_BITMAP);
139             }
140         });
141     }
142 
showPendingBitmap()143     private void showPendingBitmap() {
144         // mPendingBitmap could be null, if
145         //    1.) there is no more items
146         //    2.) mModel is paused
147         Slide slide = mPendingSlide;
148         if (slide == null) {
149             if (mIsActive) {
150                 mActivity.getStateManager().finishState(SlideshowPage.this);
151             }
152             return;
153         }
154 
155         mSlideshowView.next(slide.bitmap, slide.item.getRotation());
156 
157         setStateResult(Activity.RESULT_OK, mResultIntent
158                 .putExtra(KEY_ITEM_PATH, slide.item.getPath().toString())
159                 .putExtra(KEY_PHOTO_INDEX, slide.index));
160         mHandler.sendEmptyMessageDelayed(MSG_LOAD_NEXT_BITMAP,
161                 SLIDESHOW_DELAY);
162     }
163 
164     @Override
onPause()165     public void onPause() {
166         super.onPause();
167         mWakeLock.release();
168         mIsActive = false;
169         mModel.pause();
170         mSlideshowView.release();
171 
172         mHandler.removeMessages(MSG_LOAD_NEXT_BITMAP);
173         mHandler.removeMessages(MSG_SHOW_PENDING_BITMAP);
174     }
175 
176     @Override
onResume()177     public void onResume() {
178         super.onResume();
179         mWakeLock.acquire();
180         mIsActive = true;
181         mModel.resume();
182 
183         if (mPendingSlide != null) {
184             showPendingBitmap();
185         } else {
186             loadNextBitmap();
187         }
188     }
189 
initializeData(Bundle data)190     private void initializeData(Bundle data) {
191         boolean random = data.getBoolean(KEY_RANDOM_ORDER, false);
192 
193         // We only want to show slideshow for images only, not videos.
194         String mediaPath = data.getString(KEY_SET_PATH);
195         mediaPath = FilterUtils.newFilterPath(mediaPath,
196                 FilterUtils.FILTER_IMAGE_ONLY);
197         MediaSet mediaSet = mActivity.getDataManager().getMediaSet(mediaPath);
198 
199         if (random) {
200             boolean repeat = data.getBoolean(KEY_REPEAT);
201             mModel = new SlideshowDataAdapter(
202                     mActivity, new ShuffleSource(mediaSet, repeat), 0);
203             setStateResult(Activity.RESULT_OK,
204                     mResultIntent.putExtra(KEY_PHOTO_INDEX, 0));
205         } else {
206             int index = data.getInt(KEY_PHOTO_INDEX);
207             boolean repeat = data.getBoolean(KEY_REPEAT);
208             mModel = new SlideshowDataAdapter(mActivity,
209                     new SequentialSource(mediaSet, repeat), index);
210             setStateResult(Activity.RESULT_OK,
211                     mResultIntent.putExtra(KEY_PHOTO_INDEX, index));
212         }
213     }
214 
initializeViews()215     private void initializeViews() {
216         mSlideshowView = new SlideshowView();
217         mRootPane.addComponent(mSlideshowView);
218         setContentPane(mRootPane);
219     }
220 
findMediaItem(MediaSet mediaSet, int index)221     private static MediaItem findMediaItem(MediaSet mediaSet, int index) {
222         for (int i = 0, n = mediaSet.getSubMediaSetCount(); i < n; ++i) {
223             MediaSet subset = mediaSet.getSubMediaSet(i);
224             int count = subset.getTotalMediaItemCount();
225             if (index < count) {
226                 return findMediaItem(subset, index);
227             }
228             index -= count;
229         }
230         ArrayList<MediaItem> list = mediaSet.getMediaItem(index, 1);
231         return list.isEmpty() ? null : list.get(0);
232     }
233 
234     private static class ShuffleSource implements SlideshowDataAdapter.SlideshowSource {
235         private static final int RETRY_COUNT = 5;
236         private final MediaSet mMediaSet;
237         private final Random mRandom = new Random();
238         private int mOrder[] = new int[0];
239         private boolean mRepeat;
240         private long mSourceVersion = MediaSet.INVALID_DATA_VERSION;
241         private int mLastIndex = -1;
242 
ShuffleSource(MediaSet mediaSet, boolean repeat)243         public ShuffleSource(MediaSet mediaSet, boolean repeat) {
244             mMediaSet = Utils.checkNotNull(mediaSet);
245             mRepeat = repeat;
246         }
247 
getMediaItem(int index)248         public MediaItem getMediaItem(int index) {
249             if (!mRepeat && index >= mOrder.length) return null;
250             if (mOrder.length == 0) return null;
251             mLastIndex = mOrder[index % mOrder.length];
252             MediaItem item = findMediaItem(mMediaSet, mLastIndex);
253             for (int i = 0; i < RETRY_COUNT && item == null; ++i) {
254                 Log.w(TAG, "fail to find image: " + mLastIndex);
255                 mLastIndex = mRandom.nextInt(mOrder.length);
256                 item = findMediaItem(mMediaSet, mLastIndex);
257             }
258             return item;
259         }
260 
reload()261         public long reload() {
262             long version = mMediaSet.reload();
263             if (version != mSourceVersion) {
264                 mSourceVersion = version;
265                 int count = mMediaSet.getTotalMediaItemCount();
266                 if (count != mOrder.length) generateOrderArray(count);
267             }
268             return version;
269         }
270 
generateOrderArray(int totalCount)271         private void generateOrderArray(int totalCount) {
272             if (mOrder.length != totalCount) {
273                 mOrder = new int[totalCount];
274                 for (int i = 0; i < totalCount; ++i) {
275                     mOrder[i] = i;
276                 }
277             }
278             for (int i = totalCount - 1; i > 0; --i) {
279                 Utils.swap(mOrder, i, mRandom.nextInt(i + 1));
280             }
281             if (mOrder[0] == mLastIndex && totalCount > 1) {
282                 Utils.swap(mOrder, 0, mRandom.nextInt(totalCount - 1) + 1);
283             }
284         }
285 
addContentListener(ContentListener listener)286         public void addContentListener(ContentListener listener) {
287             mMediaSet.addContentListener(listener);
288         }
289 
removeContentListener(ContentListener listener)290         public void removeContentListener(ContentListener listener) {
291             mMediaSet.removeContentListener(listener);
292         }
293     }
294 
295     private static class SequentialSource implements SlideshowDataAdapter.SlideshowSource {
296         private static final int DATA_SIZE = 32;
297 
298         private ArrayList<MediaItem> mData = new ArrayList<MediaItem>();
299         private int mDataStart = 0;
300         private long mDataVersion = MediaObject.INVALID_DATA_VERSION;
301         private final MediaSet mMediaSet;
302         private final boolean mRepeat;
303 
SequentialSource(MediaSet mediaSet, boolean repeat)304         public SequentialSource(MediaSet mediaSet, boolean repeat) {
305             mMediaSet = mediaSet;
306             mRepeat = repeat;
307         }
308 
getMediaItem(int index)309         public MediaItem getMediaItem(int index) {
310             int dataEnd = mDataStart + mData.size();
311 
312             if (mRepeat) {
313                 int count = mMediaSet.getMediaItemCount();
314                 if (count == 0) return null;
315                 index = index % count;
316             }
317             if (index < mDataStart || index >= dataEnd) {
318                 mData = mMediaSet.getMediaItem(index, DATA_SIZE);
319                 mDataStart = index;
320                 dataEnd = index + mData.size();
321             }
322 
323             return (index < mDataStart || index >= dataEnd)
324                     ? null
325                     : mData.get(index - mDataStart);
326         }
327 
reload()328         public long reload() {
329             long version = mMediaSet.reload();
330             if (version != mDataVersion) {
331                 mDataVersion = version;
332                 mData.clear();
333             }
334             return mDataVersion;
335         }
336 
addContentListener(ContentListener listener)337         public void addContentListener(ContentListener listener) {
338             mMediaSet.addContentListener(listener);
339         }
340 
removeContentListener(ContentListener listener)341         public void removeContentListener(ContentListener listener) {
342             mMediaSet.removeContentListener(listener);
343         }
344     }
345 }
346