diff options
Diffstat (limited to 'src')
5 files changed, 365 insertions, 1286 deletions
diff --git a/src/com/sothree/slidinguppanel/SlidingupPanelLayout.java b/src/com/sothree/slidinguppanel/SlidingupPanelLayout.java deleted file mode 100644 index 7b277d5..0000000 --- a/src/com/sothree/slidinguppanel/SlidingupPanelLayout.java +++ /dev/null @@ -1,1282 +0,0 @@ -/* - * THIS FILE HAS BEEN MODIFIED BY THE CYANOGENMOD PROJECT - * - * ORIGINAL SOURCE: https://github.com/umano/AndroidSlidingUpPanel - * - * 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.sothree.slidinguppanel; - -import org.cyanogenmod.theme.chooser.R; - -import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.PixelFormat; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.os.Parcel; -import android.os.Parcelable; -import android.support.v4.view.MotionEventCompat; -import android.support.v4.view.ViewCompat; -import android.support.v4.widget.ViewDragHelper; -import android.util.AttributeSet; -import android.util.Log; -import android.view.Gravity; -import android.view.MotionEvent; -import android.view.SoundEffectConstants; -import android.view.View; -import android.view.ViewConfiguration; -import android.view.ViewGroup; -import android.view.accessibility.AccessibilityEvent; - - -public class SlidingupPanelLayout extends ViewGroup { - - private static final String TAG = SlidingupPanelLayout.class.getSimpleName(); - - /** - * Default peeking out panel height - */ - private static final int DEFAULT_PANEL_HEIGHT = 68; // dp; - - /** - * Default height of the shadow above the peeking out panel - */ - private static final int DEFAULT_SHADOW_HEIGHT = 4; // dp; - - /** - * If no fade color is given by default it will fade to 80% gray. - */ - private static final int DEFAULT_FADE_COLOR = 0x99000000; - - /** - * Default Minimum velocity that will be detected as a fling - */ - private static final int DEFAULT_MIN_FLING_VELOCITY = 400; // dips per second - - /** - * Default attributes for layout - */ - private static final int[] DEFAULT_ATTRS = new int[] { - android.R.attr.layout_gravity - }; - - /** - * Minimum velocity that will be detected as a fling - */ - private int mMinFlingVelocity = DEFAULT_MIN_FLING_VELOCITY; - - /** - * The fade color used for the panel covered by the slider. 0 = no fading. - */ - private int mCoveredFadeColor = DEFAULT_FADE_COLOR; - - /** - * The paint used to dim the main layout when sliding - */ - private final Paint mCoveredFadePaint = new Paint(); - - /** - * Drawable used to draw the shadow between panes. - */ - private Drawable mShadowDrawable; - - /** - * The size of the overhang in pixels. - */ - private int mPanelHeight = -1; - - /** - * The size of the shadow in pixels. - */ - private int mShadowHeight = -1; - - /** - * True if the collapsed panel should be dragged up. - */ - private boolean mIsSlidingUp; - - /** - * True if a panel can slide with the current measurements - */ - private boolean mCanSlide; - - /** - * If provided, the panel can be dragged by only this view. Otherwise, the entire panel can be - * used for dragging. - */ - private View mDragView; - - /** - * If provided, the panel can be dragged by only this view. Otherwise, the entire panel can be - * used for dragging. - */ - private int mDragViewResId = -1; - - /** - * The child view that can slide, if any. - */ - private View mSlideableView; - - /** - * Current state of the slideable view. - */ - private enum SlideState { - EXPANDED, - COLLAPSED, - ANCHORED - } - private SlideState mSlideState = SlideState.COLLAPSED; - - /** - * How far the panel is offset from its expanded position. - * range [0, 1] where 0 = expanded, 1 = collapsed. - */ - private float mSlideOffset; - - /** - * How far in pixels the slideable panel may move. - */ - private int mSlideRange; - - /** - * A panel view is locked into internal scrolling or another condition that - * is preventing a drag. - */ - private boolean mIsUnableToDrag; - - /** - * Flag indicating that sliding feature is enabled\disabled - */ - private boolean mIsSlidingEnabled; - - /** - * Flag indicating if a drag view can have its own touch events. If set - * to true, a drag view can scroll horizontally and have its own click listener. - * - * Default is set to false. - */ - private boolean mIsUsingDragViewTouchEvents; - - /** - * Threshold to tell if there was a scroll touch event. - */ - private final int mScrollTouchSlop; - - private float mInitialMotionX; - private float mInitialMotionY; - private float mAnchorPoint = 0.0f; - - private PanelSlideListener mPanelSlideListener; - - private final ViewDragHelper mDragHelper; - - /** - * How far the non-sliding panel is parallaxed from its usual position when open. - * range [0, 1] - */ - private float mParallaxOffset; - - /** - * Distance in pixels to parallax the fixed pane by when fully closed - */ - private int mParallaxBy; - - /** - * Stores whether or not the pane was expanded the last time it was slideable. - * If expand/collapse operations are invoked this state is modified. Used by - * instance state save/restore. - */ - private boolean mFirstLayout = true; - - private final Rect mTmpRect = new Rect(); - - /** - * Listener for monitoring events about sliding panes. - */ - public interface PanelSlideListener { - /** - * Called when a sliding pane's position changes. - * @param panel The child view that was moved - * @param slideOffset The new offset of this sliding pane within its range, from 0-1 - */ - public void onPanelSlide(View panel, float slideOffset); - /** - * Called when a sliding pane becomes slid completely collapsed. The pane may or may not - * be interactive at this point depending on if it's shown or hidden - * @param panel The child view that was slid to an collapsed position, revealing other panes - */ - public void onPanelCollapsed(View panel); - - /** - * Called when a sliding pane becomes slid completely expanded. The pane is now guaranteed - * to be interactive. It may now obscure other views in the layout. - * @param panel The child view that was slid to a expanded position - */ - public void onPanelExpanded(View panel); - - public void onPanelAnchored(View panel); - } - - /** - * No-op stubs for {@link PanelSlideListener}. If you only want to implement a subset - * of the listener methods you can extend this instead of implement the full interface. - */ - public static class SimplePanelSlideListener implements PanelSlideListener { - @Override - public void onPanelSlide(View panel, float slideOffset) { - } - @Override - public void onPanelCollapsed(View panel) { - } - @Override - public void onPanelExpanded(View panel) { - } - @Override - public void onPanelAnchored(View panel) { - } - } - - public SlidingupPanelLayout(Context context) { - this(context, null); - } - - public SlidingupPanelLayout(Context context, AttributeSet attrs) { - this(context, attrs, 0); - } - - public SlidingupPanelLayout(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - if (attrs != null) { - TypedArray defAttrs = context.obtainStyledAttributes(attrs, DEFAULT_ATTRS); - - if (defAttrs != null) { - int gravity = defAttrs.getInt(0, Gravity.NO_GRAVITY); - if (gravity != Gravity.TOP && gravity != Gravity.BOTTOM) { - throw new IllegalArgumentException("layout_gravity must be set to either top or bottom"); - } - mIsSlidingUp = gravity == Gravity.BOTTOM; - } - - defAttrs.recycle(); - - TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.SlidingUpPanelLayout); - - if (ta != null) { - mPanelHeight = ta.getDimensionPixelSize(R.styleable.SlidingUpPanelLayout_collapsedHeight, -1); - mShadowHeight = ta.getDimensionPixelSize(R.styleable.SlidingUpPanelLayout_shadowHeight, -1); - - mMinFlingVelocity = ta.getInt(R.styleable.SlidingUpPanelLayout_flingVelocity, DEFAULT_MIN_FLING_VELOCITY); - mCoveredFadeColor = ta.getColor(R.styleable.SlidingUpPanelLayout_fadeColor, DEFAULT_FADE_COLOR); - - mDragViewResId = ta.getResourceId(R.styleable.SlidingUpPanelLayout_dragView, -1); - mParallaxBy = ta.getDimensionPixelSize(R.styleable.SlidingUpPanelLayout_parallaxDistance, 0); - boolean startExpanded = ta.getBoolean(R.styleable.SlidingUpPanelLayout_startExpanded, false); - mAnchorPoint = ta.getFloat(R.styleable.SlidingUpPanelLayout_anchorPoint, 0.0f); - if (startExpanded) { - mSlideState = (mAnchorPoint != 0) ? SlideState.ANCHORED : SlideState.EXPANDED; - } - } - - ta.recycle(); - } - - final float density = context.getResources().getDisplayMetrics().density; - if (mPanelHeight == -1) { - mPanelHeight = (int) (DEFAULT_PANEL_HEIGHT * density + 0.5f); - } - if (mShadowHeight == -1) { - mShadowHeight = (int) (DEFAULT_SHADOW_HEIGHT * density + 0.5f); - } - - setWillNotDraw(false); - - mDragHelper = ViewDragHelper.create(this, 0.5f, new DragHelperCallback()); - mDragHelper.setMinVelocity(mMinFlingVelocity * density); - - mCanSlide = true; - mIsSlidingEnabled = true; - - ViewConfiguration vc = ViewConfiguration.get(context); - mScrollTouchSlop = vc.getScaledTouchSlop(); - } - - /** - * Set the Drag View after the view is inflated - */ - @Override - protected void onFinishInflate() { - super.onFinishInflate(); - if (mDragViewResId != -1) { - mDragView = findViewById(mDragViewResId); - } - } - - /** - * Set a distance to parallax the lower pane by when the upper pane is in its - * fully closed state. The lower pane will scroll between this position and - * its fully open state. - * - * @param parallaxBy Distance to parallax by in pixels - */ - public void setParallaxDistance(int parallaxBy) { - mParallaxBy = parallaxBy; - requestLayout(); - } - - /** - * @return The distance the lower pane will parallax by when the upper pane is fully closed. - * - * @see #setParallaxDistance(int) - */ - public int getParallaxDistance() { - return mParallaxBy; - } - - /** - * Set the color used to fade the pane covered by the sliding pane out when the pane - * will become fully covered in the expanded state. - * - * @param color An ARGB-packed color value - */ - public void setCoveredFadeColor(int color) { - mCoveredFadeColor = color; - invalidate(); - } - - /** - * @return The ARGB-packed color value used to fade the fixed pane - */ - public int getCoveredFadeColor() { - return mCoveredFadeColor; - } - - /** - * Set the collapsed panel height in pixels - * - * @param val A height in pixels - */ - public void setPanelHeight(int val) { - mPanelHeight = val; - requestLayout(); - } - - /** - * @return The current collapsed panel height - */ - public int getPanelHeight() { - return mPanelHeight; - } - - public void setPanelSlideListener(PanelSlideListener listener) { - mPanelSlideListener = listener; - } - - /** - * Set the draggable view portion. Use to null, to allow the whole panel to be draggable - * - * @param dragView A view that will be used to drag the panel. - */ - public void setDragView(View dragView) { - mDragView = dragView; - } - - /** - * Set an anchor point where the panel can stop during sliding - * - * @param anchorPoint A value between 0 and 1, determining the position of the anchor point - * starting from the top of the layout. - */ - public void setAnchorPoint(float anchorPoint) { - if (anchorPoint > 0 && anchorPoint < 1) - mAnchorPoint = anchorPoint; - } - - public float getAnchorPoint() { - return mAnchorPoint; - } - - /** - * Set the shadow for the sliding panel - * - */ - public void setShadowDrawable(Drawable drawable) { - mShadowDrawable = drawable; - } - - void dispatchOnPanelSlide(View panel) { - if (mPanelSlideListener != null) { - mPanelSlideListener.onPanelSlide(panel, mSlideOffset); - } - } - - void dispatchOnPanelExpanded(View panel) { - if (mPanelSlideListener != null) { - mPanelSlideListener.onPanelExpanded(panel); - } - sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); - } - - void dispatchOnPanelCollapsed(View panel) { - if (mPanelSlideListener != null) { - mPanelSlideListener.onPanelCollapsed(panel); - } - sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); - } - - void dispatchOnPanelAnchored(View panel) { - if (mPanelSlideListener != null) { - mPanelSlideListener.onPanelAnchored(panel); - } - sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); - } - - void updateObscuredViewVisibility() { - if (getChildCount() == 0) { - return; - } - final int leftBound = getPaddingLeft(); - final int rightBound = getWidth() - getPaddingRight(); - final int topBound = getPaddingTop(); - final int bottomBound = getHeight() - getPaddingBottom(); - final int left; - final int right; - final int top; - final int bottom; - if (mSlideableView != null && hasOpaqueBackground(mSlideableView)) { - left = mSlideableView.getLeft(); - right = mSlideableView.getRight(); - top = mSlideableView.getTop(); - bottom = mSlideableView.getBottom(); - } else { - left = right = top = bottom = 0; - } - View child = getChildAt(0); - final int clampedChildLeft = Math.max(leftBound, child.getLeft()); - final int clampedChildTop = Math.max(topBound, child.getTop()); - final int clampedChildRight = Math.min(rightBound, child.getRight()); - final int clampedChildBottom = Math.min(bottomBound, child.getBottom()); - final int vis; - if (clampedChildLeft >= left && clampedChildTop >= top && - clampedChildRight <= right && clampedChildBottom <= bottom) { - vis = INVISIBLE; - } else { - vis = VISIBLE; - } - child.setVisibility(vis); - } - - void setAllChildrenVisible() { - for (int i = 0, childCount = getChildCount(); i < childCount; i++) { - final View child = getChildAt(i); - if (child.getVisibility() == INVISIBLE) { - child.setVisibility(VISIBLE); - } - } - } - - private static boolean hasOpaqueBackground(View v) { - final Drawable bg = v.getBackground(); - if (bg != null) { - return bg.getOpacity() == PixelFormat.OPAQUE; - } - return false; - } - - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - mFirstLayout = true; - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - mFirstLayout = true; - } - - @Override - protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { - final int widthMode = MeasureSpec.getMode(widthMeasureSpec); - final int widthSize = MeasureSpec.getSize(widthMeasureSpec); - final int heightMode = MeasureSpec.getMode(heightMeasureSpec); - final int heightSize = MeasureSpec.getSize(heightMeasureSpec); - - if (widthMode != MeasureSpec.EXACTLY) { - throw new IllegalStateException("Width must have an exact value or MATCH_PARENT"); - } else if (heightMode != MeasureSpec.EXACTLY) { - throw new IllegalStateException("Height must have an exact value or MATCH_PARENT"); - } - - int layoutHeight = heightSize - getPaddingTop() - getPaddingBottom(); - int panelHeight = mPanelHeight; - - final int childCount = getChildCount(); - - if (childCount > 2) { - Log.e(TAG, "onMeasure: More than two child views are not supported."); - } else if (getChildAt(1).getVisibility() == GONE) { - panelHeight = 0; - } - - // We'll find the current one below. - mSlideableView = null; - mCanSlide = false; - - // First pass. Measure based on child LayoutParams width/height. - for (int i = 0; i < childCount; i++) { - final View child = getChildAt(i); - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - - int height = layoutHeight; - if (child.getVisibility() == GONE) { - lp.dimWhenOffset = false; - continue; - } - - if (i == 1) { - lp.slideable = true; - lp.dimWhenOffset = true; - mSlideableView = child; - mCanSlide = true; - } else { - height -= panelHeight; - } - - int childWidthSpec; - if (lp.width == LayoutParams.WRAP_CONTENT) { - childWidthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST); - } else if (lp.width == LayoutParams.MATCH_PARENT) { - childWidthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY); - } else { - childWidthSpec = MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY); - } - - int childHeightSpec; - if (lp.height == LayoutParams.WRAP_CONTENT) { - childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST); - } else if (lp.height == LayoutParams.MATCH_PARENT) { - childHeightSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); - } else { - childHeightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY); - } - - child.measure(childWidthSpec, childHeightSpec); - } - - setMeasuredDimension(widthSize, heightSize); - } - - @Override - protected void onLayout(boolean changed, int l, int t, int r, int b) { - final int paddingLeft = getPaddingLeft(); - final int paddingTop = getPaddingTop(); - final int slidingTop = getSlidingTop(); - - final int childCount = getChildCount(); - - if (mFirstLayout) { - switch (mSlideState) { - case EXPANDED: - mSlideOffset = mCanSlide ? 0.f : 1.f; - break; - case ANCHORED: - mSlideOffset = mCanSlide ? mAnchorPoint : 1.f; - break; - default: - mSlideOffset = 1.f; - break; - } - } - - for (int i = 0; i < childCount; i++) { - final View child = getChildAt(i); - - if (child.getVisibility() == GONE) { - continue; - } - - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - final int childHeight = child.getMeasuredHeight(); - int offset = 0; - - if (lp.slideable) { - mSlideRange = childHeight - mPanelHeight; - } else if (mCanSlide && mParallaxBy != 0) { - offset = (int) ((1 - mSlideOffset) * mParallaxBy); - } - - final int childTop; - if (mIsSlidingUp) { - childTop = (lp.slideable ? slidingTop + (int) (mSlideRange * mSlideOffset) - : paddingTop) - offset; - } else { - childTop = (lp.slideable ? slidingTop - (int) (mSlideRange * mSlideOffset) - : paddingTop + mPanelHeight) + offset; - } - final int childBottom = childTop + childHeight; - final int childLeft = paddingLeft; - final int childRight = childLeft + child.getMeasuredWidth(); - - child.layout(childLeft, childTop, childRight, childBottom); - } - - if (mFirstLayout) { - if (mCanSlide) { - if (mParallaxBy != 0) { - parallaxOtherViews(mSlideOffset); - } - } - updateObscuredViewVisibility(); - } - - mFirstLayout = false; - } - - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - // Recalculate sliding panes and their details - if (h != oldh) { - mFirstLayout = true; - } - } - - /** - * Set sliding enabled flag - * @param enabled flag value - */ - public void setSlidingEnabled(boolean enabled) { - mIsSlidingEnabled = enabled; - } - - /** - * Set if the drag view can have its own touch events. If set - * to true, a drag view can scroll horizontally and have its own click listener. - * - * Default is set to false. - */ - public void setEnableDragViewTouchEvents(boolean enabled) { - mIsUsingDragViewTouchEvents = enabled; - } - - @Override - public boolean onInterceptTouchEvent(MotionEvent ev) { - final int action = MotionEventCompat.getActionMasked(ev); - - if (!mCanSlide || !mIsSlidingEnabled || (mIsUnableToDrag && action != MotionEvent.ACTION_DOWN)) { - mDragHelper.cancel(); - return super.onInterceptTouchEvent(ev); - } - - if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { - mDragHelper.cancel(); - return false; - } - - final float x = ev.getX(); - final float y = ev.getY(); - boolean interceptTap = false; - - switch (action) { - case MotionEvent.ACTION_DOWN: { - mIsUnableToDrag = false; - mInitialMotionX = x; - mInitialMotionY = y; - if (isDragViewUnder((int) x, (int) y) && !mIsUsingDragViewTouchEvents) { - interceptTap = true; - } - break; - } - - case MotionEvent.ACTION_MOVE: { - final float adx = Math.abs(x - mInitialMotionX); - final float ady = Math.abs(y - mInitialMotionY); - final int dragSlop = mDragHelper.getTouchSlop(); - - // Handle any horizontal scrolling on the drag view. - if (mIsUsingDragViewTouchEvents) { - if (adx > mScrollTouchSlop && ady < mScrollTouchSlop) { - return super.onInterceptTouchEvent(ev); - } - // Intercept the touch if the drag view has any vertical scroll. - // onTouchEvent will determine if the view should drag vertically. - else if (ady > mScrollTouchSlop) { - interceptTap = isDragViewUnder((int) x, (int) y); - } - } - - if ((ady > dragSlop && adx > ady) || !isDragViewUnder((int) x, (int) y)) { - mDragHelper.cancel(); - mIsUnableToDrag = true; - return false; - } - break; - } - } - - final boolean interceptForDrag = mDragHelper.shouldInterceptTouchEvent(ev); - - return interceptForDrag; - } - - @Override - public boolean onTouchEvent(MotionEvent ev) { - if (!mCanSlide || !mIsSlidingEnabled) { - return super.onTouchEvent(ev); - } - - mDragHelper.processTouchEvent(ev); - - final int action = ev.getAction(); - boolean wantTouchEvents = true; - - switch (action & MotionEventCompat.ACTION_MASK) { - case MotionEvent.ACTION_DOWN: { - final float x = ev.getX(); - final float y = ev.getY(); - mInitialMotionX = x; - mInitialMotionY = y; - break; - } - - case MotionEvent.ACTION_UP: { - final float x = ev.getX(); - final float y = ev.getY(); - final float dx = x - mInitialMotionX; - final float dy = y - mInitialMotionY; - final int slop = mDragHelper.getTouchSlop(); - View dragView = mDragView != null ? mDragView : mSlideableView; - if (dx * dx + dy * dy < slop * slop && - isDragViewUnder((int) x, (int) y)) { - dragView.playSoundEffect(SoundEffectConstants.CLICK); - if (!isExpanded() && !isAnchored()) { - expandPane(mAnchorPoint); - } else if (!isAnchored()) { - expandPane(mAnchorPoint); - } else { - collapsePane(); - } - break; - } - break; - } - } - - return wantTouchEvents; - } - - private boolean isDragViewUnder(int x, int y) { - View dragView = mDragView != null ? mDragView : mSlideableView; - if (dragView == null) return false; - int[] viewLocation = new int[2]; - dragView.getLocationOnScreen(viewLocation); - int[] parentLocation = new int[2]; - this.getLocationOnScreen(parentLocation); - int screenX = parentLocation[0] + x; - int screenY = parentLocation[1] + y; - return screenX >= viewLocation[0] && screenX < viewLocation[0] + dragView.getWidth() && - screenY >= viewLocation[1] && screenY < viewLocation[1] + dragView.getHeight(); - } - - private boolean expandPane(View pane, int initialVelocity, float mSlideOffset) { - if (mFirstLayout || smoothSlideTo(mSlideOffset, initialVelocity)) { - return true; - } - return false; - } - - private boolean collapsePane(View pane, int initialVelocity) { - if (mFirstLayout || smoothSlideTo(1.f, initialVelocity)) { - return true; - } - return false; - } - - private int getSlidingTop() { - if (mSlideableView != null) { - return getMeasuredHeight() - getPaddingBottom() - mSlideableView.getMeasuredHeight(); - } - - return getMeasuredHeight() - getPaddingBottom(); - } - - /** - * Collapse the sliding pane if it is currently slideable. If first layout - * has already completed this will animate. - * - * @return true if the pane was slideable and is now collapsed/in the process of collapsing - */ - public boolean collapsePane() { - return collapsePane(mSlideableView, 0); - } - - /** - * Expand the sliding pane if it is currently slideable. If first layout - * has already completed this will animate. - * - * @return true if the pane was slideable and is now expanded/in the process of expading - */ - public boolean expandPane() { - return expandPane(0); - } - - /** - * Partially expand the sliding pane up to a specific offset - * - * @param mSlideOffset Value between 0 and 1, where 0 is completely expanded. - * @return true if the pane was slideable and is now expanded/in the process of expading - */ - public boolean expandPane(float mSlideOffset) { - if (!isPaneVisible()) { - showPane(); - } - return expandPane(mSlideableView, 0, mSlideOffset); - } - - /** - * Check if the layout is completely expanded. - * - * @return true if sliding panels are completely expanded - */ - public boolean isExpanded() { - return mSlideState == SlideState.EXPANDED; - } - - /** - * Check if the layout is anchored in an intermediate point. - * - * @return true if sliding panels are anchored - */ - public boolean isAnchored() { - return mSlideState == SlideState.ANCHORED; - } - - /** - * Check if the content in this layout cannot fully fit side by side and therefore - * the content pane can be slid back and forth. - * - * @return true if content in this layout can be expanded - */ - public boolean isSlideable() { - return mCanSlide; - } - - public boolean isPaneVisible() { - if (getChildCount() < 2) { - return false; - } - View slidingPane = getChildAt(1); - return slidingPane.getVisibility() == View.VISIBLE; - } - - public void showPane() { - if (getChildCount() < 2) { - return; - } - View slidingPane = getChildAt(1); - slidingPane.setVisibility(View.VISIBLE); - requestLayout(); - } - - public void hidePane() { - if (mSlideableView == null) { - return; - } - mSlideableView.setVisibility(View.GONE); - requestLayout(); - } - - private void onPanelDragged(int newTop) { - final int topBound = getSlidingTop(); - mSlideOffset = mIsSlidingUp - ? (float) (newTop - topBound) / mSlideRange - : (float) (topBound - newTop) / mSlideRange; - if (mParallaxBy != 0) { - parallaxOtherViews(mSlideOffset); - } - dispatchOnPanelSlide(mSlideableView); - } - - @Override - protected boolean drawChild(Canvas canvas, View child, long drawingTime) { - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - boolean result; - final int save = canvas.save(Canvas.CLIP_SAVE_FLAG); - - boolean drawScrim = false; - - if (mCanSlide && !lp.slideable && mSlideableView != null) { - // Clip against the slider; no sense drawing what will immediately be covered. - canvas.getClipBounds(mTmpRect); - if (mIsSlidingUp) { - mTmpRect.bottom = Math.min(mTmpRect.bottom, mSlideableView.getTop()); - } else { - mTmpRect.top = Math.max(mTmpRect.top, mSlideableView.getBottom()); - } - canvas.clipRect(mTmpRect); - if (mSlideOffset < 1) { - drawScrim = true; - } - } - - result = super.drawChild(canvas, child, drawingTime); - canvas.restoreToCount(save); - - if (drawScrim) { - final int baseAlpha = (mCoveredFadeColor & 0xff000000) >>> 24; - final int imag = (int) (baseAlpha * (1 - mSlideOffset)); - final int color = imag << 24 | (mCoveredFadeColor & 0xffffff); - mCoveredFadePaint.setColor(color); - canvas.drawRect(mTmpRect, mCoveredFadePaint); - } - - return result; - } - - /** - * Smoothly animate mDraggingPane to the target X position within its range. - * - * @param slideOffset position to animate to - * @param velocity initial velocity in case of fling, or 0. - */ - boolean smoothSlideTo(float slideOffset, int velocity) { - if (!mCanSlide) { - // Nothing to do. - return false; - } - - final int topBound = getSlidingTop(); - int y = mIsSlidingUp - ? (int) (topBound + slideOffset * mSlideRange) - : (int) (topBound - slideOffset * mSlideRange); - - if (mDragHelper.smoothSlideViewTo(mSlideableView, mSlideableView.getLeft(), y)) { - setAllChildrenVisible(); - ViewCompat.postInvalidateOnAnimation(this); - return true; - } - return false; - } - - @Override - public void computeScroll() { - if (mDragHelper.continueSettling(true)) { - if (!mCanSlide) { - mDragHelper.abort(); - return; - } - - ViewCompat.postInvalidateOnAnimation(this); - } - } - - @Override - public void draw(Canvas c) { - super.draw(c); - - if (mSlideableView == null) { - // No need to draw a shadow if we don't have one. - return; - } - - final int right = mSlideableView.getRight(); - final int top; - final int bottom; - if (mIsSlidingUp) { - top = mSlideableView.getTop() - mShadowHeight; - bottom = mSlideableView.getTop(); - } else { - top = mSlideableView.getBottom(); - bottom = mSlideableView.getBottom() + mShadowHeight; - } - final int left = mSlideableView.getLeft(); - - if (mShadowDrawable != null) { - mShadowDrawable.setBounds(left, top, right, bottom); - mShadowDrawable.draw(c); - } - } - - private void parallaxOtherViews(float slideOffset) { - final LayoutParams slideLp = (LayoutParams) mSlideableView.getLayoutParams(); - final int childCount = getChildCount(); - for (int i = 0; i < childCount; i++) { - final View v = getChildAt(i); - if (v == mSlideableView) continue; - - final int oldOffset = (int) ((1 - mParallaxOffset) * mParallaxBy); - mParallaxOffset = slideOffset; - final int newOffset = (int) ((1 - slideOffset) * mParallaxBy); - final int dx = mIsSlidingUp ? oldOffset - newOffset : newOffset - oldOffset; - - v.offsetTopAndBottom(dx); - } - } - - /** - * Tests scrollability within child views of v given a delta of dx. - * - * @param v View to test for horizontal scrollability - * @param checkV Whether the view v passed should itself be checked for scrollability (true), - * or just its children (false). - * @param dx Delta scrolled in pixels - * @param x X coordinate of the active touch point - * @param y Y coordinate of the active touch point - * @return true if child views of v can be scrolled by delta of dx. - */ - protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { - if (v instanceof ViewGroup) { - final ViewGroup group = (ViewGroup) v; - final int scrollX = v.getScrollX(); - final int scrollY = v.getScrollY(); - final int count = group.getChildCount(); - // Count backwards - let topmost views consume scroll distance first. - for (int i = count - 1; i >= 0; i--) { - final View child = group.getChildAt(i); - if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() && - y + scrollY >= child.getTop() && y + scrollY < child.getBottom() && - canScroll(child, true, dx, x + scrollX - child.getLeft(), - y + scrollY - child.getTop())) { - return true; - } - } - } - return checkV && ViewCompat.canScrollHorizontally(v, -dx); - } - - - @Override - protected ViewGroup.LayoutParams generateDefaultLayoutParams() { - return new LayoutParams(); - } - - @Override - protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { - return p instanceof MarginLayoutParams - ? new LayoutParams((MarginLayoutParams) p) - : new LayoutParams(p); - } - - @Override - protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { - return p instanceof LayoutParams && super.checkLayoutParams(p); - } - - @Override - public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { - return new LayoutParams(getContext(), attrs); - } - - @Override - protected Parcelable onSaveInstanceState() { - Parcelable superState = super.onSaveInstanceState(); - - SavedState ss = new SavedState(superState); - ss.mSlideState = mSlideState; - - return ss; - } - - @Override - protected void onRestoreInstanceState(Parcelable state) { - SavedState ss = (SavedState) state; - super.onRestoreInstanceState(ss.getSuperState()); - mSlideState = ss.mSlideState; - } - - private class DragHelperCallback extends ViewDragHelper.Callback { - - @Override - public boolean tryCaptureView(View child, int pointerId) { - if (mIsUnableToDrag) { - return false; - } - - return ((LayoutParams) child.getLayoutParams()).slideable; - } - - @Override - public void onViewDragStateChanged(int state) { - int anchoredTop = (int)(mAnchorPoint*mSlideRange); - - if (mDragHelper.getViewDragState() == ViewDragHelper.STATE_IDLE) { - if (mSlideOffset == 0) { - if (mSlideState != SlideState.EXPANDED) { - updateObscuredViewVisibility(); - dispatchOnPanelExpanded(mSlideableView); - mSlideState = SlideState.EXPANDED; - } - } else if (mSlideOffset == (float)anchoredTop/(float)mSlideRange) { - if (mSlideState != SlideState.ANCHORED) { - updateObscuredViewVisibility(); - dispatchOnPanelAnchored(mSlideableView); - mSlideState = SlideState.ANCHORED; - } - } else if (mSlideState != SlideState.COLLAPSED) { - dispatchOnPanelCollapsed(mSlideableView); - mSlideState = SlideState.COLLAPSED; - } - } - } - - @Override - public void onViewCaptured(View capturedChild, int activePointerId) { - // Make all child views visible in preparation for sliding things around - setAllChildrenVisible(); - } - - @Override - public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) { - onPanelDragged(top); - invalidate(); - } - - @Override - public void onViewReleased(View releasedChild, float xvel, float yvel) { - int top = mIsSlidingUp - ? getSlidingTop() - : getSlidingTop() - mSlideRange; - - if (mAnchorPoint != 0) { - int anchoredTop; - float anchorOffset; - if (mIsSlidingUp) { - anchoredTop = (int)(mAnchorPoint*mSlideRange); - anchorOffset = (float)anchoredTop/(float)mSlideRange; - } else { - anchoredTop = mPanelHeight - (int)(mAnchorPoint*mSlideRange); - anchorOffset = (float)(mPanelHeight - anchoredTop)/(float)mSlideRange; - } - - if (yvel > 0 || (yvel == 0 && mSlideOffset >= (1f+anchorOffset)/2)) { - top += mSlideRange; - } else if (yvel == 0 && mSlideOffset < (1f+anchorOffset)/2 - && mSlideOffset >= anchorOffset/2) { - top += mSlideRange * mAnchorPoint; - } - - } else if (yvel > 0 || (yvel == 0 && mSlideOffset > 0.5f)) { - top += mSlideRange; - } - - mDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top); - invalidate(); - } - - @Override - public int getViewVerticalDragRange(View child) { - return mSlideRange; - } - - @Override - public int clampViewPositionVertical(View child, int top, int dy) { - final int topBound; - final int bottomBound; - if (mIsSlidingUp) { - topBound = getSlidingTop(); - bottomBound = topBound + mSlideRange; - } else { - bottomBound = getPaddingTop(); - topBound = bottomBound - mSlideRange; - } - - return Math.min(Math.max(top, topBound), bottomBound); - } - } - - public static class LayoutParams extends ViewGroup.MarginLayoutParams { - private static final int[] ATTRS = new int[] { - android.R.attr.layout_weight - }; - - /** - * True if this pane is the slideable pane in the layout. - */ - boolean slideable; - - /** - * True if this view should be drawn dimmed - * when it's been offset from its default position. - */ - boolean dimWhenOffset; - - Paint dimPaint; - - public LayoutParams() { - super(MATCH_PARENT, MATCH_PARENT); - } - - public LayoutParams(int width, int height) { - super(width, height); - } - - public LayoutParams(android.view.ViewGroup.LayoutParams source) { - super(source); - } - - public LayoutParams(MarginLayoutParams source) { - super(source); - } - - public LayoutParams(LayoutParams source) { - super(source); - } - - public LayoutParams(Context c, AttributeSet attrs) { - super(c, attrs); - - final TypedArray a = c.obtainStyledAttributes(attrs, ATTRS); - a.recycle(); - } - - } - - static class SavedState extends BaseSavedState { - SlideState mSlideState; - - SavedState(Parcelable superState) { - super(superState); - } - - private SavedState(Parcel in) { - super(in); - try { - mSlideState = Enum.valueOf(SlideState.class, in.readString()); - } catch (IllegalArgumentException e) { - mSlideState = SlideState.COLLAPSED; - } - } - - @Override - public void writeToParcel(Parcel out, int flags) { - super.writeToParcel(out, flags); - out.writeString(mSlideState.toString()); - } - - public static final Parcelable.Creator<SavedState> CREATOR = - new Parcelable.Creator<SavedState>() { - @Override - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } - - @Override - public SavedState[] newArray(int size) { - return new SavedState[size]; - } - }; - } -}
\ No newline at end of file diff --git a/src/org/cyanogenmod/theme/chooser/ChooserDetailFragment.java b/src/org/cyanogenmod/theme/chooser/ChooserDetailFragment.java index 75fe303..155e800 100644 --- a/src/org/cyanogenmod/theme/chooser/ChooserDetailFragment.java +++ b/src/org/cyanogenmod/theme/chooser/ChooserDetailFragment.java @@ -46,7 +46,7 @@ import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.TextView; -import com.sothree.slidinguppanel.SlidingupPanelLayout; +import org.cyanogenmod.theme.util.ChooserDetailScrollView; import java.util.ArrayList; import java.util.Collections; @@ -62,13 +62,34 @@ public class ChooserDetailFragment extends Fragment implements LoaderManager.Loa private static final int LOADER_ID_THEME_INFO = 0; private static final int LOADER_ID_APPLIED_THEME = 1; + // Drawer States + private static final int DRAWER_CLOSED = 0; + private static final int DRAWER_PARTIALLY_OPEN = 1; + private static final int DRAWER_MOSTLY_OPEN = 2; + private static final int DRAWER_OPEN = 3; + + // Threshold values in "percentage scrolled" to determine what state the drawer is in + // ex: User opens up the drawer a little bit, taking up 10% of the visible space. The + // drawer is now considered partially open + // because CLOSED_THRESHOLD < 10% < PARTIAL_OPEN_THRESHOLD + private static final int DRAWER_CLOSED_THRESHOLD = 5; + private static final int DRAWER_PARTIALLY_OPEN_THRESHOLD = 25; + private static final int DRAWER_MOSTLY_OPEN_THRESHOLD = 90; + private static final int DRAWER_OPEN_THRESHOLD = 100; + + // Where to scroll when moving to a new state + private static final int DRAWER_CLOSED_SCROLL_AMOUNT = 0; + private static final int DRAWER_PARTIALLY_OPEN_AMOUNT = 25; + private static final int DRAWER_MOSTLY_OPEN_AMOUNT = 75; + private static final int DRAWER_FULLY_OPEN_AMOUNT = 100; + private TextView mTitle; private TextView mAuthor; private Button mApply; private ViewPager mPager; private ThemeDetailPagerAdapter mPagerAdapter; private String mPkgName; - private SlidingupPanelLayout mSlidingPanel; + private ChooserDetailScrollView mSlidingPanel; private Handler mHandler; private Cursor mAppliedThemeCursor; @@ -120,8 +141,28 @@ public class ChooserDetailFragment extends Fragment implements LoaderManager.Loa mTitle = (TextView) v.findViewById(R.id.title); mAuthor = (TextView) v.findViewById(R.id.author); mPager = (ViewPager) v.findViewById(R.id.pager); + mPager.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View view) { + int state = getDrawerState(); + switch(state) { + case DRAWER_CLOSED: + smoothScrollDrawerTo(DRAWER_PARTIALLY_OPEN); + break; + case DRAWER_PARTIALLY_OPEN: + case DRAWER_MOSTLY_OPEN: + smoothScrollDrawerTo(DRAWER_OPEN); + break; + case DRAWER_OPEN: + smoothScrollDrawerTo(DRAWER_CLOSED); + break; + } + } + }); + mPagerAdapter = new ThemeDetailPagerAdapter(getChildFragmentManager()); mPager.setAdapter(mPagerAdapter); + mApply = (Button) v.findViewById(R.id.apply); mApply.setOnClickListener(new OnClickListener() { @@ -132,7 +173,7 @@ public class ChooserDetailFragment extends Fragment implements LoaderManager.Loa } }); - mSlidingPanel = (SlidingupPanelLayout) v.findViewById(R.id.sliding_layout); + mSlidingPanel = (ChooserDetailScrollView) v.findViewById(R.id.sliding_layout); // Find all the checkboxes for theme components (ex wallpaper) for (Map.Entry<String, Integer> entry : sComponentToId.entrySet()) { @@ -194,7 +235,9 @@ public class ChooserDetailFragment extends Fragment implements LoaderManager.Loa private Runnable mShowSlidingPanelRunnable = new Runnable() { @Override public void run() { - mSlidingPanel.expandPane(mSlidingPanel.getAnchorPoint()); + // Arbitrarily scroll a bit at the start + int height = mSlidingPanel.getHeight() / 4; + mSlidingPanel.smoothScrollTo(0, height); } }; @@ -523,4 +566,54 @@ public class ChooserDetailFragment extends Fragment implements LoaderManager.Loa return fragment; } } + + private void smoothScrollDrawerTo(int drawerState) { + int scrollPercentage = 0; + switch(drawerState) { + case DRAWER_CLOSED: + scrollPercentage = DRAWER_CLOSED_SCROLL_AMOUNT; + break; + case DRAWER_PARTIALLY_OPEN: + scrollPercentage = DRAWER_PARTIALLY_OPEN_AMOUNT; + break; + case DRAWER_MOSTLY_OPEN: + scrollPercentage = DRAWER_MOSTLY_OPEN_AMOUNT; + break; + case DRAWER_OPEN: + scrollPercentage = DRAWER_FULLY_OPEN_AMOUNT; + break; + default: + throw new IllegalArgumentException("Bad drawer state: " + drawerState); + } + + int visibleHeight = mSlidingPanel.getHeight(); + View handle = mSlidingPanel.getHandle(); + visibleHeight -= handle.getHeight(); + + int scrollY = scrollPercentage * visibleHeight / 100; + mSlidingPanel.smoothScrollTo(0, scrollY); + } + + private int getDrawerState() { + // Scroll between 3 different heights when pager is clicked + int visibleHeight = mSlidingPanel.getHeight(); + View handle = mSlidingPanel.getHandle(); + visibleHeight -= handle.getHeight(); + int scrollY = mSlidingPanel.getScrollY(); + int percentageScrolled = (scrollY * 100) / (visibleHeight); + + //Check if we are bottom of scroll + View view = (View) mSlidingPanel.getChildAt(0); + boolean isAtBottom = (view.getBottom() - (mSlidingPanel.getHeight() + scrollY)) == 0; + + if (percentageScrolled < DRAWER_CLOSED_THRESHOLD && !isAtBottom) { + return DRAWER_CLOSED; + } else if (percentageScrolled < DRAWER_PARTIALLY_OPEN_THRESHOLD && !isAtBottom) { + return DRAWER_PARTIALLY_OPEN; + } else if (percentageScrolled < DRAWER_MOSTLY_OPEN_THRESHOLD && !isAtBottom) { + return DRAWER_MOSTLY_OPEN; + } + + return DRAWER_OPEN; + } } diff --git a/src/org/cyanogenmod/theme/util/ChooserDetailLinearLayout.java b/src/org/cyanogenmod/theme/util/ChooserDetailLinearLayout.java new file mode 100644 index 0000000..59b7340 --- /dev/null +++ b/src/org/cyanogenmod/theme/util/ChooserDetailLinearLayout.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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 org.cyanogenmod.theme.util; + +import android.content.Context; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; + + +/** + * This class will layout its children in a very specific way. It assumes it is a child of a + * scroll view. + * + * The first two children are assumed to be the "theme preview" and the "drawer handle" which will + * be visible. These children will be restricted in their size since they must be entirely visible. + * The third child will be shown as the user scrolls down and can take as much space as it needs. + */ +public class ChooserDetailLinearLayout extends LinearLayout { + + // The smaller this value the greater the effect + public final int PARALLAX_CONSTANT = 2; + + // Child indices + public final int THEME_PREVIEW_INDEX = 0; + public final int DRAWER_HANDLE_INDEX = 1; + public final int DRAWER_CONTENT_INDEX = 2; + + private ViewPager mPager; + + public ChooserDetailLinearLayout(Context context) { + super(context); + } + + public ChooserDetailLinearLayout(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ChooserDetailLinearLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Assumes the heightMeasureSpec will be exact. This exact data will be used + * to determine the visible page size. + */ + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int heightSize = MeasureSpec.getSize(heightMeasureSpec); + + // Drawer handle is just author/title. Should get whatever space it needs + View drawerHandle = getChildAt(DRAWER_HANDLE_INDEX); + measureChildWithMargins(drawerHandle, widthMeasureSpec, 0, heightMeasureSpec, 0); + + // Give the theme preview the remainder + View themePreview = getChildAt(THEME_PREVIEW_INDEX); + int usedHeight = drawerHandle.getMeasuredHeight(); + measureChildWithMargins(themePreview, widthMeasureSpec, 0, heightMeasureSpec, usedHeight); + + // Give the drawer content as much as space as it needs. + View drawerContent = getChildAt(DRAWER_CONTENT_INDEX); + final MarginLayoutParams lp = (MarginLayoutParams) drawerContent.getLayoutParams(); + int childWidthSpec = getChildMeasureSpec(widthMeasureSpec, 0, lp.width); + int childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); + drawerContent.measure(childWidthSpec, childHeightSpec); + + // Measure ourself + int width = drawerHandle.getMeasuredWidth(); + int height = themePreview.getMeasuredHeight() + + drawerHandle.getMeasuredHeight() + drawerContent.getMeasuredHeight(); + setMeasuredDimension(width, height); + } + + public void onScrollChanged(int l, int t, int oldl, int oldt) { + // Give a parallax effect to the theme preview by scrolling in the + // opposite direction of the scrollview + int yScroll = (int) (-t / PARALLAX_CONSTANT); + getChildAt(THEME_PREVIEW_INDEX).scrollTo(0, yScroll); + } + + View getHandle() { + return getChildAt(DRAWER_HANDLE_INDEX); + } +} diff --git a/src/org/cyanogenmod/theme/util/ChooserDetailScrollView.java b/src/org/cyanogenmod/theme/util/ChooserDetailScrollView.java new file mode 100644 index 0000000..8734cae --- /dev/null +++ b/src/org/cyanogenmod/theme/util/ChooserDetailScrollView.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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 org.cyanogenmod.theme.util; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.GestureDetector; +import android.view.MotionEvent; +import android.view.View; +import android.widget.ScrollView; + +/** + * A custom ScrollView capable of alerting its children when a scroll has changed. + * + * This custom view will also measure its children differently. Normally a child in scrollview + * receives an UNSPECIFIED MeasureSpec so that the child will size itself as large as it needs to be. + * + * In our case we want to have a unique layout where the first full page of the scrollview + * has very particular content + * ie the "drawer title/author", and the viewpager taking the remaining part of the screen", with the + * theme component checkboxes taking as much space as it needs. + * + **/ +public class ChooserDetailScrollView extends ScrollView { + private float xDistance; + private float yDistance; + private float lastX; + private float lastY; + + public ChooserDetailScrollView(Context context) { + super(context); + } + + public ChooserDetailScrollView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public ChooserDetailScrollView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, + int parentHeightMeasureSpec, int heightUsed) { + final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); + + final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, + mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + + widthUsed, lp.width); + child.measure(childWidthMeasureSpec, parentHeightMeasureSpec); + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + xDistance = yDistance = 0f; + lastX = ev.getX(); + lastY = ev.getY(); + break; + case MotionEvent.ACTION_MOVE: + final float curX = ev.getX(); + final float curY = ev.getY(); + xDistance = Math.abs(curX - lastX); + yDistance = Math.abs(curY - lastY); + lastX = curX; + lastY = curY; + if(xDistance > yDistance) return false; + } + + return super.onInterceptTouchEvent(ev); + } + + @Override + protected void onScrollChanged(int l, int t, int oldl, int oldt) { + super.onScrollChanged(l, t, oldl, oldt); + ((ChooserDetailLinearLayout)getChildAt(0)).onScrollChanged(l, t, oldl, oldt); + } + + public View getHandle() { + return ((ChooserDetailLinearLayout)getChildAt(0)).getHandle(); + } +} diff --git a/src/org/cyanogenmod/theme/util/ClickableViewPager.java b/src/org/cyanogenmod/theme/util/ClickableViewPager.java new file mode 100644 index 0000000..98364ea --- /dev/null +++ b/src/org/cyanogenmod/theme/util/ClickableViewPager.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2014 The CyanogenMod 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 org.cyanogenmod.theme.util; + +import android.content.Context; +import android.support.v4.view.ViewPager; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.ViewConfiguration; + +/** + * Adds click functionality for the view pager since it not normally clickable + * and focus/clickable attributes have no impact on it. + */ +public class ClickableViewPager extends ViewPager { + private boolean mIsDragging = false; + private float mSlop; + private float mLastX; + private float mLastY; + + public ClickableViewPager(Context context) { + super(context); + initView(context); + } + + public ClickableViewPager(Context context, AttributeSet attrs) { + super(context, attrs); + initView(context); + } + + private void initView(Context context) { + mSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + } + + @Override + public boolean onTouchEvent(MotionEvent ev) { + switch (ev.getAction()) { + case MotionEvent.ACTION_DOWN: + mLastX = ev.getX(); + mLastY = ev.getY(); + break; + case MotionEvent.ACTION_MOVE: + float xDist = Math.abs(mLastX - ev.getX()); + float yDist = Math.abs(mLastY - ev.getY()); + if (xDist > mSlop || yDist > mSlop) { + mIsDragging = true; + } + break; + case MotionEvent.ACTION_CANCEL: + mIsDragging = false; + break; + case MotionEvent.ACTION_UP: + if (!mIsDragging) { + performClick(); + } + mIsDragging = false; + break; + } + return super.onTouchEvent(ev); + } +}
\ No newline at end of file |