Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3d07af03 authored by Adam Cohen's avatar Adam Cohen
Browse files

-> Added edge behaviour for StackView (what to do at

   the beginning and end of the stack)
-> Wrapping children of AdapterViewAnimator in a
   FrameLayout to uphold the Adapter contract (ie.
   don't modify the Adapter's views.)
-> Fixed clipping problem in StackViews with padding

Change-Id: I83b02b5fdfd687838346e0bcb5dc30c033cd0cb8
parent 1b196022
Loading
Loading
Loading
Loading
+13 −4
Original line number Diff line number Diff line
@@ -340,9 +340,14 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
        for (int i = 0; i < mPreviousViews.size(); i++) {
            View viewToRemove = mPreviousViews.get(i);
            viewToRemove.clearAnimation();
            if (viewToRemove instanceof ViewGroup) {
                ViewGroup vg = (ViewGroup) viewToRemove;
                vg.removeAllViewsInLayout();
            }
            // applyTransformForChildAtIndex here just allows for any cleanup
            // associated with this view that may need to be done by a subclass
            applyTransformForChildAtIndex(viewToRemove, -1);

            removeViewInLayout(viewToRemove);
        }
        mPreviousViews.clear();
@@ -405,10 +410,14 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter>
                    // and apply any transform / animation
                    View newView = mAdapter.getView(i, null, this);
                    if (newView != null) {
                        mActiveViews[index] = newView;
                        addViewInLayout(newView, -1, createOrReuseLayoutParams(newView));
                        applyTransformForChildAtIndex(newView, newRelativeIndex);
                        animateViewForTransition(-1, newRelativeIndex, newView);
                        // We wrap the new view in a FrameLayout so as to respect the contract
                        // with the adapter, that is, that we don't modify this view directly
                        FrameLayout fl = new FrameLayout(mContext);
                        fl.addView(newView);
                        mActiveViews[index] = fl;
                        addViewInLayout(fl, -1, createOrReuseLayoutParams(fl));
                        applyTransformForChildAtIndex(fl, newRelativeIndex);
                        animateViewForTransition(-1, newRelativeIndex, fl);
                    }
                }
                mActiveViews[index].bringToFront();
