am ef50c458: Merge "Add slideshow page." into gingerbread
Merge commit 'ef50c458c9f9e970e51e3363a46015b69ba2aa6d' into gingerbread-plus-aosp
* commit 'ef50c458c9f9e970e51e3363a46015b69ba2aa6d':
Add slideshow page.
diff --git a/new3d/src/com/android/gallery3d/anim/FloatAnimation.java b/new3d/src/com/android/gallery3d/anim/FloatAnimation.java
index 084383f..ef177c9 100644
--- a/new3d/src/com/android/gallery3d/anim/FloatAnimation.java
+++ b/new3d/src/com/android/gallery3d/anim/FloatAnimation.java
@@ -9,6 +9,7 @@
public FloatAnimation(float from, float to, int duration) {
mFrom = from;
mTo = to;
+ mCurrent = from;
setDuration(duration);
}
diff --git a/new3d/src/com/android/gallery3d/app/AlbumSetPage.java b/new3d/src/com/android/gallery3d/app/AlbumSetPage.java
index 92cbbc2..dd78e01 100644
--- a/new3d/src/com/android/gallery3d/app/AlbumSetPage.java
+++ b/new3d/src/com/android/gallery3d/app/AlbumSetPage.java
@@ -25,8 +25,8 @@
import com.android.gallery3d.R;
import com.android.gallery3d.data.MediaSet;
import com.android.gallery3d.ui.AdaptiveBackground;
-import com.android.gallery3d.ui.GLView;
import com.android.gallery3d.ui.AlbumSetView;
+import com.android.gallery3d.ui.GLView;
import com.android.gallery3d.ui.HeadUpDisplay;
import com.android.gallery3d.ui.SelectionManager;
import com.android.gallery3d.ui.SlotView;
@@ -76,6 +76,8 @@
if (!mSelectionManager.isSelectionMode()) {
Bundle data = new Bundle();
data.putInt(AlbumPage.KEY_BUCKET_INDEX, slotIndex);
+ // uncomment the following line to test slideshow mode
+ // mContext.getStateManager().startState(SlideshowPage.class, data);
mContext.getStateManager().startState(AlbumPage.class, data);
} else {
mSelectionManager.selectSlot(slotIndex);
diff --git a/new3d/src/com/android/gallery3d/app/SlideshowDataAdapter.java b/new3d/src/com/android/gallery3d/app/SlideshowDataAdapter.java
new file mode 100644
index 0000000..04b7695
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/app/SlideshowDataAdapter.java
@@ -0,0 +1,191 @@
+/*
+ * 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.graphics.Bitmap;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import com.android.gallery3d.app.SlideshowPage.ModelListener;
+import com.android.gallery3d.data.MediaItem;
+import com.android.gallery3d.data.MediaSet;
+import com.android.gallery3d.data.MediaSet.MediaSetListener;
+import com.android.gallery3d.ui.SynchronizedHandler;
+import com.android.gallery3d.util.Future;
+import com.android.gallery3d.util.FutureListener;
+import com.android.gallery3d.util.Utils;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+
+public class SlideshowDataAdapter implements SlideshowPage.Model {
+ private static final String TAG = "SlideshowDataAdapter";
+
+ private static final int IMAGE_QUEUE_CAPACITY = 3;
+
+ private static final int MSG_LOAD_MEDIA_ITEM = 0;
+ private static final int MSG_FILL_BITMAP = 1;
+
+ private static final int MSG_LOAD_DATA = 2;
+ private static final int MSG_UPDATE_DATA = 3;
+
+ private final MediaSet mSource;
+
+ private int mIndex = 0;
+ private int mSize = 0;
+
+ private LinkedList<Bitmap> mImageQueue = new LinkedList<Bitmap>();
+ private int mImageRequestCount = IMAGE_QUEUE_CAPACITY;
+
+ private Handler mMainHandler;
+ private Handler mDataHandler;
+
+ private SlideshowPage.ModelListener mListener;
+
+ public SlideshowDataAdapter(GalleryContext context, MediaSet source) {
+ mSource = source;
+ mSource.setContentListener(new SourceListener());
+
+ mMainHandler = new SynchronizedHandler(context.getGLRoot()) {
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_UPDATE_DATA:
+ ((ReloadTask) message.obj).updateContent();
+ break;
+ case MSG_FILL_BITMAP:
+ ((LoadNextImageTask) message.obj).fillBitmap();
+ break;
+ default: throw new AssertionError(message.what);
+ }
+ }
+ };
+
+ mDataHandler = new Handler(context.getDataManager().getDataLooper()) {
+ @Override
+ public void handleMessage(Message message) {
+ switch (message.what) {
+ case MSG_LOAD_DATA:
+ ((ReloadTask) message.obj).reloadData();
+ break;
+ case MSG_LOAD_MEDIA_ITEM:
+ ((LoadNextImageTask) message.obj).loadMediaItem();
+ break;
+ default:
+ throw new AssertionError(message.what);
+ }
+ }
+ };
+
+ new ReloadTask().execute();
+ }
+
+ public boolean hasNext() {
+ return !mImageQueue.isEmpty();
+ }
+
+ public Bitmap nextSlideBitmap() {
+ new LoadNextImageTask(mIndex++).execute();
+ return mImageQueue.removeFirst();
+ }
+
+ private class ReloadTask {
+ private int mUpdateSize = -1;
+
+ public void execute() {
+ mDataHandler.sendMessage(
+ mDataHandler.obtainMessage(MSG_LOAD_DATA, this));
+ }
+
+ public void reloadData() {
+ mSource.reload();
+ mUpdateSize = mSource.getMediaItemCount();
+ mMainHandler.sendMessage(
+ mMainHandler.obtainMessage(MSG_UPDATE_DATA, this));
+ }
+
+ public void updateContent() {
+ int size = mSize;
+ mSize = mUpdateSize;
+ if (size == mIndex && size < mUpdateSize && mImageRequestCount > 0) {
+ --mImageRequestCount;
+ new LoadNextImageTask(mIndex++).execute();
+ }
+ }
+ }
+
+ private class LoadNextImageTask implements FutureListener<Bitmap>{
+ private int mItemIndex;
+ private MediaItem mItem;
+ private Bitmap mBitmap;
+
+ public LoadNextImageTask(int index) {
+ mItemIndex = index;
+ }
+
+ public void execute() {
+ mDataHandler.sendMessage(
+ mDataHandler.obtainMessage(MSG_LOAD_MEDIA_ITEM, this));
+ }
+
+ public void loadMediaItem() {
+ ArrayList<MediaItem> list = mSource.getMediaItem(mItemIndex, 1);
+ // If list is empty, assume it is the end of the list
+ if (!list.isEmpty()) {
+ mItem = list.get(0);
+ mItem.requestImage(MediaItem.TYPE_FULL_IMAGE, this);
+ }
+ }
+
+ public void onFutureDone(Future<? extends Bitmap> future) {
+ try {
+ mBitmap = future.get();
+ if (mBitmap != null) {
+ mBitmap = Utils.resizeBitmap(mBitmap, 640);
+ }
+ } catch (Throwable e) {
+ Log.w(TAG, "fail to get bitmap", e);
+ }
+ mMainHandler.sendMessage(
+ mMainHandler.obtainMessage(MSG_FILL_BITMAP, this));
+ }
+
+ public void fillBitmap() {
+ if (mBitmap != null) {
+ mImageQueue.addLast(mBitmap);
+ if (mImageQueue.size() == 1 && mListener != null) {
+ mListener.onContentChanged();
+ }
+ }
+ if (mImageRequestCount > 0 && mIndex < mSize) {
+ --mImageRequestCount;
+ new LoadNextImageTask(mIndex++).execute();
+ }
+ }
+ }
+
+ private class SourceListener implements MediaSetListener {
+ public void onContentDirty() {
+ new ReloadTask().execute();
+ }
+ }
+
+ public void setListener(ModelListener listener) {
+ mListener = listener;
+ }
+}
diff --git a/new3d/src/com/android/gallery3d/app/SlideshowPage.java b/new3d/src/com/android/gallery3d/app/SlideshowPage.java
new file mode 100644
index 0000000..7e7053f
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/app/SlideshowPage.java
@@ -0,0 +1,127 @@
+/*
+ * 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.graphics.Bitmap;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+
+import com.android.gallery3d.data.MediaSet;
+import com.android.gallery3d.ui.GLCanvas;
+import com.android.gallery3d.ui.GLView;
+import com.android.gallery3d.ui.SlideshowView;
+import com.android.gallery3d.ui.SynchronizedHandler;
+import com.android.gallery3d.util.Utils;
+
+public class SlideshowPage extends ActivityState {
+ private static final String TAG = "SlideshowPage";
+
+ public static final String KEY_BUCKET_INDEX = "keyBucketIndex";
+
+ private static final long SLIDESHOW_DELAY = 3000; // 2 seconds
+ private static final int MSG_SHOW_NEXT_SLIDE = 1;
+
+ public static interface Model {
+ public boolean hasNext();
+ public Bitmap nextSlideBitmap();
+ public void setListener(ModelListener listener);
+ }
+
+ public static interface ModelListener {
+ public void onContentChanged();
+ }
+
+ private Handler mHandler;
+ private Model mModel;
+ private SlideshowView mSlideshowView;
+ private boolean mSlideshowActive = false;
+
+ private 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 void renderBackground(GLCanvas canvas) {
+ canvas.clearBuffer();
+ }
+ };
+
+ @Override
+ public void onCreate(Bundle data, Bundle restoreState) {
+ mHandler = new SynchronizedHandler(mContext.getGLRoot()) {
+ @Override
+ public void handleMessage(Message message) {
+ Utils.Assert(message.what == MSG_SHOW_NEXT_SLIDE);
+ showNextSlide();
+ }
+ };
+ initializeViews();
+ intializeData(data);
+ }
+
+ private void showNextSlide() {
+ if (!mModel.hasNext()) {
+ mSlideshowActive = false;
+ return;
+ }
+ mSlideshowActive = true;
+ mSlideshowView.next(mModel.nextSlideBitmap());
+ mHandler.sendEmptyMessageDelayed(MSG_SHOW_NEXT_SLIDE, SLIDESHOW_DELAY);
+ }
+
+ @Override
+ public void onPause() {
+ mHandler.removeMessages(MSG_SHOW_NEXT_SLIDE);
+ }
+
+ @Override
+ public void onResume() {
+ mHandler.sendEmptyMessage(MSG_SHOW_NEXT_SLIDE);
+ }
+
+ private void initializeViews() {
+ mSlideshowView = new SlideshowView();
+ mRootPane.addComponent(mSlideshowView);
+ setContentPane(mRootPane);
+ }
+
+ private void intializeData(Bundle data) {
+ int bucketIndex = data.getInt(KEY_BUCKET_INDEX);
+ MediaSet mediaSet = mContext.getDataManager()
+ .getRootSet().getSubMediaSet(bucketIndex);
+ SlideshowDataAdapter adapter =
+ new SlideshowDataAdapter(mContext, mediaSet);
+ setModel(adapter);
+ }
+
+ public void setModel(Model source) {
+ mHandler.removeMessages(MSG_SHOW_NEXT_SLIDE);
+ mModel = source;
+ mModel.setListener(new MyModelListener());
+ showNextSlide();
+ }
+
+ private class MyModelListener implements ModelListener {
+ public void onContentChanged() {
+ if (!mSlideshowActive) showNextSlide();
+ }
+ }
+}
diff --git a/new3d/src/com/android/gallery3d/ui/BitmapTexture.java b/new3d/src/com/android/gallery3d/ui/BitmapTexture.java
index 261b20b..16df82d 100644
--- a/new3d/src/com/android/gallery3d/ui/BitmapTexture.java
+++ b/new3d/src/com/android/gallery3d/ui/BitmapTexture.java
@@ -42,4 +42,8 @@
protected Bitmap onGetBitmap() {
return mContentBitmap;
}
+
+ public Bitmap getBitmap() {
+ return mContentBitmap;
+ }
}
diff --git a/new3d/src/com/android/gallery3d/ui/SlideshowView.java b/new3d/src/com/android/gallery3d/ui/SlideshowView.java
new file mode 100644
index 0000000..0c830d2
--- /dev/null
+++ b/new3d/src/com/android/gallery3d/ui/SlideshowView.java
@@ -0,0 +1,162 @@
+/*
+ * 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.ui;
+
+import android.graphics.Bitmap;
+import android.graphics.PointF;
+
+import com.android.gallery3d.anim.CanvasAnimation;
+import com.android.gallery3d.anim.FloatAnimation;
+
+import java.util.Random;
+
+import javax.microedition.khronos.opengles.GL11;
+
+public class SlideshowView extends GLView {
+ private static final String TAG = "SlideshowView";
+
+ private static final int SLIDESHOW_DURATION = 3500;
+ private static final int TRANSITION_DURATION = 1000;
+
+ private static final float SCALE_SPEED = 0.20f ;
+ private static final float MOVE_SPEED = SCALE_SPEED;
+
+ private BitmapTexture mCurrentTexture;
+ private SlideshowAnimation mCurrentAnimation;
+
+ private BitmapTexture mPrevTexture;
+ private SlideshowAnimation mPrevAnimation;
+
+ private final FloatAnimation mTransitionAnimation =
+ new FloatAnimation(0, 1, TRANSITION_DURATION);
+
+ private Random mRandom = new Random();
+
+ public static interface SlideshowListener {
+ public void onSlideshowEnd();
+ }
+
+ public SlideshowView() {
+ }
+
+ public void next(Bitmap bitmap) {
+
+ mTransitionAnimation.start();
+
+ if (mPrevTexture != null) {
+ mPrevTexture.getBitmap().recycle();
+ mPrevTexture.recycle();
+ }
+
+ mPrevTexture = mCurrentTexture;
+ mPrevAnimation = mCurrentAnimation;
+
+ mCurrentTexture = new BitmapTexture(bitmap);
+ mCurrentAnimation = new SlideshowAnimation(mCurrentTexture, mRandom);
+ mCurrentAnimation.start();
+
+ invalidate();
+ }
+
+ public void close() {
+ if (mPrevTexture != null) {
+ mPrevTexture.getBitmap().recycle();
+ mPrevTexture.recycle();
+ mPrevTexture = null;
+ }
+ if (mCurrentTexture != null) {
+ mCurrentTexture.getBitmap().recycle();
+ mCurrentTexture.recycle();
+ mCurrentTexture = null;
+ }
+ }
+
+ @Override
+ protected void render(GLCanvas canvas) {
+ long currentTimeMillis = canvas.currentAnimationTimeMillis();
+ boolean requestRender = mTransitionAnimation.calculate(currentTimeMillis);
+ GL11 gl = canvas.getGLInstance();
+ gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE);
+ float alpha = mPrevTexture == null ? 1f : mTransitionAnimation.get();
+
+ if (mPrevTexture != null && alpha != 1f) {
+ requestRender |= mPrevAnimation.calculate(currentTimeMillis);
+ canvas.save(GLCanvas.SAVE_FLAG_ALPHA
+ | mPrevAnimation.getCanvasSaveFlags());
+ canvas.setAlpha(1f - alpha);
+ mPrevAnimation.apply(canvas);
+ mPrevTexture.draw(canvas, -mPrevTexture.getWidth() / 2,
+ -mPrevTexture.getHeight() / 2);
+ canvas.restore();
+ }
+ if (mCurrentTexture != null) {
+ requestRender |= mCurrentAnimation.calculate(currentTimeMillis);
+ canvas.save(GLCanvas.SAVE_FLAG_ALPHA
+ | mCurrentAnimation.getCanvasSaveFlags());
+ canvas.setAlpha(alpha);
+ mCurrentAnimation.apply(canvas);
+ mCurrentTexture.draw(canvas, -mCurrentTexture.getWidth() / 2,
+ -mCurrentTexture.getHeight() / 2);
+ canvas.restore();
+ }
+ if (requestRender) invalidate();
+ gl.glBlendFunc(GL11.GL_ONE, GL11.GL_ONE_MINUS_SRC_ALPHA);
+ }
+
+ private class SlideshowAnimation extends CanvasAnimation {
+ private final int mWidth;
+ private final int mHeight;
+
+ private final PointF mMovingVector;
+ private float mProgress;
+
+ public SlideshowAnimation(Texture texture, Random random) {
+ mWidth = texture.getWidth();
+ mHeight = texture.getHeight();
+ mMovingVector = new PointF(
+ MOVE_SPEED * mWidth * (random.nextFloat() - 0.5f),
+ MOVE_SPEED * mHeight * (random.nextFloat() - 0.5f));
+ setDuration(SLIDESHOW_DURATION);
+ }
+
+ @Override
+ public void apply(GLCanvas canvas) {
+ int viewWidth = getWidth();
+ int viewHeight = getHeight();
+
+ float initScale = Math.min(2f, Math.min((float)
+ viewWidth / mWidth, (float) viewHeight / mHeight));
+ float scale = initScale * (1 + SCALE_SPEED * mProgress);
+
+ float centerX = viewWidth / 2 + mMovingVector.x * mProgress;
+ float centerY = viewHeight / 2 + mMovingVector.y * mProgress;
+
+ canvas.translate(centerX, centerY, 0);
+ canvas.scale(scale, scale, 0);
+ }
+
+ @Override
+ public int getCanvasSaveFlags() {
+ return GLCanvas.SAVE_FLAG_MATRIX;
+ }
+
+ @Override
+ protected void onCalculate(float progress) {
+ mProgress = progress;
+ }
+ }
+}
diff --git a/new3d/src/com/android/gallery3d/ui/UploadedTexture.java b/new3d/src/com/android/gallery3d/ui/UploadedTexture.java
index f9248e5..1be54a6 100644
--- a/new3d/src/com/android/gallery3d/ui/UploadedTexture.java
+++ b/new3d/src/com/android/gallery3d/ui/UploadedTexture.java
@@ -16,11 +16,11 @@
package com.android.gallery3d.ui;
-import com.android.gallery3d.util.Utils;
-
import android.graphics.Bitmap;
import android.opengl.GLUtils;
+import com.android.gallery3d.util.Utils;
+
import javax.microedition.khronos.opengles.GL11;
import javax.microedition.khronos.opengles.GL11Ext;
diff --git a/new3d/src/com/android/gallery3d/util/Utils.java b/new3d/src/com/android/gallery3d/util/Utils.java
index 686f3c6..cac7d3f 100644
--- a/new3d/src/com/android/gallery3d/util/Utils.java
+++ b/new3d/src/com/android/gallery3d/util/Utils.java
@@ -91,6 +91,24 @@
return bitmap;
}
+ public static final Bitmap resizeBitmap(Bitmap bitmap, int maxSize) {
+ int srcWidth = bitmap.getWidth();
+ int srcHeight = bitmap.getHeight();
+ float scale = Math.min(
+ (float) maxSize / srcWidth, (float) maxSize / srcHeight);
+ if (scale >= 1.0f) return bitmap;
+
+ int width = Math.round(srcWidth * scale);
+ int height = Math.round(srcHeight * scale);
+ Bitmap target = Bitmap.createBitmap(width, height,
+ bitmap.hasAlpha() ? Config.ARGB_8888 : Config.RGB_565);
+ Canvas canvas = new Canvas(target);
+ canvas.scale(scale, scale);
+ canvas.drawBitmap(bitmap, 0, 0, null);
+ bitmap.recycle();
+ return target;
+ }
+
// Throws AssertionError if the input is false.
public static void Assert(boolean cond) {
if (!cond) {