diff options
Diffstat (limited to 'src/com/android/gallery3d/ui/SlotView.java')
-rw-r--r-- | src/com/android/gallery3d/ui/SlotView.java | 788 |
1 files changed, 0 insertions, 788 deletions
diff --git a/src/com/android/gallery3d/ui/SlotView.java b/src/com/android/gallery3d/ui/SlotView.java deleted file mode 100644 index bd0ffdc15..000000000 --- a/src/com/android/gallery3d/ui/SlotView.java +++ /dev/null @@ -1,788 +0,0 @@ -/* - * 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.Rect; -import android.os.Handler; -import android.view.GestureDetector; -import android.view.MotionEvent; -import android.view.animation.DecelerateInterpolator; - -import com.android.gallery3d.anim.Animation; -import com.android.gallery3d.app.AbstractGalleryActivity; -import com.android.gallery3d.common.Utils; -import com.android.gallery3d.glrenderer.GLCanvas; - -public class SlotView extends GLView { - @SuppressWarnings("unused") - private static final String TAG = "SlotView"; - - private static final boolean WIDE = true; - private static final int INDEX_NONE = -1; - - public static final int RENDER_MORE_PASS = 1; - public static final int RENDER_MORE_FRAME = 2; - - public interface Listener { - public void onDown(int index); - public void onUp(boolean followedByLongPress); - public void onSingleTapUp(int index); - public void onLongTap(int index); - public void onScrollPositionChanged(int position, int total); - } - - public static class SimpleListener implements Listener { - @Override public void onDown(int index) {} - @Override public void onUp(boolean followedByLongPress) {} - @Override public void onSingleTapUp(int index) {} - @Override public void onLongTap(int index) {} - @Override public void onScrollPositionChanged(int position, int total) {} - } - - public static interface SlotRenderer { - public void prepareDrawing(); - public void onVisibleRangeChanged(int visibleStart, int visibleEnd); - public void onSlotSizeChanged(int width, int height); - public int renderSlot(GLCanvas canvas, int index, int pass, int width, int height); - } - - private final GestureDetector mGestureDetector; - private final ScrollerHelper mScroller; - private final Paper mPaper = new Paper(); - - private Listener mListener; - private UserInteractionListener mUIListener; - - private boolean mMoreAnimation = false; - private SlotAnimation mAnimation = null; - private final Layout mLayout = new Layout(); - private int mStartIndex = INDEX_NONE; - - // whether the down action happened while the view is scrolling. - private boolean mDownInScrolling; - private int mOverscrollEffect = OVERSCROLL_3D; - private final Handler mHandler; - - private SlotRenderer mRenderer; - - private int[] mRequestRenderSlots = new int[16]; - - public static final int OVERSCROLL_3D = 0; - public static final int OVERSCROLL_SYSTEM = 1; - public static final int OVERSCROLL_NONE = 2; - - // to prevent allocating memory - private final Rect mTempRect = new Rect(); - - public SlotView(AbstractGalleryActivity activity, Spec spec) { - mGestureDetector = new GestureDetector(activity, new MyGestureListener()); - mScroller = new ScrollerHelper(activity); - mHandler = new SynchronizedHandler(activity.getGLRoot()); - setSlotSpec(spec); - } - - public void setSlotRenderer(SlotRenderer slotDrawer) { - mRenderer = slotDrawer; - if (mRenderer != null) { - mRenderer.onSlotSizeChanged(mLayout.mSlotWidth, mLayout.mSlotHeight); - mRenderer.onVisibleRangeChanged(getVisibleStart(), getVisibleEnd()); - } - } - - public void setCenterIndex(int index) { - int slotCount = mLayout.mSlotCount; - if (index < 0 || index >= slotCount) { - return; - } - Rect rect = mLayout.getSlotRect(index, mTempRect); - int position = WIDE - ? (rect.left + rect.right - getWidth()) / 2 - : (rect.top + rect.bottom - getHeight()) / 2; - setScrollPosition(position); - } - - public void makeSlotVisible(int index) { - Rect rect = mLayout.getSlotRect(index, mTempRect); - int visibleBegin = WIDE ? mScrollX : mScrollY; - int visibleLength = WIDE ? getWidth() : getHeight(); - int visibleEnd = visibleBegin + visibleLength; - int slotBegin = WIDE ? rect.left : rect.top; - int slotEnd = WIDE ? rect.right : rect.bottom; - - int position = visibleBegin; - if (visibleLength < slotEnd - slotBegin) { - position = visibleBegin; - } else if (slotBegin < visibleBegin) { - position = slotBegin; - } else if (slotEnd > visibleEnd) { - position = slotEnd - visibleLength; - } - - setScrollPosition(position); - } - - public void setScrollPosition(int position) { - position = Utils.clamp(position, 0, mLayout.getScrollLimit()); - mScroller.setPosition(position); - updateScrollPosition(position, false); - } - - public void setSlotSpec(Spec spec) { - mLayout.setSlotSpec(spec); - } - - @Override - public void addComponent(GLView view) { - throw new UnsupportedOperationException(); - } - - @Override - protected void onLayout(boolean changeSize, int l, int t, int r, int b) { - if (!changeSize) return; - - // Make sure we are still at a resonable scroll position after the size - // is changed (like orientation change). We choose to keep the center - // visible slot still visible. This is arbitrary but reasonable. - int visibleIndex = - (mLayout.getVisibleStart() + mLayout.getVisibleEnd()) / 2; - mLayout.setSize(r - l, b - t); - makeSlotVisible(visibleIndex); - if (mOverscrollEffect == OVERSCROLL_3D) { - mPaper.setSize(r - l, b - t); - } - } - - public void startScatteringAnimation(RelativePosition position) { - mAnimation = new ScatteringAnimation(position); - mAnimation.start(); - if (mLayout.mSlotCount != 0) invalidate(); - } - - public void startRisingAnimation() { - mAnimation = new RisingAnimation(); - mAnimation.start(); - if (mLayout.mSlotCount != 0) invalidate(); - } - - private void updateScrollPosition(int position, boolean force) { - if (!force && (WIDE ? position == mScrollX : position == mScrollY)) return; - if (WIDE) { - mScrollX = position; - } else { - mScrollY = position; - } - mLayout.setScrollPosition(position); - onScrollPositionChanged(position); - } - - protected void onScrollPositionChanged(int newPosition) { - int limit = mLayout.getScrollLimit(); - mListener.onScrollPositionChanged(newPosition, limit); - } - - public Rect getSlotRect(int slotIndex) { - return mLayout.getSlotRect(slotIndex, new Rect()); - } - - @Override - protected boolean onTouch(MotionEvent event) { - if (mUIListener != null) mUIListener.onUserInteraction(); - mGestureDetector.onTouchEvent(event); - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - mDownInScrolling = !mScroller.isFinished(); - mScroller.forceFinished(); - break; - case MotionEvent.ACTION_UP: - mPaper.onRelease(); - invalidate(); - break; - } - return true; - } - - public void setListener(Listener listener) { - mListener = listener; - } - - public void setUserInteractionListener(UserInteractionListener listener) { - mUIListener = listener; - } - - public void setOverscrollEffect(int kind) { - mOverscrollEffect = kind; - mScroller.setOverfling(kind == OVERSCROLL_SYSTEM); - } - - private static int[] expandIntArray(int array[], int capacity) { - while (array.length < capacity) { - array = new int[array.length * 2]; - } - return array; - } - - @Override - protected void render(GLCanvas canvas) { - super.render(canvas); - - if (mRenderer == null) return; - mRenderer.prepareDrawing(); - - long animTime = AnimationTime.get(); - boolean more = mScroller.advanceAnimation(animTime); - more |= mLayout.advanceAnimation(animTime); - int oldX = mScrollX; - updateScrollPosition(mScroller.getPosition(), false); - - boolean paperActive = false; - if (mOverscrollEffect == OVERSCROLL_3D) { - // Check if an edge is reached and notify mPaper if so. - int newX = mScrollX; - int limit = mLayout.getScrollLimit(); - if (oldX > 0 && newX == 0 || oldX < limit && newX == limit) { - float v = mScroller.getCurrVelocity(); - if (newX == limit) v = -v; - - // I don't know why, but getCurrVelocity() can return NaN. - if (!Float.isNaN(v)) { - mPaper.edgeReached(v); - } - } - paperActive = mPaper.advanceAnimation(); - } - - more |= paperActive; - - if (mAnimation != null) { - more |= mAnimation.calculate(animTime); - } - - canvas.translate(-mScrollX, -mScrollY); - - int requestCount = 0; - int requestedSlot[] = expandIntArray(mRequestRenderSlots, - mLayout.mVisibleEnd - mLayout.mVisibleStart); - - for (int i = mLayout.mVisibleEnd - 1; i >= mLayout.mVisibleStart; --i) { - int r = renderItem(canvas, i, 0, paperActive); - if ((r & RENDER_MORE_FRAME) != 0) more = true; - if ((r & RENDER_MORE_PASS) != 0) requestedSlot[requestCount++] = i; - } - - for (int pass = 1; requestCount != 0; ++pass) { - int newCount = 0; - for (int i = 0; i < requestCount; ++i) { - int r = renderItem(canvas, - requestedSlot[i], pass, paperActive); - if ((r & RENDER_MORE_FRAME) != 0) more = true; - if ((r & RENDER_MORE_PASS) != 0) requestedSlot[newCount++] = i; - } - requestCount = newCount; - } - - canvas.translate(mScrollX, mScrollY); - - if (more) invalidate(); - - final UserInteractionListener listener = mUIListener; - if (mMoreAnimation && !more && listener != null) { - mHandler.post(new Runnable() { - @Override - public void run() { - listener.onUserInteractionEnd(); - } - }); - } - mMoreAnimation = more; - } - - private int renderItem( - GLCanvas canvas, int index, int pass, boolean paperActive) { - canvas.save(GLCanvas.SAVE_FLAG_ALPHA | GLCanvas.SAVE_FLAG_MATRIX); - Rect rect = mLayout.getSlotRect(index, mTempRect); - if (paperActive) { - canvas.multiplyMatrix(mPaper.getTransform(rect, mScrollX), 0); - } else { - canvas.translate(rect.left, rect.top, 0); - } - if (mAnimation != null && mAnimation.isActive()) { - mAnimation.apply(canvas, index, rect); - } - int result = mRenderer.renderSlot( - canvas, index, pass, rect.right - rect.left, rect.bottom - rect.top); - canvas.restore(); - return result; - } - - public static abstract class SlotAnimation extends Animation { - protected float mProgress = 0; - - public SlotAnimation() { - setInterpolator(new DecelerateInterpolator(4)); - setDuration(1500); - } - - @Override - protected void onCalculate(float progress) { - mProgress = progress; - } - - abstract public void apply(GLCanvas canvas, int slotIndex, Rect target); - } - - public static class RisingAnimation extends SlotAnimation { - private static final int RISING_DISTANCE = 128; - - @Override - public void apply(GLCanvas canvas, int slotIndex, Rect target) { - canvas.translate(0, 0, RISING_DISTANCE * (1 - mProgress)); - } - } - - public static class ScatteringAnimation extends SlotAnimation { - private int PHOTO_DISTANCE = 1000; - private RelativePosition mCenter; - - public ScatteringAnimation(RelativePosition center) { - mCenter = center; - } - - @Override - public void apply(GLCanvas canvas, int slotIndex, Rect target) { - canvas.translate( - (mCenter.getX() - target.centerX()) * (1 - mProgress), - (mCenter.getY() - target.centerY()) * (1 - mProgress), - slotIndex * PHOTO_DISTANCE * (1 - mProgress)); - canvas.setAlpha(mProgress); - } - } - - // This Spec class is used to specify the size of each slot in the SlotView. - // There are two ways to do it: - // - // (1) Specify slotWidth and slotHeight: they specify the width and height - // of each slot. The number of rows and the gap between slots will be - // determined automatically. - // (2) Specify rowsLand, rowsPort, and slotGap: they specify the number - // of rows in landscape/portrait mode and the gap between slots. The - // width and height of each slot is determined automatically. - // - // The initial value of -1 means they are not specified. - public static class Spec { - public int slotWidth = -1; - public int slotHeight = -1; - public int slotHeightAdditional = 0; - - public int rowsLand = -1; - public int rowsPort = -1; - public int slotGap = -1; - } - - public class Layout { - - private int mVisibleStart; - private int mVisibleEnd; - - private int mSlotCount; - private int mSlotWidth; - private int mSlotHeight; - private int mSlotGap; - - private Spec mSpec; - - private int mWidth; - private int mHeight; - - private int mUnitCount; - private int mContentLength; - private int mScrollPosition; - - private IntegerAnimation mVerticalPadding = new IntegerAnimation(); - private IntegerAnimation mHorizontalPadding = new IntegerAnimation(); - - public void setSlotSpec(Spec spec) { - mSpec = spec; - } - - public boolean setSlotCount(int slotCount) { - if (slotCount == mSlotCount) return false; - if (mSlotCount != 0) { - mHorizontalPadding.setEnabled(true); - mVerticalPadding.setEnabled(true); - } - mSlotCount = slotCount; - int hPadding = mHorizontalPadding.getTarget(); - int vPadding = mVerticalPadding.getTarget(); - initLayoutParameters(); - return vPadding != mVerticalPadding.getTarget() - || hPadding != mHorizontalPadding.getTarget(); - } - - public Rect getSlotRect(int index, Rect rect) { - int col, row; - if (WIDE) { - col = index / mUnitCount; - row = index - col * mUnitCount; - } else { - row = index / mUnitCount; - col = index - row * mUnitCount; - } - - int x = mHorizontalPadding.get() + col * (mSlotWidth + mSlotGap); - int y = mVerticalPadding.get() + row * (mSlotHeight + mSlotGap); - rect.set(x, y, x + mSlotWidth, y + mSlotHeight); - return rect; - } - - public int getSlotWidth() { - return mSlotWidth; - } - - public int getSlotHeight() { - return mSlotHeight; - } - - // Calculate - // (1) mUnitCount: the number of slots we can fit into one column (or row). - // (2) mContentLength: the width (or height) we need to display all the - // columns (rows). - // (3) padding[]: the vertical and horizontal padding we need in order - // to put the slots towards to the center of the display. - // - // The "major" direction is the direction the user can scroll. The other - // direction is the "minor" direction. - // - // The comments inside this method are the description when the major - // directon is horizontal (X), and the minor directon is vertical (Y). - private void initLayoutParameters( - int majorLength, int minorLength, /* The view width and height */ - int majorUnitSize, int minorUnitSize, /* The slot width and height */ - int[] padding) { - int unitCount = (minorLength + mSlotGap) / (minorUnitSize + mSlotGap); - if (unitCount == 0) unitCount = 1; - mUnitCount = unitCount; - - // We put extra padding above and below the column. - int availableUnits = Math.min(mUnitCount, mSlotCount); - int usedMinorLength = availableUnits * minorUnitSize + - (availableUnits - 1) * mSlotGap; - padding[0] = (minorLength - usedMinorLength) / 2; - - // Then calculate how many columns we need for all slots. - int count = ((mSlotCount + mUnitCount - 1) / mUnitCount); - mContentLength = count * majorUnitSize + (count - 1) * mSlotGap; - - // If the content length is less then the screen width, put - // extra padding in left and right. - padding[1] = Math.max(0, (majorLength - mContentLength) / 2); - } - - private void initLayoutParameters() { - // Initialize mSlotWidth and mSlotHeight from mSpec - if (mSpec.slotWidth != -1) { - mSlotGap = 0; - mSlotWidth = mSpec.slotWidth; - mSlotHeight = mSpec.slotHeight; - } else { - int rows = (mWidth > mHeight) ? mSpec.rowsLand : mSpec.rowsPort; - mSlotGap = mSpec.slotGap; - mSlotHeight = Math.max(1, (mHeight - (rows - 1) * mSlotGap) / rows); - mSlotWidth = mSlotHeight - mSpec.slotHeightAdditional; - } - - if (mRenderer != null) { - mRenderer.onSlotSizeChanged(mSlotWidth, mSlotHeight); - } - - int[] padding = new int[2]; - if (WIDE) { - initLayoutParameters(mWidth, mHeight, mSlotWidth, mSlotHeight, padding); - mVerticalPadding.startAnimateTo(padding[0]); - mHorizontalPadding.startAnimateTo(padding[1]); - } else { - initLayoutParameters(mHeight, mWidth, mSlotHeight, mSlotWidth, padding); - mVerticalPadding.startAnimateTo(padding[1]); - mHorizontalPadding.startAnimateTo(padding[0]); - } - updateVisibleSlotRange(); - } - - public void setSize(int width, int height) { - mWidth = width; - mHeight = height; - initLayoutParameters(); - } - - private void updateVisibleSlotRange() { - int position = mScrollPosition; - - if (WIDE) { - int startCol = position / (mSlotWidth + mSlotGap); - int start = Math.max(0, mUnitCount * startCol); - int endCol = (position + mWidth + mSlotWidth + mSlotGap - 1) / - (mSlotWidth + mSlotGap); - int end = Math.min(mSlotCount, mUnitCount * endCol); - setVisibleRange(start, end); - } else { - int startRow = position / (mSlotHeight + mSlotGap); - int start = Math.max(0, mUnitCount * startRow); - int endRow = (position + mHeight + mSlotHeight + mSlotGap - 1) / - (mSlotHeight + mSlotGap); - int end = Math.min(mSlotCount, mUnitCount * endRow); - setVisibleRange(start, end); - } - } - - public void setScrollPosition(int position) { - if (mScrollPosition == position) return; - mScrollPosition = position; - updateVisibleSlotRange(); - } - - private void setVisibleRange(int start, int end) { - if (start == mVisibleStart && end == mVisibleEnd) return; - if (start < end) { - mVisibleStart = start; - mVisibleEnd = end; - } else { - mVisibleStart = mVisibleEnd = 0; - } - if (mRenderer != null) { - mRenderer.onVisibleRangeChanged(mVisibleStart, mVisibleEnd); - } - } - - public int getVisibleStart() { - return mVisibleStart; - } - - public int getVisibleEnd() { - return mVisibleEnd; - } - - public int getSlotIndexByPosition(float x, float y) { - int absoluteX = Math.round(x) + (WIDE ? mScrollPosition : 0); - int absoluteY = Math.round(y) + (WIDE ? 0 : mScrollPosition); - - absoluteX -= mHorizontalPadding.get(); - absoluteY -= mVerticalPadding.get(); - - if (absoluteX < 0 || absoluteY < 0) { - return INDEX_NONE; - } - - int columnIdx = absoluteX / (mSlotWidth + mSlotGap); - int rowIdx = absoluteY / (mSlotHeight + mSlotGap); - - if (!WIDE && columnIdx >= mUnitCount) { - return INDEX_NONE; - } - - if (WIDE && rowIdx >= mUnitCount) { - return INDEX_NONE; - } - - if (absoluteX % (mSlotWidth + mSlotGap) >= mSlotWidth) { - return INDEX_NONE; - } - - if (absoluteY % (mSlotHeight + mSlotGap) >= mSlotHeight) { - return INDEX_NONE; - } - - int index = WIDE - ? (columnIdx * mUnitCount + rowIdx) - : (rowIdx * mUnitCount + columnIdx); - - return index >= mSlotCount ? INDEX_NONE : index; - } - - public int getScrollLimit() { - int limit = WIDE ? mContentLength - mWidth : mContentLength - mHeight; - return limit <= 0 ? 0 : limit; - } - - public boolean advanceAnimation(long animTime) { - // use '|' to make sure both sides will be executed - return mVerticalPadding.calculate(animTime) | mHorizontalPadding.calculate(animTime); - } - } - - private class MyGestureListener implements GestureDetector.OnGestureListener { - private boolean isDown; - - // We call the listener's onDown() when our onShowPress() is called and - // call the listener's onUp() when we receive any further event. - @Override - public void onShowPress(MotionEvent e) { - GLRoot root = getGLRoot(); - root.lockRenderThread(); - try { - if (isDown) return; - int index = mLayout.getSlotIndexByPosition(e.getX(), e.getY()); - if (index != INDEX_NONE) { - isDown = true; - mListener.onDown(index); - } - } finally { - root.unlockRenderThread(); - } - } - - private void cancelDown(boolean byLongPress) { - if (!isDown) return; - isDown = false; - mListener.onUp(byLongPress); - } - - @Override - public boolean onDown(MotionEvent e) { - return false; - } - - @Override - public boolean onFling(MotionEvent e1, - MotionEvent e2, float velocityX, float velocityY) { - cancelDown(false); - int scrollLimit = mLayout.getScrollLimit(); - if (scrollLimit == 0) return false; - float velocity = WIDE ? velocityX : velocityY; - mScroller.fling((int) -velocity, 0, scrollLimit); - if (mUIListener != null) mUIListener.onUserInteractionBegin(); - invalidate(); - return true; - } - - @Override - public boolean onScroll(MotionEvent e1, - MotionEvent e2, float distanceX, float distanceY) { - cancelDown(false); - float distance = WIDE ? distanceX : distanceY; - int overDistance = mScroller.startScroll( - Math.round(distance), 0, mLayout.getScrollLimit()); - if (mOverscrollEffect == OVERSCROLL_3D && overDistance != 0) { - mPaper.overScroll(overDistance); - } - invalidate(); - return true; - } - - @Override - public boolean onSingleTapUp(MotionEvent e) { - cancelDown(false); - if (mDownInScrolling) return true; - int index = mLayout.getSlotIndexByPosition(e.getX(), e.getY()); - if (index != INDEX_NONE) mListener.onSingleTapUp(index); - return true; - } - - @Override - public void onLongPress(MotionEvent e) { - cancelDown(true); - if (mDownInScrolling) return; - lockRendering(); - try { - int index = mLayout.getSlotIndexByPosition(e.getX(), e.getY()); - if (index != INDEX_NONE) mListener.onLongTap(index); - } finally { - unlockRendering(); - } - } - } - - public void setStartIndex(int index) { - mStartIndex = index; - } - - // Return true if the layout parameters have been changed - public boolean setSlotCount(int slotCount) { - boolean changed = mLayout.setSlotCount(slotCount); - - // mStartIndex is applied the first time setSlotCount is called. - if (mStartIndex != INDEX_NONE) { - setCenterIndex(mStartIndex); - mStartIndex = INDEX_NONE; - } - // Reset the scroll position to avoid scrolling over the updated limit. - setScrollPosition(WIDE ? mScrollX : mScrollY); - return changed; - } - - public int getVisibleStart() { - return mLayout.getVisibleStart(); - } - - public int getVisibleEnd() { - return mLayout.getVisibleEnd(); - } - - public int getScrollX() { - return mScrollX; - } - - public int getScrollY() { - return mScrollY; - } - - public Rect getSlotRect(int slotIndex, GLView rootPane) { - // Get slot rectangle relative to this root pane. - Rect offset = new Rect(); - rootPane.getBoundsOf(this, offset); - Rect r = getSlotRect(slotIndex); - r.offset(offset.left - getScrollX(), - offset.top - getScrollY()); - return r; - } - - private static class IntegerAnimation extends Animation { - private int mTarget; - private int mCurrent = 0; - private int mFrom = 0; - private boolean mEnabled = false; - - public void setEnabled(boolean enabled) { - mEnabled = enabled; - } - - public void startAnimateTo(int target) { - if (!mEnabled) { - mTarget = mCurrent = target; - return; - } - if (target == mTarget) return; - - mFrom = mCurrent; - mTarget = target; - setDuration(180); - start(); - } - - public int get() { - return mCurrent; - } - - public int getTarget() { - return mTarget; - } - - @Override - protected void onCalculate(float progress) { - mCurrent = Math.round(mFrom + progress * (mTarget - mFrom)); - if (progress == 1f) mEnabled = false; - } - } -} |