+114 −42
Original line number Diff line number Diff line
@@ -48,7 +48,7 @@ public class StackView extends AdapterViewAnimator {
    /**
     * Default animation parameters
     */
    private final int DEFAULT_ANIMATION_DURATION = 500;
    private final int DEFAULT_ANIMATION_DURATION = 400;
    private final int MINIMUM_ANIMATION_DURATION = 50;

    /**
@@ -141,9 +141,9 @@ public class StackView extends AdapterViewAnimator {
            view.setVisibility(VISIBLE);

            LayoutParams lp = (LayoutParams) view.getLayoutParams();
            int largestDuration =
                Math.round(mStackSlider.getDurationForNeutralPosition()*DEFAULT_ANIMATION_DURATION);

            int largestDuration = Math.round(
                    (lp.verticalOffset*1.0f/-mViewHeight)*DEFAULT_ANIMATION_DURATION);
            int duration = largestDuration;
            if (mYVelocity != 0) {
                duration = 1000*(0 - lp.verticalOffset)/Math.abs(mYVelocity);
@@ -165,7 +165,8 @@ public class StackView extends AdapterViewAnimator {
            // Slide item out
            LayoutParams lp = (LayoutParams) view.getLayoutParams();

            int largestDuration = Math.round(mStackSlider.getYProgress()*DEFAULT_ANIMATION_DURATION);
            int largestDuration = Math.round(mStackSlider.getDurationForOffscreenPosition()*
                    DEFAULT_ANIMATION_DURATION);
            int duration = largestDuration;
            if (mYVelocity != 0) {
                duration = 1000*(lp.verticalOffset + mViewHeight)/Math.abs(mYVelocity);
@@ -245,6 +246,7 @@ public class StackView extends AdapterViewAnimator {
            // ClipChildren and ClipToPadding. We're probably going  to want to reset
            // these flags as well.
            setClipChildren(false);
            setClipToPadding(false);
            ViewGroup view = this;
            while (view.getParent() != null && view.getParent() instanceof ViewGroup) {
                view = (ViewGroup) view.getParent();
@@ -297,22 +299,37 @@ public class StackView extends AdapterViewAnimator {

    private void beginGestureIfNeeded(float deltaY) {
        if ((int) Math.abs(deltaY) > mTouchSlop && mSwipeGestureType == GESTURE_NONE) {
            mSwipeGestureType = deltaY < 0 ? GESTURE_SLIDE_UP : GESTURE_SLIDE_DOWN;
            int swipeGestureType = deltaY < 0 ? GESTURE_SLIDE_UP : GESTURE_SLIDE_DOWN;
            cancelLongPress();
            requestDisallowInterceptTouchEvent(true);

            int activeIndex = mSwipeGestureType == GESTURE_SLIDE_DOWN ? mNumActiveViews - 1
            int activeIndex = swipeGestureType == GESTURE_SLIDE_DOWN ? mNumActiveViews - 1
                    : mNumActiveViews - 2;

            if (mAdapter == null) return;

            if (mCurrentWindowStartUnbounded + activeIndex == 0) {
                mStackSlider.setMode(StackSlider.BEGINNING_OF_STACK_MODE);
            } else if (mCurrentWindowStartUnbounded + activeIndex == mAdapter.getCount()) {
                activeIndex--;
                mStackSlider.setMode(StackSlider.END_OF_STACK_MODE);
            } else {
                mStackSlider.setMode(StackSlider.NORMAL_MODE);
            }

            View v = getViewAtRelativeIndex(activeIndex);
            if (v != null) {
            if (v == null) return;

            mHighlight.setImageBitmap(createOutline(v));
            mHighlight.bringToFront();
            v.bringToFront();
            mStackSlider.setView(v);
                if (mSwipeGestureType == GESTURE_SLIDE_DOWN)

            if (swipeGestureType == GESTURE_SLIDE_DOWN)
                v.setVisibility(VISIBLE);
            }

            // We only register this gesture if we've made it this far without a problem
            mSwipeGestureType = swipeGestureType;
        }
    }

@@ -339,7 +356,7 @@ public class StackView extends AdapterViewAnimator {
            case MotionEvent.ACTION_MOVE: {
                beginGestureIfNeeded(deltaY);

                float rx = 0.3f*deltaX/(mViewHeight*1.0f);
                float rx = deltaX/(mViewHeight*1.0f);
                if (mSwipeGestureType == GESTURE_SLIDE_DOWN) {
                    float r = (deltaY-mTouchSlop*1.0f)/mViewHeight*1.0f;
                    mStackSlider.setYProgress(1 - r);
@@ -351,7 +368,6 @@ public class StackView extends AdapterViewAnimator {
                    mStackSlider.setXProgress(rx);
                    return true;
                }

                break;
            }
            case MotionEvent.ACTION_UP: {
@@ -412,7 +428,7 @@ public class StackView extends AdapterViewAnimator {
                }
            }
            // if we made it this far, it means we didn't find a satisfactory new pointer :(,
            // so end the
            // so end the gesture
            handlePointerUp(ev);
        }
    }
@@ -422,25 +438,30 @@ public class StackView extends AdapterViewAnimator {
        float newY = ev.getY(pointerIndex);
        int deltaY = (int) (newY - mInitialY);

        if (mVelocityTracker != null) {
            mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
            mYVelocity = (int) mVelocityTracker.getYVelocity(mActivePointerId);
        }

        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }

        if (deltaY > mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_DOWN) {
        if (deltaY > mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_DOWN
                && mStackSlider.mMode == StackSlider.NORMAL_MODE) {
            // Swipe threshold exceeded, swipe down
            showNext();
            mHighlight.bringToFront();
        } else if (deltaY < -mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_UP) {
        } else if (deltaY < -mSwipeThreshold && mSwipeGestureType == GESTURE_SLIDE_UP
                && mStackSlider.mMode == StackSlider.NORMAL_MODE) {
            // Swipe threshold exceeded, swipe up
            showPrevious();
            mHighlight.bringToFront();
        } else if (mSwipeGestureType == GESTURE_SLIDE_UP) {
            // Didn't swipe up far enough, snap back down
            int duration = Math.round(mStackSlider.getYProgress()*DEFAULT_ANIMATION_DURATION);
            int duration =
                Math.round(mStackSlider.getDurationForNeutralPosition()*DEFAULT_ANIMATION_DURATION);

            StackSlider animationSlider = new StackSlider(mStackSlider);
            PropertyAnimator snapBackY = new PropertyAnimator(duration, animationSlider,
@@ -453,8 +474,9 @@ public class StackView extends AdapterViewAnimator {
            snapBackX.start();
        } else if (mSwipeGestureType == GESTURE_SLIDE_DOWN) {
            // Didn't swipe down far enough, snap back up
            int duration = Math.round((1 -
                    mStackSlider.getYProgress())*DEFAULT_ANIMATION_DURATION);
            int duration = Math.round(mStackSlider.getDurationForOffscreenPosition()*
                    DEFAULT_ANIMATION_DURATION);

            StackSlider animationSlider = new StackSlider(mStackSlider);
            PropertyAnimator snapBackY = new PropertyAnimator(duration, animationSlider,
                    "YProgress", mStackSlider.getYProgress(), 1);
@@ -475,6 +497,12 @@ public class StackView extends AdapterViewAnimator {
        float mYProgress;
        float mXProgress;

        static final int NORMAL_MODE = 0;
        static final int BEGINNING_OF_STACK_MODE = 1;
        static final int END_OF_STACK_MODE = 2;

        int mMode = NORMAL_MODE;

        public StackSlider() {
        }

@@ -482,6 +510,7 @@ public class StackView extends AdapterViewAnimator {
            mView = copy.mView;
            mYProgress = copy.mYProgress;
            mXProgress = copy.mXProgress;
            mMode = copy.mMode;
        }

        private float cubic(float r) {
@@ -525,10 +554,11 @@ public class StackView extends AdapterViewAnimator {
            r = Math.max(0, r);

            mYProgress = r;

            final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams();
            final LayoutParams highlightLp = (LayoutParams) mHighlight.getLayoutParams();

            switch (mMode) {
                case NORMAL_MODE:
                    viewLp.setVerticalOffset(Math.round(-r*mViewHeight));
                    highlightLp.setVerticalOffset(Math.round(-r*mViewHeight));
                    mHighlight.setAlpha(highlightAlphaInterpolator(r));
@@ -539,29 +569,70 @@ public class StackView extends AdapterViewAnimator {
                    // so that they don't interfere with click events.
                    if (mView.getAlpha() == 0 && alpha != 0 && mView.getVisibility() != VISIBLE) {
                        mView.setVisibility(VISIBLE);
            } else if (alpha == 0 && mView.getAlpha() != 0 && mView.getVisibility() == VISIBLE) {
                    } else if (alpha == 0 && mView.getAlpha() != 0
                            && mView.getVisibility() == VISIBLE) {
                        mView.setVisibility(INVISIBLE);
                    }

            mView.setAlpha(viewAlphaInterpolator(1-r));
                    mView.setAlpha(alpha);
                    mView.setRotationX(90.0f*rotationInterpolator(r));
                    mHighlight.setRotationX(90.0f*rotationInterpolator(r));
                    break;
                case BEGINNING_OF_STACK_MODE:
                    r = r*0.2f;
                    viewLp.setVerticalOffset(Math.round(-r*mViewHeight));
                    highlightLp.setVerticalOffset(Math.round(-r*mViewHeight));
                    mHighlight.setAlpha(highlightAlphaInterpolator(r));
                    break;
                case END_OF_STACK_MODE:
                    r = (1-r)*0.2f;
                    viewLp.setVerticalOffset(Math.round(r*mViewHeight));
                    highlightLp.setVerticalOffset(Math.round(r*mViewHeight));
                    mHighlight.setAlpha(highlightAlphaInterpolator(r));
                    break;
            }
        }

        public void setXProgress(float r) {
            // enforce r between 0 and 1
            r = Math.min(1.0f, r);
            r = Math.max(-1.0f, r);
            r = Math.min(2.0f, r);
            r = Math.max(-2.0f, r);

            mXProgress = r;

            final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams();
            final LayoutParams highlightLp = (LayoutParams) mHighlight.getLayoutParams();

            r *= 0.2f;
            viewLp.setHorizontalOffset(Math.round(r*mViewHeight));
            highlightLp.setHorizontalOffset(Math.round(r*mViewHeight));
        }

        void setMode(int mode) {
            mMode = mode;
        }

        float getDurationForNeutralPosition() {
            return getDuration(false);
        }

        float getDurationForOffscreenPosition() {
            return getDuration(mMode == END_OF_STACK_MODE ? false : true);
        }

        private float getDuration(boolean invert) {
            if (mView != null) {
                final LayoutParams viewLp = (LayoutParams) mView.getLayoutParams();

                float d = (float) Math.sqrt(Math.pow(viewLp.horizontalOffset,2) +
                        Math.pow(viewLp.verticalOffset,2));
                float maxd = (float) Math.sqrt(Math.pow(mViewHeight, 2) +
                        Math.pow(0.4f*mViewHeight, 2));
                return invert ? (1-d/maxd) : d/maxd;
            }
            return 0;
        }

        float getYProgress() {
            return mYProgress;
        }
@@ -569,6 +640,7 @@ public class StackView extends AdapterViewAnimator {
        float getXProgress() {
            return mXProgress;
        }

    }

    @Override