summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/ui
diff options
context:
space:
mode:
authorChih-Chung Chang <chihchung@google.com>2012-05-11 17:55:02 +0800
committerChih-Chung Chang <chihchung@google.com>2012-05-17 15:34:28 -0700
commit17df03ce167eab04f51bf6f67d7f8085ac8ae10d (patch)
tree6343eeb9aa5d85b397ee9e0c3ff2d3a09d3a03a8 /src/com/android/gallery3d/ui
parent931a10095f72a0334ac5ab5da174e646cb5bacf9 (diff)
downloadandroid_packages_apps_Snap-17df03ce167eab04f51bf6f67d7f8085ac8ae10d.tar.gz
android_packages_apps_Snap-17df03ce167eab04f51bf6f67d7f8085ac8ae10d.tar.bz2
android_packages_apps_Snap-17df03ce167eab04f51bf6f67d7f8085ac8ae10d.zip
Show gray tile for screennails not loaded yet.
Bug: 6452217 Change-Id: Ied9c2e2c91f4ffe218a73ba1a123df92a2aab98a
Diffstat (limited to 'src/com/android/gallery3d/ui')
-rw-r--r--src/com/android/gallery3d/ui/BitmapScreenNail.java120
-rw-r--r--src/com/android/gallery3d/ui/BitmapTileProvider.java4
-rw-r--r--src/com/android/gallery3d/ui/PhotoView.java141
-rw-r--r--src/com/android/gallery3d/ui/PositionController.java4
-rw-r--r--src/com/android/gallery3d/ui/TileImageView.java11
-rw-r--r--src/com/android/gallery3d/ui/TileImageViewAdapter.java14
6 files changed, 176 insertions, 118 deletions
diff --git a/src/com/android/gallery3d/ui/BitmapScreenNail.java b/src/com/android/gallery3d/ui/BitmapScreenNail.java
index 7f654058e..14d3f1919 100644
--- a/src/com/android/gallery3d/ui/BitmapScreenNail.java
+++ b/src/com/android/gallery3d/ui/BitmapScreenNail.java
@@ -20,17 +20,32 @@ import android.graphics.Bitmap;
import android.graphics.RectF;
import android.util.Log;
+import com.android.gallery3d.common.Utils;
import com.android.gallery3d.data.MediaItem;
-// This is a ScreenNail wraps a Bitmap. It also includes the rotation
-// information. The getWidth() and getHeight() methods return the width/height
-// before rotation.
+// This is a ScreenNail wraps a Bitmap. There are some extra functions:
+//
+// - If we need to draw before the bitmap is available, we draw a rectange of
+// placeholder color (gray).
+//
+// - When the the bitmap is available, and we have drawn the placeholder color
+// before, we will do a fade-in animation.
public class BitmapScreenNail implements ScreenNail {
private static final String TAG = "BitmapScreenNail";
- private final int mWidth;
- private final int mHeight;
+ private static final int PLACEHOLDER_COLOR = 0xFF222222;
+ // The duration of the fading animation in milliseconds
+ private static final int DURATION = 180;
+ // These are special values for mAnimationStartTime
+ private static final long ANIMATION_NOT_NEEDED = -1;
+ private static final long ANIMATION_NEEDED = -2;
+ private static final long ANIMATION_DONE = -3;
+
+ private int mWidth;
+ private int mHeight;
private Bitmap mBitmap;
private BitmapTexture mTexture;
+ private FadeInTexture mFadeInTexture;
+ private long mAnimationStartTime = ANIMATION_NOT_NEEDED;
public BitmapScreenNail(Bitmap bitmap) {
mWidth = bitmap.getWidth();
@@ -40,6 +55,56 @@ public class BitmapScreenNail implements ScreenNail {
// actually need it.
}
+ public BitmapScreenNail(int width, int height) {
+ if (width == 0 || height == 0) {
+ width = 640;
+ height = 480;
+ }
+ mWidth = width;
+ mHeight = height;
+ }
+
+ // Combines the two ScreenNails.
+ // Returns the used one and recycle the unused one.
+ public ScreenNail combine(ScreenNail other) {
+ if (other == null) {
+ return this;
+ }
+
+ if (!(other instanceof BitmapScreenNail)) {
+ recycle();
+ return other;
+ }
+
+ // Now both are BitmapScreenNail. Move over the information about width,
+ // height, and Bitmap, then recycle the other.
+ BitmapScreenNail newer = (BitmapScreenNail) other;
+ mWidth = newer.mWidth;
+ mHeight = newer.mHeight;
+ if (newer.mBitmap != null) {
+ if (mBitmap != null) {
+ MediaItem.getThumbPool().recycle(mBitmap);
+ }
+ mBitmap = newer.mBitmap;
+ newer.mBitmap = null;
+
+ if (mTexture != null) {
+ mTexture.recycle();
+ mTexture = null;
+ }
+ }
+
+ newer.recycle();
+ return this;
+ }
+
+ public void updatePlaceholderSize(int width, int height) {
+ if (mBitmap != null) return;
+ if (width == 0 || height == 0) return;
+ mWidth = width;
+ mHeight = height;
+ }
+
@Override
public int getWidth() {
return mWidth;
@@ -68,17 +133,60 @@ public class BitmapScreenNail implements ScreenNail {
@Override
public void draw(GLCanvas canvas, int x, int y, int width, int height) {
+ if (mBitmap == null) {
+ if (mAnimationStartTime == ANIMATION_NOT_NEEDED) {
+ mAnimationStartTime = ANIMATION_NEEDED;
+ }
+ canvas.fillRect(x, y, width, height, PLACEHOLDER_COLOR);
+ return;
+ }
+
if (mTexture == null) {
mTexture = new BitmapTexture(mBitmap);
}
- mTexture.draw(canvas, x, y, width, height);
+
+ if (mAnimationStartTime == ANIMATION_NEEDED) {
+ mAnimationStartTime = now();
+ }
+
+ if (isAnimating()) {
+ canvas.drawMixed(mTexture, PLACEHOLDER_COLOR, getRatio(), x, y,
+ width, height);
+ } else {
+ mTexture.draw(canvas, x, y, width, height);
+ }
}
@Override
public void draw(GLCanvas canvas, RectF source, RectF dest) {
+ if (mBitmap == null) {
+ canvas.fillRect(dest.left, dest.top, dest.width(), dest.height(),
+ PLACEHOLDER_COLOR);
+ return;
+ }
+
if (mTexture == null) {
mTexture = new BitmapTexture(mBitmap);
}
+
canvas.drawTexture(mTexture, source, dest);
}
+
+ public boolean isAnimating() {
+ if (mAnimationStartTime < 0) return false;
+ if (now() - mAnimationStartTime >= DURATION) {
+ mAnimationStartTime = ANIMATION_DONE;
+ return false;
+ }
+ return true;
+ }
+
+ private static long now() {
+ return AnimationTime.get();
+ }
+
+ private float getRatio() {
+ float r = (float)(now() - mAnimationStartTime) / DURATION;
+ return Utils.clamp(1.0f - r, 0.0f, 1.0f);
+ }
}
diff --git a/src/com/android/gallery3d/ui/BitmapTileProvider.java b/src/com/android/gallery3d/ui/BitmapTileProvider.java
index be05b33ab..320118e89 100644
--- a/src/com/android/gallery3d/ui/BitmapTileProvider.java
+++ b/src/com/android/gallery3d/ui/BitmapTileProvider.java
@@ -99,8 +99,4 @@ public class BitmapTileProvider implements TileImageView.Model {
mScreenNail.recycle();
}
}
-
- public boolean isFailedToLoad() {
- return false;
- }
}
diff --git a/src/com/android/gallery3d/ui/PhotoView.java b/src/com/android/gallery3d/ui/PhotoView.java
index 46d7c9362..e7ec3a948 100644
--- a/src/com/android/gallery3d/ui/PhotoView.java
+++ b/src/com/android/gallery3d/ui/PhotoView.java
@@ -76,6 +76,12 @@ public class PhotoView extends GLView {
// Returns true if the item is a Video.
public boolean isVideo(int offset);
+
+ public static final int LOADING_INIT = 0;
+ public static final int LOADING_COMPLETE = 1;
+ public static final int LOADING_FAIL = 2;
+
+ public int getLoadingState(int offset);
}
public interface Listener {
@@ -111,18 +117,10 @@ public class PhotoView extends GLView {
// There are four transitions we need to check if we need to
// lock/unlock. Marked as A to D above and in the code.
- private static final int MSG_SHOW_LOADING = 1;
private static final int MSG_CANCEL_EXTRA_SCALING = 2;
private static final int MSG_SWITCH_FOCUS = 3;
private static final int MSG_CAPTURE_ANIMATION_DONE = 4;
- private static final long DELAY_SHOW_LOADING = 250; // 250ms;
-
- private static final int LOADING_INIT = 0;
- private static final int LOADING_TIMEOUT = 1;
- private static final int LOADING_COMPLETE = 2;
- private static final int LOADING_FAIL = 3;
-
private static final int MOVE_THRESHOLD = 256;
private static final float SWIPE_THRESHOLD = 300f;
@@ -160,12 +158,8 @@ public class PhotoView extends GLView {
private EdgeView mEdgeView;
private Texture mVideoPlayIcon;
- private ProgressSpinner mLoadingSpinner;
-
private SynchronizedHandler mHandler;
- private int mLoadingState = LOADING_COMPLETE;
-
private Point mImageCenter = new Point();
private boolean mCancelExtraScalingPending;
private boolean mFilmMode = false;
@@ -195,7 +189,6 @@ public class PhotoView extends GLView {
Context context = activity.getAndroidContext();
mEdgeView = new EdgeView(context);
addComponent(mEdgeView);
- mLoadingSpinner = new ProgressSpinner(context);
mLoadingText = StringTexture.newInstance(
context.getString(R.string.loading),
DEFAULT_TEXT_SIZE, Color.WHITE);
@@ -249,17 +242,6 @@ public class PhotoView extends GLView {
@Override
public void handleMessage(Message message) {
switch (message.what) {
- case MSG_SHOW_LOADING: {
- if (mLoadingState == LOADING_INIT) {
- // We don't need the opening animation
- mPositionController.setOpenAnimationRect(null);
-
- mLoadingSpinner.startAnimation();
- mLoadingState = LOADING_TIMEOUT;
- invalidate();
- }
- break;
- }
case MSG_CANCEL_EXTRA_SCALING: {
mGestureRecognizer.cancelScale();
mPositionController.setExtraScalingRange(false);
@@ -281,28 +263,6 @@ public class PhotoView extends GLView {
}
};
- private void updateLoadingState() {
- // Possible transitions of mLoadingState:
- // INIT --> TIMEOUT, COMPLETE, FAIL
- // TIMEOUT --> COMPLETE, FAIL, INIT
- // COMPLETE --> INIT
- // FAIL --> INIT
- if (mModel.getLevelCount() != 0 || mModel.getScreenNail() != null) {
- mHandler.removeMessages(MSG_SHOW_LOADING);
- mLoadingState = LOADING_COMPLETE;
- } else if (mModel.isFailedToLoad()) {
- mHandler.removeMessages(MSG_SHOW_LOADING);
- mLoadingState = LOADING_FAIL;
- // We don't want the opening animation after loading failure
- mPositionController.setOpenAnimationRect(null);
- } else if (mLoadingState != LOADING_INIT) {
- mLoadingState = LOADING_INIT;
- mHandler.removeMessages(MSG_SHOW_LOADING);
- mHandler.sendEmptyMessageDelayed(
- MSG_SHOW_LOADING, DELAY_SHOW_LOADING);
- }
- }
-
////////////////////////////////////////////////////////////////////////////
// Data/Image change notifications
////////////////////////////////////////////////////////////////////////////
@@ -427,6 +387,7 @@ public class PhotoView extends GLView {
private boolean mIsCamera;
private boolean mIsPanorama;
private boolean mIsVideo;
+ private int mLoadingState = Model.LOADING_INIT;
private boolean mWasCameraCenter;
public void FullPicture(TileImageView tileView) {
@@ -441,9 +402,9 @@ public class PhotoView extends GLView {
mIsCamera = mModel.isCamera(0);
mIsPanorama = mModel.isPanorama(0);
mIsVideo = mModel.isVideo(0);
+ mLoadingState = mModel.getLoadingState(0);
setScreenNail(mModel.getScreenNail(0));
updateSize(false);
- updateLoadingState();
}
@Override
@@ -466,13 +427,9 @@ public class PhotoView extends GLView {
@Override
public void draw(GLCanvas canvas, Rect r) {
- boolean isCenter = mPositionController.isCenter();
-
- if (mLoadingState == LOADING_COMPLETE) {
- drawTileView(canvas, r);
- }
- renderMessage(canvas, r.centerX(), r.centerY());
+ drawTileView(canvas, r);
+ boolean isCenter = mPositionController.isCenter();
if (mIsCamera) {
boolean full = !mFilmMode && isCenter
&& mPositionController.isAtMinimalScale();
@@ -573,13 +530,18 @@ public class PhotoView extends GLView {
setTileViewPosition(cx, cy, viewW, viewH, imageScale);
PhotoView.super.render(canvas);
- // Draw the play video icon.
- if (mIsVideo) {
- canvas.translate((int) (cx + 0.5f), (int) (cy + 0.5f));
- int s = (int) (scale * Math.min(r.width(), r.height()) + 0.5f);
- drawVideoPlayIcon(canvas, s);
+ // Draw the play video icon and the message.
+ canvas.translate((int) (cx + 0.5f), (int) (cy + 0.5f));
+ int s = (int) (scale * Math.min(r.width(), r.height()) + 0.5f);
+ if (mIsVideo) drawVideoPlayIcon(canvas, s);
+ if (mLoadingState == Model.LOADING_FAIL) {
+ drawLoadingFailMessage(canvas);
}
+ // Draw a debug indicator showing which picture has focus (index ==
+ // 0).
+ //canvas.fillRect(-10, -10, 20, 20, 0x80FF00FF);
+
canvas.restore();
}
@@ -605,33 +567,6 @@ public class PhotoView extends GLView {
}
mTileView.setPosition(x, y, scale, mRotation);
}
-
- private void renderMessage(GLCanvas canvas, int x, int y) {
- // Draw the progress spinner and the text below it
- //
- // (x, y) is where we put the center of the spinner.
- // s is the size of the video play icon, and we use s to layout text
- // because we want to keep the text at the same place when the video
- // play icon is shown instead of the spinner.
- int w = getWidth();
- int h = getHeight();
- int s = Math.min(w, h) / ICON_RATIO;
-
- if (mLoadingState == LOADING_TIMEOUT) {
- StringTexture m = mLoadingText;
- ProgressSpinner p = mLoadingSpinner;
- p.draw(canvas, x - p.getWidth() / 2, y - p.getHeight() / 2);
- m.draw(canvas, x - m.getWidth() / 2, y + s / 2 + 5);
- invalidate(); // we need to keep the spinner rotating
- } else if (mLoadingState == LOADING_FAIL) {
- StringTexture m = mNoThumbnailText;
- m.draw(canvas, x - m.getWidth() / 2, y + s / 2 + 5);
- }
-
- // Draw a debug indicator showing which picture has focus (index ==
- // 0).
- // canvas.fillRect(x - 10, y - 10, 20, 20, 0x80FF00FF);
- }
}
private class ScreenNailPicture implements Picture {
@@ -642,6 +577,7 @@ public class PhotoView extends GLView {
private boolean mIsCamera;
private boolean mIsPanorama;
private boolean mIsVideo;
+ private int mLoadingState = Model.LOADING_INIT;
public ScreenNailPicture(int index) {
mIndex = index;
@@ -652,17 +588,17 @@ public class PhotoView extends GLView {
mIsCamera = mModel.isCamera(mIndex);
mIsPanorama = mModel.isPanorama(mIndex);
mIsVideo = mModel.isVideo(mIndex);
+ mLoadingState = mModel.getLoadingState(mIndex);
setScreenNail(mModel.getScreenNail(mIndex));
}
@Override
public void draw(GLCanvas canvas, Rect r) {
if (mScreenNail == null) {
- // Draw a placeholder rectange if there will be a picture in
- // this position.
+ // Draw a placeholder rectange if there should be a picture in
+ // this position (but somehow there isn't).
if (mIndex >= mPrevBound && mIndex <= mNextBound) {
- canvas.fillRect(r.left, r.top, r.width(), r.height(),
- PLACEHOLDER_COLOR);
+ drawPlaceHolder(canvas, r);
}
return;
}
@@ -703,10 +639,22 @@ public class PhotoView extends GLView {
int drawW = getRotated(mRotation, r.width(), r.height());
int drawH = getRotated(mRotation, r.height(), r.width());
mScreenNail.draw(canvas, -drawW / 2, -drawH / 2, drawW, drawH);
- if (mIsVideo) drawVideoPlayIcon(canvas, Math.min(drawW, drawH));
+ if (isScreenNailAnimating()) {
+ invalidate();
+ }
+ int s = Math.min(drawW, drawH);
+ if (mIsVideo) drawVideoPlayIcon(canvas, s);
+ if (mLoadingState == Model.LOADING_FAIL) {
+ drawLoadingFailMessage(canvas);
+ }
canvas.restore();
}
+ private boolean isScreenNailAnimating() {
+ return (mScreenNail instanceof BitmapScreenNail)
+ && ((BitmapScreenNail) mScreenNail).isAnimating();
+ }
+
@Override
public void setScreenNail(ScreenNail s) {
if (mScreenNail == s) return;
@@ -750,6 +698,11 @@ public class PhotoView extends GLView {
}
}
+ // Draw a gray placeholder in the specified rectangle.
+ private void drawPlaceHolder(GLCanvas canvas, Rect r) {
+ canvas.fillRect(r.left, r.top, r.width(), r.height(), PLACEHOLDER_COLOR);
+ }
+
// Draw the video play icon (in the place where the spinner was)
private void drawVideoPlayIcon(GLCanvas canvas, int side) {
int s = side / ICON_RATIO;
@@ -757,6 +710,12 @@ public class PhotoView extends GLView {
mVideoPlayIcon.draw(canvas, -s / 2, -s / 2, s, s);
}
+ // Draw the "no thumbnail" message
+ private void drawLoadingFailMessage(GLCanvas canvas) {
+ StringTexture m = mNoThumbnailText;
+ m.draw(canvas, -m.getWidth() / 2, -m.getHeight() / 2);
+ }
+
private static int getRotated(int degree, int original, int theother) {
return (degree % 180 == 0) ? original : theother;
}
@@ -1239,7 +1198,7 @@ public class PhotoView extends GLView {
}
mHolding |= HOLD_CAPTURE_ANIMATION;
Message m = mHandler.obtainMessage(MSG_CAPTURE_ANIMATION_DONE, offset, 0);
- mHandler.sendMessageDelayed(m, 800);
+ mHandler.sendMessageDelayed(m, PositionController.CAPTURE_ANIMATION_TIME);
return true;
}
diff --git a/src/com/android/gallery3d/ui/PositionController.java b/src/com/android/gallery3d/ui/PositionController.java
index 9797ce903..226826da7 100644
--- a/src/com/android/gallery3d/ui/PositionController.java
+++ b/src/com/android/gallery3d/ui/PositionController.java
@@ -34,6 +34,8 @@ class PositionController {
public static final int IMAGE_AT_TOP_EDGE = 4;
public static final int IMAGE_AT_BOTTOM_EDGE = 8;
+ public static final int CAPTURE_ANIMATION_TIME = 600;
+
// Special values for animation time.
private static final long NO_ANIMATION = -1;
private static final long LAST_ANIMATION = -2;
@@ -56,7 +58,7 @@ class PositionController {
300, // ANIM_KIND_ZOOM
400, // ANIM_KIND_OPENING
0, // ANIM_KIND_FLING (the duration is calculated dynamically)
- 800, // ANIM_KIND_CAPTURE
+ CAPTURE_ANIMATION_TIME, // ANIM_KIND_CAPTURE
};
// We try to scale up the image to fill the screen. But in order not to
diff --git a/src/com/android/gallery3d/ui/TileImageView.java b/src/com/android/gallery3d/ui/TileImageView.java
index 7ee203dd0..fb0e333a1 100644
--- a/src/com/android/gallery3d/ui/TileImageView.java
+++ b/src/com/android/gallery3d/ui/TileImageView.java
@@ -139,7 +139,6 @@ public class TileImageView extends GLView {
// The method would be called in another thread.
public Bitmap getTile(int level, int x, int y, int tileSize,
int borderSize);
- public boolean isFailedToLoad();
}
public TileImageView(GalleryContext context) {
@@ -407,7 +406,7 @@ public class TileImageView extends GLView {
}
}
try {
- if (level != mLevelCount) {
+ if (level != mLevelCount && !isScreenNailAnimating()) {
if (mScreenNail != null) {
mScreenNail.noDraw();
}
@@ -427,6 +426,9 @@ public class TileImageView extends GLView {
mScreenNail.draw(canvas, mOffsetX, mOffsetY,
Math.round(mImageWidth * mScale),
Math.round(mImageHeight * mScale));
+ if (isScreenNailAnimating()) {
+ invalidate();
+ }
}
} finally {
if (flags != 0) canvas.restore();
@@ -439,6 +441,11 @@ public class TileImageView extends GLView {
}
}
+ private boolean isScreenNailAnimating() {
+ return (mScreenNail instanceof BitmapScreenNail)
+ && ((BitmapScreenNail) mScreenNail).isAnimating();
+ }
+
private void uploadBackgroundTiles(GLCanvas canvas) {
mBackgroundTileUploaded = true;
int n = mActiveTiles.size();
diff --git a/src/com/android/gallery3d/ui/TileImageViewAdapter.java b/src/com/android/gallery3d/ui/TileImageViewAdapter.java
index 0400de6f3..5c9281202 100644
--- a/src/com/android/gallery3d/ui/TileImageViewAdapter.java
+++ b/src/com/android/gallery3d/ui/TileImageViewAdapter.java
@@ -33,7 +33,6 @@ public class TileImageViewAdapter implements TileImageView.Model {
protected int mImageWidth;
protected int mImageHeight;
protected int mLevelCount;
- protected boolean mFailedToLoad;
public TileImageViewAdapter() {
}
@@ -54,7 +53,6 @@ public class TileImageViewAdapter implements TileImageView.Model {
mImageHeight = 0;
mLevelCount = 0;
mRegionDecoder = null;
- mFailedToLoad = false;
}
public synchronized void setScreenNail(Bitmap bitmap, int width, int height) {
@@ -64,7 +62,6 @@ public class TileImageViewAdapter implements TileImageView.Model {
mImageHeight = height;
mRegionDecoder = null;
mLevelCount = 0;
- mFailedToLoad = false;
}
public synchronized void setScreenNail(
@@ -75,7 +72,6 @@ public class TileImageViewAdapter implements TileImageView.Model {
mImageHeight = height;
mRegionDecoder = null;
mLevelCount = 0;
- mFailedToLoad = false;
}
private void updateScreenNail(ScreenNail screenNail, boolean own) {
@@ -91,7 +87,6 @@ public class TileImageViewAdapter implements TileImageView.Model {
mImageWidth = decoder.getWidth();
mImageHeight = decoder.getHeight();
mLevelCount = calculateLevelCount();
- mFailedToLoad = false;
}
private int calculateLevelCount() {
@@ -184,13 +179,4 @@ public class TileImageViewAdapter implements TileImageView.Model {
public int getLevelCount() {
return mLevelCount;
}
-
- public void setFailedToLoad() {
- mFailedToLoad = true;
- }
-
- @Override
- public boolean isFailedToLoad() {
- return mFailedToLoad;
- }
}