content: /*
   * Copyright (C) 2011 Cyril Mottier (http://www.cyrilmottier.com)
   *
   * 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 greendroid.widget;
  
  
  import java.util.LinkedList;
  import java.util.Queue;
  
  import android.content.Context;
  import android.database.DataSetObserver;
  import android.os.Handler;
  import android.os.Parcel;
  import android.os.Parcelable;
  import android.util.AttributeSet;
  import android.util.DisplayMetrics;
  import android.util.SparseArray;
  import android.view.MotionEvent;
  import android.view.VelocityTracker;
  import android.view.View;
  import android.view.ViewConfiguration;
  import android.view.ViewGroup;
  import android.view.animation.DecelerateInterpolator;
  import android.webkit.WebView;
  import android.widget.Scroller;
  
  import com.androidquery.simplefeed.R;
  import com.androidquery.util.AQUtility;
  
  /**
   * <p>
   * A View that shows items in a "paged" manner. Pages can be scrolled
   * horizontally by swiping the View. The PagedView uses a reuse mechanism
   * similar to the one used by the ListView widget. Pages come from a
   * {@link PagedAdapter}.
   * </p>
   * <p>
   * Clients may listen to PagedView changes (scrolling, page change, etc.) using
   * an {@link OnPagedViewChangeListener} .
   * </p>
   * <p>
   * It is usually a good idea to show the user which page is currently on screen.
   * This can be easily done with a {@link PageIndicator}.
   * </p>
   * 
   * @author Cyril Mottier
   */
  public class PagedView extends ViewGroup {
  
      private static final String LOG_TAG = PagedView.class.getSimpleName();
  
      /**
       * Clients may listen to changes occurring on a PagedView via this
       * interface.
       * 
       * @author Cyril Mottier
       */
      public interface OnPagedViewChangeListener {
  
          /**
           * Notify the client the current page has changed.
           * 
           * @param pagedView The PagedView that changed its current page
           * @param previousPage The previously selected page
           * @param newPage The newly selected page
           */
          void onPageChanged(PagedView pagedView, int previousPage, int newPage);
  
          /**
           * Notify the client the user started tracking.
           * 
           * @param pagedView The PagedView the user started to track.
           */
          void onStartTracking(PagedView pagedView);
  
          /**
           * Notify the client the user ended tracking.
           * 
           * @param pagedView The PagedView the user ended to track.
           */
          void onStopTracking(PagedView pagedView);
      }
  
      private static final int INVALID_PAGE = -1;
      private static final int MINIMUM_PAGE_CHANGE_VELOCITY = 500;
      private static final int VELOCITY_UNITS = 1000;
      private static final int FRAME_RATE = 1000 / 60;
  
      private final Handler mHandler = new Handler();
  
      private int mPageCount;
      private int mCurrentPage;
      private int mTargetPage = INVALID_PAGE;
  
      private int mPagingTouchSlop;
      private int mMinimumVelocity;
      private int mMaximumVelocity;
      private int mPageSlop;
  
      private boolean mIsBeingDragged;
  
      private int mOffsetX;
      private int mStartMotionX;
      private int mStartOffsetX;
  
      private Scroller mScroller;
      private VelocityTracker mVelocityTracker;
  
      private OnPagedViewChangeListener mOnPageChangeListener;
  
      private PagedAdapter mAdapter;
  
      private SparseArray<View> mActiveViews = new SparseArray<View>();
      private Queue<View> mRecycler = new LinkedList<View>();
  
      public PagedView(Context context) {
          this(context, null);
      }
  
      public PagedView(Context context, AttributeSet attrs) {
          this(context, attrs, 0);
      }
  
      public PagedView(Context context, AttributeSet attrs, int defStyle) {
          super(context, attrs, defStyle);
          initPagedView();
      }
  
      private void initPagedView() {
  
          final Context context = getContext();
  
          mScroller = new Scroller(context, new DecelerateInterpolator());
  
          final ViewConfiguration conf = ViewConfiguration.get(context);
          // getScaledPagingTouchSlop() only available in API Level 8
          mPagingTouchSlop = conf.getScaledTouchSlop() * 2;
          mMaximumVelocity = conf.getScaledMaximumFlingVelocity();
  
          final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
          mMinimumVelocity = (int) (metrics.density * MINIMUM_PAGE_CHANGE_VELOCITY + 0.5f);
          
          WebView wv = new WebView(context);
  		initScale = wv.getScale();
  		
  		AQUtility.debug("seed scale", initScale);
  		
      }
      
      private float initScale;
  
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  
          int widthMode = MeasureSpec.getMode(widthMeasureSpec);
          int heightMode = MeasureSpec.getMode(heightMeasureSpec);
          int widthSize = MeasureSpec.getSize(widthMeasureSpec);
          int heightSize = MeasureSpec.getSize(heightMeasureSpec);
  
          int childWidth = 0;
          int childHeight = 0;
  
          int itemCount = mAdapter == null ? 0 : mAdapter.getCount();
          if (itemCount > 0) {
  
              if (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED) {
  
                  final View child = obtainView(mCurrentPage);
  
                  measureChild(child, widthMeasureSpec, heightMeasureSpec);
                  childWidth = child.getMeasuredWidth();
                  childHeight = child.getMeasuredHeight();
              }
  
              if (widthMode == MeasureSpec.UNSPECIFIED) {
                  widthSize = childWidth;
              }
  
              if (heightMode == MeasureSpec.UNSPECIFIED) {
                  heightSize = childHeight;
              }
          }
  
          setMeasuredDimension(widthSize, heightSize);
      }
  
      @Override
      protected void onSizeChanged(int w, int h, int oldw, int oldh) {
          super.onSizeChanged(w, h, oldw, oldh);
          mPageSlop = (int) (w * 0.5);
          // Make sure the offset adapts itself to mCurrentPage
          mOffsetX = getOffsetForPage(mCurrentPage);
      }
  
      @Override
      protected void onLayout(boolean changed, int l, int t, int r, int b) {
  
          if (mPageCount <= 0) {
              return;
          }
  
          final int startPage = getPageForOffset(mOffsetX);
          final int endPage = getPageForOffset(mOffsetX - getWidth() + 1);
  
          recycleViews(startPage, endPage);
  
          for (int i = startPage; i <= endPage; i++) {
              View child = mActiveViews.get(i);
              if (child == null) {
                  child = obtainView(i);
              }
              setupView(child, i);
          }
      }
  
      private boolean isScrollable(){
      	
      	View view = mActiveViews.get(getCurrentPage());
      	
      	if(view != null){
      		WebView wv = (WebView) view.findViewById(R.id.web);
      		float scale = wv.getScale();    		
      		return scale == initScale;
      	}
      	
      	return true;
      }
      
      
      @Override
      public boolean onInterceptTouchEvent(MotionEvent ev) {
  
      	
      	
          /*
           * Shortcut the most recurring case: the user is in the dragging state
           * and he is moving his finger. We want to intercept this motion.
           */
          final int action = ev.getAction();
          if (action == MotionEvent.ACTION_MOVE && mIsBeingDragged) {
              return true;
          }
          
          
      	
  
          final int x = (int) ev.getX();
  
          switch (action) {
              case MotionEvent.ACTION_DOWN:
                  mStartMotionX = x;
                  /*
                   * If currently scrolling and user touches the screen, initiate
                   * drag; otherwise don't. mScroller.isFinished should be false
                   * when being flinged.
                   */
                  mIsBeingDragged = !mScroller.isFinished();
                  if (mIsBeingDragged) {
                      mScroller.forceFinished(true);
                      mHandler.removeCallbacks(mScrollerRunnable);
                      AQUtility.debug("e1");
                  }
                  break;
  
              case MotionEvent.ACTION_MOVE:
                  /*
                   * mIsBeingDragged == false, otherwise the shortcut would have
                   * caught it. Check whether the user has moved far enough from
                   * his original down touch.
                   */
  
                  final int xDiff = (int) Math.abs(x - mStartMotionX);
                  if (xDiff > mPagingTouchSlop && isScrollable()) {
                      mIsBeingDragged = true;
                      performStartTracking(x);
                      //AQUtility.debug("e2");
                  }
                  break;
  
              case MotionEvent.ACTION_CANCEL:
              case MotionEvent.ACTION_UP:
                  /*
                   * Release the drag
                   */
                  mIsBeingDragged = false;
                  break;
          }
  
          /*
           * Motion events are only intercepted during dragging mode.
           */
          return mIsBeingDragged;
      }
  
      @Override
      public boolean onTouchEvent(MotionEvent ev) {
  
          final int action = ev.getAction();
          final int x = (int) ev.getX();
  
          if (mVelocityTracker == null) {
              mVelocityTracker = VelocityTracker.obtain();
          }
          mVelocityTracker.addMovement(ev);
  
          switch (action) {
              case MotionEvent.ACTION_DOWN:
                  if (!mScroller.isFinished()) {
                      mScroller.forceFinished(true);
                      mHandler.removeCallbacks(mScrollerRunnable);
                  }
                  performStartTracking(x);
                  break;
  
              case MotionEvent.ACTION_MOVE:
                  // Scroll to follow the motion event
                  final int newOffset = mStartOffsetX - (mStartMotionX - x);
                  if (newOffset > 0 || newOffset < getOffsetForPage(mPageCount - 1)) {
                      mStartOffsetX = mOffsetX;
                      mStartMotionX = x;
                  } else {
                      setOffsetX(newOffset);
                  }
  
                  break;
  
              case MotionEvent.ACTION_UP:
              case MotionEvent.ACTION_CANCEL:
  
                  setOffsetX(mStartOffsetX - (mStartMotionX - x));
  
                  int direction = 0;
  
                  final int slop = mStartMotionX - x;
                  if (Math.abs(slop) > mPageSlop) {
                      direction = (slop > 0) ? 1 : -1;
                  } else {
                      mVelocityTracker.computeCurrentVelocity(VELOCITY_UNITS, mMaximumVelocity);
                      final int initialVelocity = (int) mVelocityTracker.getXVelocity();
                      if (Math.abs(initialVelocity) > mMinimumVelocity) {
                          direction = (initialVelocity > 0) ? -1 : 1;
                      }
                  }
  
                  if (mOnPageChangeListener != null) {
                      mOnPageChangeListener.onStopTracking(this);
                  }
  
                  smoothScrollToPage(getActualCurrentPage() + direction);
  
                  if (mVelocityTracker != null) {
                      mVelocityTracker.recycle();
                      mVelocityTracker = null;
                  }
  
                  break;
          }
  
          return true;
      }
  
      /**
       * Set a listener to be notified of changes that may occur in this
       * {@link PagedView}.
       * 
       * @param listener The listener to callback.
       */
      public void setOnPageChangeListener(OnPagedViewChangeListener listener) {
          mOnPageChangeListener = listener;
      }
  
      /**
       * Sets the {@link PagedAdapter} used to fill this {@link PagedView} with
       * some basic information : the number of displayed pages, the pages, etc.
       * 
       * @param adapter The {@link PagedAdapter} to set to this {@link PagedView}
       */
      public void setAdapter(PagedAdapter adapter) {
  
          if (null != mAdapter) {
              mAdapter.unregisterDataSetObserver(mDataSetObserver);
          }
  
          // Reset
          mRecycler.clear();
          mActiveViews.clear();
          removeAllViews();
  
          mAdapter = adapter;
  
          mTargetPage = INVALID_PAGE;
          mCurrentPage = 0;
          mOffsetX = 0;
  
          if (null != mAdapter) {
              mAdapter.registerDataSetObserver(mDataSetObserver);
              mPageCount = mAdapter.getCount();
          }
  
          requestLayout();
          invalidate();
      }
  
      /**
       * Returns the current page.
       * 
       * @return The current page
       */
      public int getCurrentPage() {
          return mCurrentPage;
      }
  
      private int getActualCurrentPage() {
          return mTargetPage != INVALID_PAGE ? mTargetPage : mCurrentPage;
      }
  
      /**
       * Initiate an animated scrolling from the current position to the given
       * page
       * 
       * @param page The page to scroll to.
       */
      public void smoothScrollToPage(int page) {
          scrollToPage(page, true);
      }
  
      /**
       * Initiate an animated scrolling to the next page
       */
      public void smoothScrollToNext() {
          smoothScrollToPage(getActualCurrentPage() + 1);
      }
  
      /**
       * Initiate an animated scrolling to the previous page
       */
      public void smoothScrollToPrevious() {
          smoothScrollToPage(getActualCurrentPage() - 1);
      }
  
      /**
       * Instantly moves the PagedView from the current position to the given
       * page.
       * 
       * @param page The page to scroll to.
       */
      public void scrollToPage(int page) {
          scrollToPage(page, false);
      }
  
      /**
       * Instantly moves to the next page
       */
      public void scrollToNext() {
          scrollToPage(getActualCurrentPage() + 1);
      }
  
      /**
       * Instantly moves to the previous page
       */
      public void scrollToPrevious() {
          scrollToPage(getActualCurrentPage() - 1);
      }
  
      private void scrollToPage(int page, boolean animated) {
  
          // Make sure page is bound to correct values
          page = Math.max(0, Math.min(page, mPageCount - 1));
  
          final int targetOffset = getOffsetForPage(page);
  
          final int dx = targetOffset - mOffsetX;
          if (dx == 0) {
              performPageChange(page);
              return;
          }
  
          if (animated) {
              mTargetPage = page;
              mScroller.startScroll(mOffsetX, 0, dx, 0);
              mHandler.post(mScrollerRunnable);
          } else {
              setOffsetX(targetOffset);
              performPageChange(page);
          }
      }
  
      private void setOffsetX(int offsetX) {
  
          if (offsetX == mOffsetX) {
              return;
          }
  
          final int startPage = getPageForOffset(offsetX);
          final int endPage = getPageForOffset(offsetX - getWidth() + 1);
  
          recycleViews(startPage, endPage);
  
          final int leftAndRightOffset = offsetX - mOffsetX;
          for (int i = startPage; i <= endPage; i++) {
  
              View child = mActiveViews.get(i);
              if (child == null) {
                  child = obtainView(i);
                  setupView(child, i);
              }
  
              child.offsetLeftAndRight(leftAndRightOffset);
          }
  
          mOffsetX = offsetX;
          invalidate();
      }
  
      private int getOffsetForPage(int page) {
          return -(page * getWidth());
      }
  
      private int getPageForOffset(int offset) {
          return -offset / getWidth();
      }
  
      private void recycleViews(int start, int end) {
          // [start, end] <=> range of pages that needs to be displayed
          final SparseArray<View> activeViews = mActiveViews;
  
          final int count = activeViews.size();
          for (int i = 0; i < count; i++) {
              final int key = activeViews.keyAt(i);
              if (key < start || key > end) {
                  final View recycled = activeViews.valueAt(i);
                  removeView(recycled);
                  mRecycler.add(recycled);
  
                  activeViews.delete(key);
              }
          }
      }
  
      private View obtainView(int position) {
          // Get a view from the recycler
          final View recycled = mRecycler.poll();
  
          View child = mAdapter.getView(position, recycled, this);
  
          if (child == null) {
              throw new NullPointerException("PagedAdapter.getView must return a non-null View");
          }
          
          /*
          if (recycled != null && child != recycled) {
              if (Config.GD_WARNING_LOGS_ENABLED) {
                  Log.w(LOG_TAG, "Not reusing the convertView may impact PagedView performance.");
              }
          }
           */
          addView(child);
          mActiveViews.put(position, child);
  
          return child;
      }
  
      private void setupView(View child, int position) {
  
          if (child == null) {
              return;
          }
  
          LayoutParams lp = child.getLayoutParams();
          if (lp == null) {
              lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);
          }
  
          // Measure the view
          final int childWidthSpec = getChildMeasureSpec(MeasureSpec.makeMeasureSpec(getWidth(), MeasureSpec.EXACTLY), 0, lp.width);
          final int childHeightSpec = getChildMeasureSpec(MeasureSpec.makeMeasureSpec(getHeight(), MeasureSpec.EXACTLY), 0, lp.height);
          child.measure(childWidthSpec, childHeightSpec);
  
          // Layout the view
          final int childLeft = mOffsetX - getOffsetForPage(position);
          child.layout(childLeft, 0, childLeft + child.getMeasuredWidth(), child.getMeasuredHeight());
      }
  
      private void performStartTracking(int startMotionX) {
          if (mOnPageChangeListener != null) {
              mOnPageChangeListener.onStartTracking(this);
          }
          mStartMotionX = startMotionX;
          mStartOffsetX = mOffsetX;
      }
  
      private void performPageChange(int newPage) {
          if (mCurrentPage != newPage) {
              if (mOnPageChangeListener != null) {
                  mOnPageChangeListener.onPageChanged(this, mCurrentPage, newPage);
              }
              mCurrentPage = newPage;
          }
      }
  
      static class SavedState extends BaseSavedState {
  
          int currentPage;
  
          SavedState(Parcelable superState) {
              super(superState);
          }
  
          private SavedState(Parcel in) {
              super(in);
              currentPage = in.readInt();
          }
  
          @Override
          public void writeToParcel(Parcel out, int flags) {
              super.writeToParcel(out, flags);
              out.writeInt(currentPage);
          }
  
          public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
              public SavedState createFromParcel(Parcel in) {
                  return new SavedState(in);
              }
  
              public SavedState[] newArray(int size) {
                  return new SavedState[size];
              }
          };
      }
  
      @Override
      public Parcelable onSaveInstanceState() {
          Parcelable superState = super.onSaveInstanceState();
          SavedState ss = new SavedState(superState);
  
          ss.currentPage = mCurrentPage;
  
          return ss;
      }
  
      @Override
      public void onRestoreInstanceState(Parcelable state) {
          SavedState ss = (SavedState) state;
          super.onRestoreInstanceState(ss.getSuperState());
  
          mCurrentPage = ss.currentPage;
      }
  
      private DataSetObserver mDataSetObserver = new DataSetObserver() {
  
          public void onInvalidated() {
              // Not handled
          };
  
          public void onChanged() {
              // TODO Cyril : When data has changed we should normally
              // look for the position that as the same id is case
              // Adapter.hasStableIds() returns true.
              final int currentPage = mCurrentPage;
              setAdapter(mAdapter);
              mCurrentPage = currentPage;
              setOffsetX(getOffsetForPage(currentPage));
          };
  
      };
  
      private Runnable mScrollerRunnable = new Runnable() {
          @Override
          public void run() {
              final Scroller scroller = mScroller;
              if (!scroller.isFinished()) {
                  scroller.computeScrollOffset();
                  setOffsetX(scroller.getCurrX());
                  mHandler.postDelayed(this, FRAME_RATE);
              } else {
                  performPageChange(mTargetPage);
              }
          }
      };
  
  }
  