summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/app/SlideshowPage.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/gallery3d/app/SlideshowPage.java')
-rw-r--r--src/com/android/gallery3d/app/SlideshowPage.java366
1 files changed, 366 insertions, 0 deletions
diff --git a/src/com/android/gallery3d/app/SlideshowPage.java b/src/com/android/gallery3d/app/SlideshowPage.java
new file mode 100644
index 000000000..174058dc8
--- /dev/null
+++ b/src/com/android/gallery3d/app/SlideshowPage.java
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.gallery3d.app;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.view.MotionEvent;
+
+import com.android.gallery3d.R;
+import com.android.gallery3d.common.Utils;
+import com.android.gallery3d.data.ContentListener;
+import com.android.gallery3d.data.MediaItem;
+import com.android.gallery3d.data.MediaObject;
+import com.android.gallery3d.data.MediaSet;
+import com.android.gallery3d.data.Path;
+import com.android.gallery3d.glrenderer.GLCanvas;
+import com.android.gallery3d.ui.GLView;
+import com.android.gallery3d.ui.SlideshowView;
+import com.android.gallery3d.ui.SynchronizedHandler;
+import com.android.gallery3d.util.Future;
+import com.android.gallery3d.util.FutureListener;
+
+import java.util.ArrayList;
+import java.util.Random;
+
+public class SlideshowPage extends ActivityState {
+ private static final String TAG = "SlideshowPage";
+
+ public static final String KEY_SET_PATH = "media-set-path";
+ public static final String KEY_ITEM_PATH = "media-item-path";
+ public static final String KEY_PHOTO_INDEX = "photo-index";
+ public static final String KEY_RANDOM_ORDER = "random-order";
+ public static final String KEY_REPEAT = "repeat";
+ public static final String KEY_DREAM = "dream";
+
+ private static final long SLIDESHOW_DELAY = 3000; // 3 seconds
+
+ private static final int MSG_LOAD_NEXT_BITMAP = 1;
+ private static final int MSG_SHOW_PENDING_BITMAP = 2;
+
+ public static interface Model {
+ public void pause();
+
+ public void resume();
+
+ public Future<Slide> nextSlide(FutureListener<Slide> listener);
+ }
+
+ public static class Slide {
+ public Bitmap bitmap;
+ public MediaItem item;
+ public int index;
+
+ public Slide(MediaItem item, int index, Bitmap bitmap) {
+ this.bitmap = bitmap;
+ this.item = item;
+ this.index = index;
+ }
+ }
+
+ private Handler mHandler;
+ private Model mModel;
+ private SlideshowView mSlideshowView;
+
+ private Slide mPendingSlide = null;
+ private boolean mIsActive = false;
+ private final Intent mResultIntent = new Intent();
+
+ @Override
+ protected int getBackgroundColorId() {
+ return R.color.slideshow_background;
+ }
+
+ private final GLView mRootPane = new GLView() {
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ mSlideshowView.layout(0, 0, right - left, bottom - top);
+ }
+
+ @Override
+ protected boolean onTouch(MotionEvent event) {
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ onBackPressed();
+ }
+ return true;
+ }
+
+ @Override
+ protected void renderBackground(GLCanvas canvas) {
+ canvas.clearBuffer(getBackgroundColor());
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle data, Bundle restoreState) {
+ super.onCreate(data, restoreState);
+ mFlags |= (FLAG_HIDE_ACTION_BAR | FLAG_HIDE_STATUS_BAR);
+ if (data.getBoolean(KEY_DREAM)) {
+ // Dream screensaver only keeps screen on for plugged devices.
+ mFlags |= FLAG_SCREEN_ON_WHEN_PLUGGED | FLAG_SHOW_WHEN_LOCKED;
+ } else {
+ // User-initiated slideshow would always keep screen on.
+ mFlags |= FLAG_SCREEN_ON_ALWAYS;
+ }
+
+ mHandler = new SynchronizedHandler(mActivity.getGLRoot()) {
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_SHOW_PENDING_BITMAP:
+ showPendingBitmap();
+ break;
+ case MSG_LOAD_NEXT_BITMAP:
+ loadNextBitmap();
+ break;
+ default: throw new AssertionError();
+ }
+ }
+ };
+ initializeViews();
+ initializeData(data);
+ }
+
+ private void loadNextBitmap() {
+ mModel.nextSlide(new FutureListener<Slide>() {
+ @Override
+ public void onFutureDone(Future<Slide> future) {
+ mPendingSlide = future.get();
+ mHandler.sendEmptyMessage(MSG_SHOW_PENDING_BITMAP);
+ }
+ });
+ }
+
+ private void showPendingBitmap() {
+ // mPendingBitmap could be null, if
+ // 1.) there is no more items
+ // 2.) mModel is paused
+ Slide slide = mPendingSlide;
+ if (slide == null) {
+ if (mIsActive) {
+ mActivity.getStateManager().finishState(SlideshowPage.this);
+ }
+ return;
+ }
+
+ mSlideshowView.next(slide.bitmap, slide.item.getRotation());
+
+ setStateResult(Activity.RESULT_OK, mResultIntent
+ .putExtra(KEY_ITEM_PATH, slide.item.getPath().toString())
+ .putExtra(KEY_PHOTO_INDEX, slide.index));
+ mHandler.sendEmptyMessageDelayed(MSG_LOAD_NEXT_BITMAP, SLIDESHOW_DELAY);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mIsActive = false;
+ mModel.pause();
+ mSlideshowView.release();
+
+ mHandler.removeMessages(MSG_LOAD_NEXT_BITMAP);
+ mHandler.removeMessages(MSG_SHOW_PENDING_BITMAP);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mIsActive = true;
+ mModel.resume();
+
+ if (mPendingSlide != null) {
+ showPendingBitmap();
+ } else {
+ loadNextBitmap();
+ }
+ }
+
+ private void initializeData(Bundle data) {
+ boolean random = data.getBoolean(KEY_RANDOM_ORDER, false);
+
+ // We only want to show slideshow for images only, not videos.
+ String mediaPath = data.getString(KEY_SET_PATH);
+ mediaPath = FilterUtils.newFilterPath(mediaPath, FilterUtils.FILTER_IMAGE_ONLY);
+ MediaSet mediaSet = mActivity.getDataManager().getMediaSet(mediaPath);
+
+ if (random) {
+ boolean repeat = data.getBoolean(KEY_REPEAT);
+ mModel = new SlideshowDataAdapter(mActivity,
+ new ShuffleSource(mediaSet, repeat), 0, null);
+ setStateResult(Activity.RESULT_OK, mResultIntent.putExtra(KEY_PHOTO_INDEX, 0));
+ } else {
+ int index = data.getInt(KEY_PHOTO_INDEX);
+ String itemPath = data.getString(KEY_ITEM_PATH);
+ Path path = itemPath != null ? Path.fromString(itemPath) : null;
+ boolean repeat = data.getBoolean(KEY_REPEAT);
+ mModel = new SlideshowDataAdapter(mActivity, new SequentialSource(mediaSet, repeat),
+ index, path);
+ setStateResult(Activity.RESULT_OK, mResultIntent.putExtra(KEY_PHOTO_INDEX, index));
+ }
+ }
+
+ private void initializeViews() {
+ mSlideshowView = new SlideshowView();
+ mRootPane.addComponent(mSlideshowView);
+ setContentPane(mRootPane);
+ }
+
+ private static MediaItem findMediaItem(MediaSet mediaSet, int index) {
+ for (int i = 0, n = mediaSet.getSubMediaSetCount(); i < n; ++i) {
+ MediaSet subset = mediaSet.getSubMediaSet(i);
+ int count = subset.getTotalMediaItemCount();
+ if (index < count) {
+ return findMediaItem(subset, index);
+ }
+ index -= count;
+ }
+ ArrayList<MediaItem> list = mediaSet.getMediaItem(index, 1);
+ return list.isEmpty() ? null : list.get(0);
+ }
+
+ private static class ShuffleSource implements SlideshowDataAdapter.SlideshowSource {
+ private static final int RETRY_COUNT = 5;
+ private final MediaSet mMediaSet;
+ private final Random mRandom = new Random();
+ private int mOrder[] = new int[0];
+ private final boolean mRepeat;
+ private long mSourceVersion = MediaSet.INVALID_DATA_VERSION;
+ private int mLastIndex = -1;
+
+ public ShuffleSource(MediaSet mediaSet, boolean repeat) {
+ mMediaSet = Utils.checkNotNull(mediaSet);
+ mRepeat = repeat;
+ }
+
+ @Override
+ public int findItemIndex(Path path, int hint) {
+ return hint;
+ }
+
+ @Override
+ public MediaItem getMediaItem(int index) {
+ if (!mRepeat && index >= mOrder.length) return null;
+ if (mOrder.length == 0) return null;
+ mLastIndex = mOrder[index % mOrder.length];
+ MediaItem item = findMediaItem(mMediaSet, mLastIndex);
+ for (int i = 0; i < RETRY_COUNT && item == null; ++i) {
+ Log.w(TAG, "fail to find image: " + mLastIndex);
+ mLastIndex = mRandom.nextInt(mOrder.length);
+ item = findMediaItem(mMediaSet, mLastIndex);
+ }
+ return item;
+ }
+
+ @Override
+ public long reload() {
+ long version = mMediaSet.reload();
+ if (version != mSourceVersion) {
+ mSourceVersion = version;
+ int count = mMediaSet.getTotalMediaItemCount();
+ if (count != mOrder.length) generateOrderArray(count);
+ }
+ return version;
+ }
+
+ private void generateOrderArray(int totalCount) {
+ if (mOrder.length != totalCount) {
+ mOrder = new int[totalCount];
+ for (int i = 0; i < totalCount; ++i) {
+ mOrder[i] = i;
+ }
+ }
+ for (int i = totalCount - 1; i > 0; --i) {
+ Utils.swap(mOrder, i, mRandom.nextInt(i + 1));
+ }
+ if (mOrder[0] == mLastIndex && totalCount > 1) {
+ Utils.swap(mOrder, 0, mRandom.nextInt(totalCount - 1) + 1);
+ }
+ }
+
+ @Override
+ public void addContentListener(ContentListener listener) {
+ mMediaSet.addContentListener(listener);
+ }
+
+ @Override
+ public void removeContentListener(ContentListener listener) {
+ mMediaSet.removeContentListener(listener);
+ }
+ }
+
+ private static class SequentialSource implements SlideshowDataAdapter.SlideshowSource {
+ private static final int DATA_SIZE = 32;
+
+ private ArrayList<MediaItem> mData = new ArrayList<MediaItem>();
+ private int mDataStart = 0;
+ private long mDataVersion = MediaObject.INVALID_DATA_VERSION;
+ private final MediaSet mMediaSet;
+ private final boolean mRepeat;
+
+ public SequentialSource(MediaSet mediaSet, boolean repeat) {
+ mMediaSet = mediaSet;
+ mRepeat = repeat;
+ }
+
+ @Override
+ public int findItemIndex(Path path, int hint) {
+ return mMediaSet.getIndexOfItem(path, hint);
+ }
+
+ @Override
+ public MediaItem getMediaItem(int index) {
+ int dataEnd = mDataStart + mData.size();
+
+ if (mRepeat) {
+ int count = mMediaSet.getMediaItemCount();
+ if (count == 0) return null;
+ index = index % count;
+ }
+ if (index < mDataStart || index >= dataEnd) {
+ mData = mMediaSet.getMediaItem(index, DATA_SIZE);
+ mDataStart = index;
+ dataEnd = index + mData.size();
+ }
+
+ return (index < mDataStart || index >= dataEnd) ? null : mData.get(index - mDataStart);
+ }
+
+ @Override
+ public long reload() {
+ long version = mMediaSet.reload();
+ if (version != mDataVersion) {
+ mDataVersion = version;
+ mData.clear();
+ }
+ return mDataVersion;
+ }
+
+ @Override
+ public void addContentListener(ContentListener listener) {
+ mMediaSet.addContentListener(listener);
+ }
+
+ @Override
+ public void removeContentListener(ContentListener listener) {
+ mMediaSet.removeContentListener(listener);
+ }
+ }
+}