package info.guardianproject.securereaderinterface.widgets; import info.guardianproject.securereaderinterface.models.ViewPagerIndicator; import android.content.Context; import android.database.DataSetObserver; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.ViewParent; public class NestedViewPager extends ViewPager { private final static int CLICK_SENSITIVITY = 5; private boolean mPropagateClicks; private ViewPagerIndicator mViewPagerIndicator; private OnPageChangeListener mOnPageChangeListener; private float mPositionOffset; public NestedViewPager(Context context, AttributeSet attrs) { super(context, attrs); super.setOnPageChangeListener(new OnPageChangeListener() { @Override public void onPageScrollStateChanged(int arg0) { if (mOnPageChangeListener != null) mOnPageChangeListener.onPageScrollStateChanged(arg0); } @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { mPositionOffset = positionOffset; if (mOnPageChangeListener != null) mOnPageChangeListener.onPageScrolled(position, positionOffset, positionOffsetPixels); } @Override public void onPageSelected(int arg0) { if (mViewPagerIndicator != null) { mViewPagerIndicator.onCurrentChanged(arg0); } if (mOnPageChangeListener != null) mOnPageChangeListener.onPageSelected(arg0); } }); } /** * If true this NestedViewPager will try to find its nearest parent with * clickable=true and call setPressed/onClicked on that View. * * @return true if click events are propagated. */ public boolean propagateClicks() { return mPropagateClicks; } /** * Sets whether this NestedViewPager will try to find its nearest parent * with clickable=true and call setPressed/onClicked on that View. * * @param propagate * True to propagate events. Default is false. */ public void setPropagateClicks(boolean propagate) { mPropagateClicks = propagate; } private float downX = 0; private float downY = 0; @Override public boolean canScrollHorizontally(int direction) { if (direction < 0 && getCurrentItem() == 0 && mPositionOffset == 0) { return false; } else if (direction > 0 && mPositionOffset == 0) { if (getAdapter() != null) { int current = getCurrentItem(); int count = getAdapter().getCount(); if (current >= (count - 1)) { return false; } } } return true; } @Override public boolean onInterceptTouchEvent(MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: downX = event.getX(); downY = event.getY(); if (mPropagateClicks) { propagatePressed(true); return true; } break; } return super.onInterceptTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); switch (action) { case MotionEvent.ACTION_DOWN: downX = event.getX(); downY = event.getY(); if (mPropagateClicks) propagatePressed(true); break; case MotionEvent.ACTION_MOVE: try { if (mPropagateClicks) { float xDeltaTotal = event.getX() - downX; float yDeltaTotal = event.getY() - downY; if (Math.abs(xDeltaTotal) >= CLICK_SENSITIVITY || Math.abs(yDeltaTotal) >= CLICK_SENSITIVITY) { propagatePressed(false); } } float xDelta = event.getX() - downX; float yDelta = event.getY() - downY; if (event.getHistorySize() > 0) { xDelta = event.getX() - event.getHistoricalX(0); yDelta = event.getY() - event.getHistoricalY(0); } if (Math.abs(xDelta) > Math.abs(yDelta)) { if (xDelta > 0 && getCurrentItem() == 0 && mPositionOffset == 0) { return false; } else if (xDelta < 0 && mPositionOffset == 0) { if (getAdapter() != null) { int current = getCurrentItem(); int count = getAdapter().getCount(); if (current >= (count - 1)) { return false; } } } NestedViewPager.this.getParent().requestDisallowInterceptTouchEvent(true); } } catch (Exception e) { // nothing } break; case MotionEvent.ACTION_UP: if (mPropagateClicks) { propagatePressed(false); float xDelta = event.getX() - downX; float yDelta = event.getY() - downY; if (Math.abs(xDelta) < CLICK_SENSITIVITY && Math.abs(yDelta) < CLICK_SENSITIVITY) { propagateClicked(); } } break; } return super.onTouchEvent(event); } public void setViewPagerIndicator(ViewPagerIndicator viewPagerIndicator) { mViewPagerIndicator = viewPagerIndicator; updateViewPagerIndicator(); } /** * If a ViewPagerIndicator is set (using * {@link #setViewPagerIndicator(ViewPagerIndicator)}) this will update it * with total number of items and the current item. */ private void updateViewPagerIndicator() { if (mViewPagerIndicator != null) { if (getAdapter() != null) mViewPagerIndicator.onTotalChanged(getAdapter().getCount()); else mViewPagerIndicator.onTotalChanged(0); mViewPagerIndicator.onCurrentChanged(this.getCurrentItem()); } } private final DataSetObserver mDataSetObserver = new DataSetObserver() { @Override public void onChanged() { if (mViewPagerIndicator != null) { if (getAdapter() != null) mViewPagerIndicator.onTotalChanged(getAdapter().getCount()); } } }; @Override public void setAdapter(PagerAdapter adapter) { // Unregister old, if any if (getAdapter() != null) getAdapter().unregisterDataSetObserver(mDataSetObserver); if (adapter != null) adapter.registerDataSetObserver(mDataSetObserver); super.setAdapter(adapter); updateViewPagerIndicator(); } @Override public void setOnPageChangeListener(OnPageChangeListener listener) { mOnPageChangeListener = listener; } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); if (getAdapter() != null) getAdapter().notifyDataSetChanged(); } private Runnable propagatePressed; private void propagatePressed(boolean pressed) { if (pressed) { if (propagatePressed == null) { propagatePressed = new Runnable() { @Override public void run() { _propagatePressed(true); } }; } this.removeCallbacks(propagatePressed); this.postDelayed(propagatePressed, 200); } else { if (propagatePressed != null) this.removeCallbacks(propagatePressed); _propagatePressed(false); } } private void _propagatePressed(boolean pressed) { ViewParent parent = getParent(); while (parent != null && parent instanceof View) { if (((View) parent).isClickable()) { ((View) parent).setPressed(pressed); break; } parent = parent.getParent(); } } private void propagateClicked() { ViewParent parent = getParent(); while (parent != null && parent instanceof View) { if (((View) parent).isClickable()) { ((View) parent).performClick(); break; } parent = parent.getParent(); } } }