diff options
author | Chih-Chung Chang <chihchung@google.com> | 2012-05-11 17:55:02 +0800 |
---|---|---|
committer | Chih-Chung Chang <chihchung@google.com> | 2012-05-17 15:34:28 -0700 |
commit | 17df03ce167eab04f51bf6f67d7f8085ac8ae10d (patch) | |
tree | 6343eeb9aa5d85b397ee9e0c3ff2d3a09d3a03a8 /src/com/android/gallery3d/ui | |
parent | 931a10095f72a0334ac5ab5da174e646cb5bacf9 (diff) | |
download | android_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.java | 120 | ||||
-rw-r--r-- | src/com/android/gallery3d/ui/BitmapTileProvider.java | 4 | ||||
-rw-r--r-- | src/com/android/gallery3d/ui/PhotoView.java | 141 | ||||
-rw-r--r-- | src/com/android/gallery3d/ui/PositionController.java | 4 | ||||
-rw-r--r-- | src/com/android/gallery3d/ui/TileImageView.java | 11 | ||||
-rw-r--r-- | src/com/android/gallery3d/ui/TileImageViewAdapter.java | 14 |
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; - } } |