summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/com/sothree/slidinguppanel/SlidingupPanelLayout.java1282
-rw-r--r--src/org/cyanogenmod/theme/chooser/ChooserDetailFragment.java101
-rw-r--r--src/org/cyanogenmod/theme/util/ChooserDetailLinearLayout.java98
-rw-r--r--src/org/cyanogenmod/theme/util/ChooserDetailScrollView.java96
-rw-r--r--src/org/cyanogenmod/theme/util/ClickableViewPager.java74
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