content: package com.origamilabs.library.views;
  
  /*
   * Copyright (C) 2012 The Android Open Source 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.
   *
   * modified by Maurycy Wojtowicz
   *
   */
  
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.HashSet;
  
  import android.annotation.TargetApi;
  import android.content.Context;
  import android.content.res.TypedArray;
  import android.database.DataSetObserver;
  import android.graphics.Canvas;
  import android.graphics.Rect;
  import android.graphics.drawable.Drawable;
  import android.graphics.drawable.TransitionDrawable;
  import android.os.Build;
  import android.os.Handler;
  import android.os.Parcel;
  import android.os.Parcelable;
  import android.support.v4.util.SparseArrayCompat;
  import android.support.v4.view.MotionEventCompat;
  import android.support.v4.view.VelocityTrackerCompat;
  import android.support.v4.view.ViewCompat;
  import android.support.v4.widget.EdgeEffectCompat;
  import android.util.AttributeSet;
  import android.util.Log;
  import android.util.SparseArray;
  import android.view.ContextMenu;
  import android.view.ContextMenu.ContextMenuInfo;
  import android.view.HapticFeedbackConstants;
  import android.view.MotionEvent;
  import android.view.SoundEffectConstants;
  import android.view.VelocityTracker;
  import android.view.View;
  import android.view.ViewConfiguration;
  import android.view.ViewGroup;
  import android.view.accessibility.AccessibilityEvent;
  import android.widget.ListAdapter;
  
  import com.origamilabs.library.R;
  
  /**
   * ListView and GridView just not complex enough? Try StaggeredGridView!
   *
   * <p>StaggeredGridView presents a multi-column grid with consistent column sizes
   * but varying row sizes between the columns. Each successive item from a
   * {@link android.widget.ListAdapter ListAdapter} will be arranged from top to bottom,
   * left to right. The largest vertical gap is always filled first.</p>
   *
   * <p>Item views may span multiple columns as specified by their {@link LayoutParams}.
   * The attribute <code>android:layout_span</code> may be used when inflating
   * item views from xml.</p>
   */
  public class StaggeredGridView extends ViewGroup {
      private static final String TAG = "StaggeredGridView";
  
      /*
       * There are a few things you should know if you're going to make modifications
       * to StaggeredGridView.
       *
       * Like ListView, SGV populates from an adapter and recycles views that fall out
       * of the visible boundaries of the grid. A few invariants always hold:
       *
       * - mFirstPosition is the adapter position of the View returned by getChildAt(0).
       * - Any child index can be translated to an adapter position by adding mFirstPosition.
       * - Any adapter position can be translated to a child index by subtracting mFirstPosition.
       * - Views for items in the range [mFirstPosition, mFirstPosition + getChildCount()) are
       *   currently attached to the grid as children. All other adapter positions do not have
       *   active views.
       *
       * This means a few things thanks to the staggered grid's nature. Some views may stay attached
       * long after they have scrolled offscreen if removing and recycling them would result in
       * breaking one of the invariants above.
       *
       * LayoutRecords are used to track data about a particular item's layout after the associated
       * view has been removed. These let positioning and the choice of column for an item
       * remain consistent even though the rules for filling content up vs. filling down vary.
       *
       * Whenever layout parameters for a known LayoutRecord change, other LayoutRecords before
       * or after it may need to be invalidated. e.g. if the item's height or the number
       * of columns it spans changes, all bets for other items in the same direction are off
       * since the cached information no longer applies.
       */
  
      private ListAdapter mAdapter;
  
      public static final int COLUMN_COUNT_AUTO = -1;
  
      private int mColCountSetting = 2;
      private int mColCount = 2;
      private int mMinColWidth = 0;
      private int mItemMargin;
  
      private int[] mItemTops;
      private int[] mItemBottoms;
  
      private boolean mFastChildLayout;
      private boolean mPopulating;
      private boolean mInLayout;
      private int[] mRestoreOffsets;
  
      private final RecycleBin mRecycler = new RecycleBin();
  
      private final AdapterDataSetObserver mObserver = new AdapterDataSetObserver();
  
      private boolean mDataChanged;
      private int mItemCount;
      private boolean mHasStableIds;
  
      private int mFirstPosition;
  
      private int mTouchSlop;
      private int mMaximumVelocity;
      private int mFlingVelocity;
      private float mLastTouchY;
      private float mLastTouchX;
      private float mTouchRemainderY;
      private int mActivePointerId;
      private int mMotionPosition;
      private int mColWidth;
      private int mNumCols;
      private long mFirstAdapterId;
      private boolean mBeginClick;
  
      private static final int TOUCH_MODE_IDLE = 0;
      private static final int TOUCH_MODE_DRAGGING = 1;
      private static final int TOUCH_MODE_FLINGING = 2;
  	private static final int TOUCH_MODE_DOWN = 3;
  	private static final int TOUCH_MODE_TAP = 4;
  	private static final int TOUCH_MODE_DONE_WAITING = 5;
  	private static final int TOUCH_MODE_REST = 6;
  
  	private static final int INVALID_POSITION = -1;
  
      private int mTouchMode;
      private final VelocityTracker mVelocityTracker = VelocityTracker.obtain();
      private final ScrollerCompat mScroller;
  
      private final EdgeEffectCompat mTopEdge;
      private final EdgeEffectCompat mBottomEdge;
  
      private ArrayList<HashSet<Integer>> mColMappings = new ArrayList<HashSet<Integer>>();
  
      private Runnable mPendingCheckForTap;
  
      private ContextMenuInfo mContextMenuInfo = null;
  
      /**
       * The drawable used to draw the selector
       */
      Drawable mSelector;
  
      boolean mDrawSelectorOnTop = false;
  
      /**
       * Delayed action for touch mode.
       */
      private Runnable mTouchModeReset;
  
      /**
       * The selection's left padding
       */
      int mSelectionLeftPadding = 0;
  
      /**
       * The selection's top padding
       */
      int mSelectionTopPadding = 0;
  
      /**
       * The selection's right padding
       */
      int mSelectionRightPadding = 0;
  
      /**
       * The selection's bottom padding
       */
      int mSelectionBottomPadding = 0;
  
      /**
       * The select child's view (from the adapter's getView) is enabled.
       */
      private boolean mIsChildViewEnabled;
  
      /**
       * Defines the selector's location and dimension at drawing time
       */
      Rect mSelectorRect = new Rect();
  
      /**
       * The current position of the selector in the list.
       */
      int mSelectorPosition = INVALID_POSITION;
  
      /**
       * The listener that receives notifications when an item is clicked.
       */
      OnItemClickListener mOnItemClickListener;
  
      /**
       * The listener that receives notifications when an item is long clicked.
       */
      OnItemLongClickListener mOnItemLongClickListener;
  
      /**
       * The last CheckForLongPress runnable we posted, if any
       */
      private CheckForLongPress mPendingCheckForLongPress;
  
      /**
       * Acts upon click
       */
      private PerformClick mPerformClick;
  
      /**
       * Rectangle used for hit testing children
       */
      private Rect mTouchFrame;
  
      private static final class LayoutRecord {
          public int column;
          public long id = -1;
          public int height;
          public int span;
          private int[] mMargins;
  
          private final void ensureMargins() {
              if (mMargins == null) {
                  // Don't need to confirm length;
                  // all layoutrecords are purged when column count changes.
                  mMargins = new int[span * 2];
              }
          }
  
          public final int getMarginAbove(int col) {
              if (mMargins == null) {
                  return 0;
              }
              return mMargins[col * 2];
          }
  
          public final int getMarginBelow(int col) {
              if (mMargins == null) {
                  return 0;
              }
              return mMargins[col * 2 + 1];
          }
  
          public final void setMarginAbove(int col, int margin) {
              if (mMargins == null && margin == 0) {
                  return;
              }
              ensureMargins();
              mMargins[col * 2] = margin;
          }
  
          public final void setMarginBelow(int col, int margin) {
              if (mMargins == null && margin == 0) {
                  return;
              }
              ensureMargins();
              mMargins[col * 2 + 1] = margin;
          }
  
          @Override
          public String toString() {
              String result = "LayoutRecord{c=" + column + ", id=" + id + " h=" + height +
                      " s=" + span;
              if (mMargins != null) {
                  result += " margins[above, below](";
                  for (int i = 0; i < mMargins.length; i += 2) {
                      result += "[" + mMargins[i] + ", " + mMargins[i+1] + "]";
                  }
                  result += ")";
              }
              return result + "}";
          }
      }
      private final SparseArrayCompat<LayoutRecord> mLayoutRecords =
              new SparseArrayCompat<LayoutRecord>();
  
      public StaggeredGridView(Context context) {
          this(context, null);
      }
  
      public StaggeredGridView(Context context, AttributeSet attrs) {
          this(context, attrs, 0);
      }
  
      public StaggeredGridView(Context context, AttributeSet attrs, int defStyle) {
          super(context, attrs, defStyle);
  
          if(attrs!=null){
          	TypedArray a=getContext().obtainStyledAttributes( attrs, R.styleable.StaggeredGridView);
              mColCount = a.getInteger(R.styleable.StaggeredGridView_numColumns, 2);
              mDrawSelectorOnTop = a.getBoolean(R.styleable.StaggeredGridView_drawSelectorOnTop, false);
              mItemMargin = (int) a.getDimension(R.styleable.StaggeredGridView_itemMargin, 0);
          }else{
          	mColCount = 2;
          	mDrawSelectorOnTop = false;
          }
  
          final ViewConfiguration vc = ViewConfiguration.get(context);
          mTouchSlop = vc.getScaledTouchSlop();
          mMaximumVelocity = vc.getScaledMaximumFlingVelocity();
          mFlingVelocity = vc.getScaledMinimumFlingVelocity();
          mScroller = ScrollerCompat.from(context);
  
          mTopEdge = new EdgeEffectCompat(context);
          mBottomEdge = new EdgeEffectCompat(context);
          setWillNotDraw(false);
          setClipToPadding(false);
          this.setFocusableInTouchMode(false);
  
          if (mSelector == null) {
              useDefaultSelector();
          }
      }
  
      /**
       * Set a fixed number of columns for this grid. Space will be divided evenly
       * among all columns, respecting the item margin between columns.
       * The default is 2. (If it were 1, perhaps you should be using a
       * {@link android.widget.ListView ListView}.)
       *
       * @param colCount Number of columns to display.
       * @see #setMinColumnWidth(int)
       */
      public void setColumnCount(int colCount) {
          if (colCount < 1 && colCount != COLUMN_COUNT_AUTO) {
              throw new IllegalArgumentException("Column count must be at least 1 - received " +
                      colCount);
          }
          final boolean needsPopulate = colCount != mColCount;
          mColCount = mColCountSetting = colCount;
          if (needsPopulate) {
              populate(false);
          }
      }
  
      public int getColumnCount() {
          return mColCount;
      }
  
      /**
       * Set a minimum column width for
       * @param minColWidth
       */
      public void setMinColumnWidth(int minColWidth) {
          mMinColWidth = minColWidth;
          setColumnCount(COLUMN_COUNT_AUTO);
      }
  
      /**
       * Set the margin between items in pixels. This margin is applied
       * both vertically and horizontally.
       *
       * @param marginPixels Spacing between items in pixels
       */
      public void setItemMargin(int marginPixels) {
          final boolean needsPopulate = marginPixels != mItemMargin;
          mItemMargin = marginPixels;
          if (needsPopulate) {
              populate(false);
          }
      }
  
      /**
       * Return the first adapter position with a view currently attached as
       * a child view of this grid.
       *
       * @return the adapter position represented by the view at getChildAt(0).
       */
      public int getFirstPosition() {
          return mFirstPosition;
      }
  
      @Override
      public boolean onInterceptTouchEvent(MotionEvent ev) {
          mVelocityTracker.addMovement(ev);
          final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
          switch (action) {
              case MotionEvent.ACTION_DOWN:
                  mVelocityTracker.clear();
                  mScroller.abortAnimation();
                  mLastTouchY = ev.getY();
                  mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
                  mTouchRemainderY = 0;
                  if (mTouchMode == TOUCH_MODE_FLINGING) {
                      // Catch!
                      mTouchMode = TOUCH_MODE_DRAGGING;
                      return true;
                  }
                  break;
  
              case MotionEvent.ACTION_MOVE: {
                  final int index = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
                  if (index < 0) {
                      Log.e(TAG, "onInterceptTouchEvent could not find pointer with id " +
                              mActivePointerId + " - did StaggeredGridView receive an inconsistent " +
                              "event stream?");
                      return false;
                  }
                  final float y = MotionEventCompat.getY(ev, index);
                  final float dy = y - mLastTouchY + mTouchRemainderY;
                  final int deltaY = (int) dy;
                  mTouchRemainderY = dy - deltaY;
  
                  if (Math.abs(dy) > mTouchSlop) {
                      mTouchMode = TOUCH_MODE_DRAGGING;
                      return true;
                  }
              }
          }
  
          return false;
      }
  
      void hideSelector() {
          if (this.mSelectorPosition != INVALID_POSITION) {
              // TODO: hide selector properly
          }
      }
  
      @Override
      public boolean onTouchEvent(MotionEvent ev) {
          mVelocityTracker.addMovement(ev);
          final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
  
          int motionPosition = pointToPosition((int) ev.getX(), (int) ev.getY());
  
          switch (action) {
              case MotionEvent.ACTION_DOWN:
  
                  mVelocityTracker.clear();
                  mScroller.abortAnimation();
                  mLastTouchY = ev.getY();
                  mLastTouchX = ev.getX();
                  motionPosition = pointToPosition((int) mLastTouchX, (int) mLastTouchY);
                  mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
                  mTouchRemainderY = 0;
  
                  if(mTouchMode != TOUCH_MODE_FLINGING && !mDataChanged && motionPosition >= 0
                      && mAdapter != null && mAdapter.isEnabled(motionPosition)) {
                  	mTouchMode = TOUCH_MODE_DOWN;
  
                  	mBeginClick = true;
  
                  	if (mPendingCheckForTap == null) {
                      	mPendingCheckForTap = new CheckForTap();
                      }
  
                      postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                  }
  
                  mMotionPosition = motionPosition;
                  invalidate();
                  break;
  
              case MotionEvent.ACTION_MOVE: {
  
  
                  final int index = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
                  if (index < 0) {
                      Log.e(TAG, "onInterceptTouchEvent could not find pointer with id " +
                              mActivePointerId + " - did StaggeredGridView receive an inconsistent " +
                              "event stream?");
                      return false;
                  }
                  final float y = MotionEventCompat.getY(ev, index);
                  final float dy = y - mLastTouchY + mTouchRemainderY;
                  final int deltaY = (int) dy;
                  mTouchRemainderY = dy - deltaY;
  
                  if (Math.abs(dy) > mTouchSlop) {
                      mTouchMode = TOUCH_MODE_DRAGGING;
                  }
  
                  if (mTouchMode == TOUCH_MODE_DRAGGING) {
                      mLastTouchY = y;
  
                      if (!trackMotionScroll(deltaY, true)) {
                          // Break fling velocity if we impacted an edge.
                          mVelocityTracker.clear();
                      }
                  }
  
                  updateSelectorState();
              } break;
  
              case MotionEvent.ACTION_CANCEL:
                  mTouchMode = TOUCH_MODE_IDLE;
                  updateSelectorState();
                  setPressed(false);
                  View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
                  if (motionView != null) {
                      motionView.setPressed(false);
                  }
                  final Handler handler = getHandler();
                  if (handler != null) {
                      handler.removeCallbacks(mPendingCheckForLongPress);
                  }
  
                  if (mTopEdge != null) {
                  	mTopEdge.onRelease();
                  	mBottomEdge.onRelease();
                  }
  
                  mTouchMode = TOUCH_MODE_IDLE;
                  break;
  
              case MotionEvent.ACTION_UP: {
                  mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                  final float velocity = VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId);
                  final int prevTouchMode = mTouchMode;
  
                  if (Math.abs(velocity) > mFlingVelocity) { // TODO
                      mTouchMode = TOUCH_MODE_FLINGING;
                      mScroller.fling(0, 0, 0, (int) velocity, 0, 0,
                              Integer.MIN_VALUE, Integer.MAX_VALUE);
                      mLastTouchY = 0;
                      invalidate();
                  } else {
                      mTouchMode = TOUCH_MODE_IDLE;
                  }
  
                  if (!mDataChanged && mAdapter!=null && mAdapter.isEnabled(motionPosition)) {
                      // TODO : handle
                  	mTouchMode = TOUCH_MODE_TAP;
                  } else {
                      mTouchMode = TOUCH_MODE_REST;
                  }
  
                  switch(prevTouchMode){
                  	case TOUCH_MODE_DOWN:
                  	case TOUCH_MODE_TAP:
                  	case TOUCH_MODE_DONE_WAITING:
                          final View child = getChildAt(motionPosition - mFirstPosition);
                          final float x = ev.getX();
                          final boolean inList = x > getPaddingLeft() && x < getWidth() - getPaddingRight();
                          if (child != null && !child.hasFocusable() && inList) {
                          	if (mTouchMode != TOUCH_MODE_DOWN) {
                                  child.setPressed(false);
                              }
  
                          	if (mPerformClick == null) {
                          		invalidate();
                                  mPerformClick = new PerformClick();
                              }
  
                          	final PerformClick performClick = mPerformClick;
                              performClick.mClickMotionPosition = motionPosition;
                              performClick.rememberWindowAttachCount();
  
  
                              if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) {
                                  final Handler handlerTouch = getHandler();
                                  if (handlerTouch != null) {
                                  	handlerTouch.removeCallbacks(mTouchMode == TOUCH_MODE_DOWN ?
                                              mPendingCheckForTap : mPendingCheckForLongPress);
                                  }
  
                                  if (!mDataChanged && mAdapter != null && mAdapter.isEnabled(motionPosition)) {
                                      mTouchMode = TOUCH_MODE_TAP;
  
                                      layoutChildren(mDataChanged);
                                      child.setPressed(true);
                                      positionSelector(mMotionPosition, child);
                                      setPressed(true);
                                      if (mSelector != null) {
                                          Drawable d = mSelector.getCurrent();
                                          if (d != null && d instanceof TransitionDrawable) {
                                              ((TransitionDrawable) d).resetTransition();
                                          }
                                      }
                                      if (mTouchModeReset != null) {
                                          removeCallbacks(mTouchModeReset);
                                      }
                                      mTouchModeReset = new Runnable() {
                                          @Override
                                          public void run() {
                                              mTouchMode = TOUCH_MODE_REST;
                                              child.setPressed(false);
                                              setPressed(false);
                                              if (!mDataChanged) {
                                                  performClick.run();
                                              }
                                          }
                                      };
                                      postDelayed(mTouchModeReset, ViewConfiguration.getPressedStateDuration());
  
                                  } else {
                                      mTouchMode = TOUCH_MODE_REST;
                                  }
                                  return true;
                              } else if (!mDataChanged && mAdapter != null && mAdapter.isEnabled(motionPosition)) {
                                  performClick.run();
                              }
                          }
  
                          mTouchMode = TOUCH_MODE_REST;
                  }
  
                  mBeginClick = false;
  
                  updateSelectorState();
              } break;
          }
          return true;
      }
  
      /**
       *
       * @param deltaY Pixels that content should move by
       * @return true if the movement completed, false if it was stopped prematurely.
       */
      private boolean trackMotionScroll(int deltaY, boolean allowOverScroll) {
          final boolean contentFits = contentFits();
          final int allowOverhang = Math.abs(deltaY);
  
          final int overScrolledBy;
          final int movedBy;
          if (!contentFits) {
              final int overhang;
              final boolean up;
              mPopulating = true;
              if (deltaY > 0) {
                  overhang = fillUp(mFirstPosition - 1, allowOverhang)+ mItemMargin;
                  up = true;
              } else {
                  overhang = fillDown(mFirstPosition + getChildCount(), allowOverhang) + mItemMargin;
                  up = false;
              }
              movedBy = Math.min(overhang, allowOverhang);
              offsetChildren(up ? movedBy : -movedBy);
              recycleOffscreenViews();
              mPopulating = false;
              overScrolledBy = allowOverhang - overhang;
          } else {
              overScrolledBy = allowOverhang;
              movedBy = 0;
          }
  
          if (allowOverScroll) {
              final int overScrollMode = ViewCompat.getOverScrollMode(this);
  
              if (overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS ||
                      (overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS && !contentFits)) {
                  if (overScrolledBy > 0) {
                      EdgeEffectCompat edge = deltaY > 0 ? mTopEdge : mBottomEdge;
                      edge.onPull((float) Math.abs(deltaY) / getHeight());
                      invalidate();
                  }
              }
          }
  
          if (mSelectorPosition != INVALID_POSITION) {
              final int childIndex = mSelectorPosition - mFirstPosition;
              if (childIndex >= 0 && childIndex < getChildCount()) {
                  positionSelector(INVALID_POSITION, getChildAt(childIndex));
              }
          } else {
              mSelectorRect.setEmpty();
          }
  
          return deltaY == 0 || movedBy != 0;
      }
  
      private final boolean contentFits() {
          if (mFirstPosition != 0 || getChildCount() != mItemCount) {
              return false;
          }
  
          int topmost = Integer.MAX_VALUE;
          int bottommost = Integer.MIN_VALUE;
          for (int i = 0; i < mColCount; i++) {
              if (mItemTops[i] < topmost) {
                  topmost = mItemTops[i];
              }
              if (mItemBottoms[i] > bottommost) {
                  bottommost = mItemBottoms[i];
              }
          }
  
          return topmost >= getPaddingTop() && bottommost <= getHeight() - getPaddingBottom();
      }
  
      private void recycleAllViews() {
          for (int i = 0; i < getChildCount(); i++) {
              mRecycler.addScrap(getChildAt(i));
          }
  
          if (mInLayout) {
              removeAllViewsInLayout();
          } else {
              removeAllViews();
          }
      }
  
      /**
       * Important: this method will leave offscreen views attached if they
       * are required to maintain the invariant that child view with index i
       * is always the view corresponding to position mFirstPosition + i.
       */
      private void recycleOffscreenViews() {
          final int height = getHeight();
          final int clearAbove = -mItemMargin;
          final int clearBelow = height + mItemMargin;
          for (int i = getChildCount() - 1; i >= 0; i--) {
              final View child = getChildAt(i);
              if (child.getTop() <= clearBelow)  {
                  // There may be other offscreen views, but we need to maintain
                  // the invariant documented above.
                  break;
              }
  
              if (mInLayout) {
                  removeViewsInLayout(i, 1);
              } else {
                  removeViewAt(i);
              }
  
              mRecycler.addScrap(child);
          }
  
          while (getChildCount() > 0) {
              final View child = getChildAt(0);
              if (child.getBottom() >= clearAbove) {
                  // There may be other offscreen views, but we need to maintain
                  // the invariant documented above.
                  break;
              }
  
              if (mInLayout) {
                  removeViewsInLayout(0, 1);
              } else {
                  removeViewAt(0);
              }
  
              mRecycler.addScrap(child);
              mFirstPosition++;
          }
  
          final int childCount = getChildCount();
          if (childCount > 0) {
              // Repair the top and bottom column boundaries from the views we still have
              Arrays.fill(mItemTops, Integer.MAX_VALUE);
              Arrays.fill(mItemBottoms, Integer.MIN_VALUE);
  
              for (int i = 0; i < childCount; i++){
                  final View child = getChildAt(i);
                  final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                  final int top = child.getTop() - mItemMargin;
                  final int bottom = child.getBottom();
                  final LayoutRecord rec = mLayoutRecords.get(mFirstPosition + i);
  
                  final int colEnd = lp.column + Math.min(mColCount, lp.span);
                  for (int col = lp.column; col < colEnd; col++) {
                      final int colTop = top - rec.getMarginAbove(col - lp.column);
                      final int colBottom = bottom + rec.getMarginBelow(col - lp.column);
                      if (colTop < mItemTops[col]) {
                          mItemTops[col] = colTop;
                      }
                      if (colBottom > mItemBottoms[col]) {
                          mItemBottoms[col] = colBottom;
                      }
                  }
              }
  
              for (int col = 0; col < mColCount; col++) {
                  if (mItemTops[col] == Integer.MAX_VALUE) {
                      // If one was untouched, both were.
                      mItemTops[col] = 0;
                      mItemBottoms[col] = 0;
                  }
              }
          }
      }
  
      public void computeScroll() {
          if (mScroller.computeScrollOffset()) {
              final int y = mScroller.getCurrY();
              final int dy = (int) (y - mLastTouchY);
              mLastTouchY = y;
              final boolean stopped = !trackMotionScroll(dy, false);
  
              if (!stopped && !mScroller.isFinished()) {
                  postInvalidate();
              } else {
                  if (stopped) {
                      final int overScrollMode = ViewCompat.getOverScrollMode(this);
                      if (overScrollMode != ViewCompat.OVER_SCROLL_NEVER) {
                          final EdgeEffectCompat edge;
                          if (dy > 0) {
                              edge = mTopEdge;
                          } else {
                              edge = mBottomEdge;
                          }
                          edge.onAbsorb(Math.abs((int) mScroller.getCurrVelocity()));
                          postInvalidate();
                      }
                      mScroller.abortAnimation();
                  }
                  mTouchMode = TOUCH_MODE_IDLE;
              }
          }
      }
  
      @Override
      protected void dispatchDraw(Canvas canvas) {
          final boolean drawSelectorOnTop = mDrawSelectorOnTop;
          if (!drawSelectorOnTop) {
              drawSelector(canvas);
          }
  
          super.dispatchDraw(canvas);
  
          if (drawSelectorOnTop) {
              drawSelector(canvas);
          }
      }
  
      private void drawSelector(Canvas canvas) {
      	if (!mSelectorRect.isEmpty() && mSelector != null && mBeginClick ) {
              final Drawable selector = mSelector;
              selector.setBounds(mSelectorRect);
              selector.draw(canvas);
          }
      }
  
      @Override
      public void draw(Canvas canvas) {
          super.draw(canvas);
  
          if (mTopEdge != null) {
              boolean needsInvalidate = false;
              if (!mTopEdge.isFinished()) {
                  mTopEdge.draw(canvas);
                  needsInvalidate = true;
              }
              if (!mBottomEdge.isFinished()) {
                  final int restoreCount = canvas.save();
                  final int width = getWidth();
                  canvas.translate(-width, getHeight());
                  canvas.rotate(180, width, 0);
                  mBottomEdge.draw(canvas);
                  canvas.restoreToCount(restoreCount);
                  needsInvalidate = true;
              }
  
              if (needsInvalidate) {
                  invalidate();
              }
          }
  
  //        drawSelector(canvas);
      }
  
      public void beginFastChildLayout() {
          mFastChildLayout = true;
      }
  
      public void endFastChildLayout() {
          mFastChildLayout = false;
          populate(false);
      }
  
      @Override
      public void requestLayout() {
          if (!mPopulating && !mFastChildLayout) {
              super.requestLayout();
          }
      }
  
      @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);
  
          if (widthMode != MeasureSpec.EXACTLY) {
              widthMode = MeasureSpec.EXACTLY;
          }
          if (heightMode != MeasureSpec.EXACTLY) {
              heightMode = MeasureSpec.EXACTLY;
          }
  
          setMeasuredDimension(widthSize, heightSize);
  
          if (mColCountSetting == COLUMN_COUNT_AUTO) {
              final int colCount = widthSize / mMinColWidth;
              if (colCount != mColCount) {
                  mColCount = colCount;
              }
          }
      }
  
      @Override
      protected void onLayout(boolean changed, int l, int t, int r, int b) {
      	mInLayout = true;
          populate(false);
          mInLayout = false;
  
          final int width = r - l;
          final int height = b - t;
          mTopEdge.setSize(width, height);
          mBottomEdge.setSize(width, height);
      }
  
      private void populate(boolean clearData) {
  
      	if (getWidth() == 0 || getHeight() == 0) {
              return;
          }
  
      	if (mColCount == COLUMN_COUNT_AUTO) {
              final int colCount = getWidth() / mMinColWidth;
              if (colCount != mColCount) {
                  mColCount = colCount;
              }
          }
  
          final int colCount = mColCount;
  
          // setup arraylist for mappings
          if(mColMappings.size() != mColCount){
          	mColMappings.clear();
          	for(int i=0; i < mColCount; i++){
          		mColMappings.add(new HashSet<Integer>());
          	}
          }
  
          if (mItemTops == null || mItemTops.length != colCount) {
          	mItemTops = new int[colCount];
              mItemBottoms = new int[colCount];
  
              mLayoutRecords.clear();
              if (mInLayout) {
                  removeAllViewsInLayout();
              } else {
                  removeAllViews();
              }
          }
  
          final int top = getPaddingTop();
          for(int i = 0; i<colCount; i++){
          	final int offset =  top + ((mRestoreOffsets != null)? Math.min(mRestoreOffsets[i], 0) : 0);
          	mItemTops[i] = (offset == 0) ? mItemTops[i] : offset;
          	mItemBottoms[i] = (offset == 0) ? mItemBottoms[i] : offset;
          }
  
          mPopulating = true;
  
          layoutChildren(mDataChanged);
          fillDown(mFirstPosition + getChildCount(), 0);
          fillUp(mFirstPosition - 1, 0);
          mPopulating = false;
          mDataChanged = false;
  
          if(clearData){
          	if(mRestoreOffsets!=null)
          		Arrays.fill(mRestoreOffsets,0);
          }
      }
  
  
  
      final void offsetChildren(int offset) {
          final int childCount = getChildCount();
          for (int i = 0; i < childCount; i++) {
              final View child = getChildAt(i);
              child.layout(child.getLeft(), child.getTop() + offset,
                      child.getRight(), child.getBottom() + offset);
          }
  
          final int colCount = mColCount;
          for (int i = 0; i < colCount; i++) {
              mItemTops[i] += offset;
              mItemBottoms[i] += offset;
          }
      }
  
      /**
       * Measure and layout all currently visible children.
       *
       * @param queryAdapter true to requery the adapter for view data
       */
      final void layoutChildren(boolean queryAdapter) {
          final int paddingLeft = getPaddingLeft();
          final int paddingRight = getPaddingRight();
          final int itemMargin = mItemMargin;
          final int colWidth = (getWidth() - paddingLeft - paddingRight - itemMargin * (mColCount - 1)) / mColCount;
          mColWidth = colWidth;
          int rebuildLayoutRecordsBefore = -1;
          int rebuildLayoutRecordsAfter = -1;
  
          Arrays.fill(mItemBottoms, Integer.MIN_VALUE);
  
          final int childCount = getChildCount();
          int amountRemoved = 0;
  
          for (int i = 0; i < childCount; i++) {
              View child = getChildAt(i);
              LayoutParams lp = (LayoutParams) child.getLayoutParams();
              final int col = lp.column;
              final int position = mFirstPosition + i;
              final boolean needsLayout = queryAdapter || child.isLayoutRequested();
  
              if (queryAdapter) {
  
                  View newView = obtainView(position, child);
                  if(newView == null){
                  	// child has been removed
                  	removeViewAt(i);
                  	if(i-1>=0) invalidateLayoutRecordsAfterPosition(i-1);
                  	amountRemoved++;
                  	continue;
                  }else if (newView != child) {
                      removeViewAt(i);
                      addView(newView, i);
                      child = newView;
                  }
                  lp = (LayoutParams) child.getLayoutParams(); // Might have changed
              }
  
              final int span = Math.min(mColCount, lp.span);
              final int widthSize = colWidth * span + itemMargin * (span - 1);
  
              if (needsLayout) {
                  final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
  
                  final int heightSpec;
                  if (lp.height == LayoutParams.WRAP_CONTENT) {
                      heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
                  } else {
                      heightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
                  }
  
                  child.measure(widthSpec, heightSpec);
              }
  
              int childTop = mItemBottoms[col] > Integer.MIN_VALUE ? mItemBottoms[col] + mItemMargin : child.getTop();
  
              if (span > 1) {
                  int lowest = childTop;
                  for (int j = col + 1; j < col + span; j++) {
                      final int bottom = mItemBottoms[j] + mItemMargin;
                      if (bottom > lowest) {
                          lowest = bottom;
                      }
                  }
                  childTop = lowest;
              }
              final int childHeight = child.getMeasuredHeight();
              final int childBottom = childTop + childHeight;
              final int childLeft = paddingLeft + col * (colWidth + itemMargin);
              final int childRight = childLeft + child.getMeasuredWidth();
              child.layout(childLeft, childTop, childRight, childBottom);
  
              for (int j = col; j < col + span; j++) {
                  mItemBottoms[j] = childBottom;
              }
  
              final LayoutRecord rec = mLayoutRecords.get(position);
              if (rec != null && rec.height != childHeight) {
                  // Invalidate our layout records for everything before this.
                  rec.height = childHeight;
                  rebuildLayoutRecordsBefore = position;
              }
  
              if (rec != null && rec.span != span) {
                  // Invalidate our layout records for everything after this.
                  rec.span = span;
                  rebuildLayoutRecordsAfter = position;
              }
          }
  
          // Update mItemBottoms for any empty columns
          for (int i = 0; i < mColCount; i++) {
              if (mItemBottoms[i] == Integer.MIN_VALUE) {
                  mItemBottoms[i] = mItemTops[i];
              }
          }
  
          if (rebuildLayoutRecordsBefore >= 0 || rebuildLayoutRecordsAfter >= 0) {
              if (rebuildLayoutRecordsBefore >= 0) {
                  invalidateLayoutRecordsBeforePosition(rebuildLayoutRecordsBefore);
              }
              if (rebuildLayoutRecordsAfter >= 0) {
                  invalidateLayoutRecordsAfterPosition(rebuildLayoutRecordsAfter);
              }
              for (int i = 0; i < (childCount - amountRemoved); i++) {
                  final int position = mFirstPosition + i;
                  final View child = getChildAt(i);
                  final LayoutParams lp = (LayoutParams) child.getLayoutParams();
                  LayoutRecord rec = mLayoutRecords.get(position);
                  if (rec == null) {
                      rec = new LayoutRecord();
                      mLayoutRecords.put(position, rec);
                  }
                  rec.column = lp.column;
                  rec.height = child.getHeight();
                  rec.id = lp.id;
                  rec.span = Math.min(mColCount, lp.span);
              }
          }
  
          if(this.mSelectorPosition != INVALID_POSITION){
          	View child = getChildAt(mMotionPosition - mFirstPosition);
          	if (child != null) positionSelector(mMotionPosition, child);
           } else if (mTouchMode > TOUCH_MODE_DOWN) {
               View child = getChildAt(mMotionPosition - mFirstPosition);
               if (child != null) positionSelector(mMotionPosition, child);
           } else {
               mSelectorRect.setEmpty();
           }
      }
  
      final void invalidateLayoutRecordsBeforePosition(int position) {
          int endAt = 0;
          while (endAt < mLayoutRecords.size() && mLayoutRecords.keyAt(endAt) < position) {
              endAt++;
          }
          mLayoutRecords.removeAtRange(0, endAt);
      }
  
      final void invalidateLayoutRecordsAfterPosition(int position) {
          int beginAt = mLayoutRecords.size() - 1;
          while (beginAt >= 0 && mLayoutRecords.keyAt(beginAt) > position) {
              beginAt--;
          }
          beginAt++;
          mLayoutRecords.removeAtRange(beginAt + 1, mLayoutRecords.size() - beginAt);
      }
  
      /**
       * Should be called with mPopulating set to true
       *
       * @param fromPosition Position to start filling from
       * @param overhang the number of extra pixels to fill beyond the current top edge
       * @return the max overhang beyond the beginning of the view of any added items at the top
       */
      final int fillUp(int fromPosition, int overhang) {
  
          final int paddingLeft = getPaddingLeft();
          final int paddingRight = getPaddingRight();
          final int itemMargin = mItemMargin;
          final int colWidth =
                  (getWidth() - paddingLeft - paddingRight - itemMargin * (mColCount - 1)) / mColCount;
          mColWidth = colWidth;
          final int gridTop = getPaddingTop();
          final int fillTo = gridTop - overhang;
          int nextCol = getNextColumnUp();
          int position = fromPosition;
  
          while (nextCol >= 0 && mItemTops[nextCol] > fillTo && position >= 0) {
              // make sure the nextCol is correct. check to see if has been mapped
          	// otherwise stick to getNextColumnUp()
          	Integer positionInt = Integer.valueOf(position);
  			if(!mColMappings.get(nextCol).contains(positionInt)){
          		for(int i=0; i < mColMappings.size(); i++){
          			if(mColMappings.get(i).contains(positionInt)){
          				nextCol = i;
          				break;
          			}
          		}
          	}
  
  //        	displayMapping();
  
          	final View child = obtainView(position, null);
  
          	if(child == null) continue;
  
              LayoutParams lp = (LayoutParams) child.getLayoutParams();
  
              if(lp == null){
              	lp = this.generateDefaultLayoutParams();
              	child.setLayoutParams(lp);
              }
  
              if (child.getParent() != this) {
                  if (mInLayout) {
                      addViewInLayout(child, 0, lp);
                  } else {
                      addView(child, 0);
                  }
              }
  
              final int span = Math.min(mColCount, lp.span);
              final int widthSize = colWidth * span + itemMargin * (span - 1);
              final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
  
              LayoutRecord rec;
              if (span > 1) {
                  rec = getNextRecordUp(position, span);
  //                nextCol = rec.column;
              } else {
                  rec = mLayoutRecords.get(position);
              }
  
              boolean invalidateBefore = false;
              if (rec == null) {
                  rec = new LayoutRecord();
                  mLayoutRecords.put(position, rec);
                  rec.column = nextCol;
                  rec.span = span;
              } else if (span != rec.span) {
                  rec.span = span;
                  rec.column = nextCol;
                  invalidateBefore = true;
              } else {
  //                nextCol = rec.column;
              }
  
              if (mHasStableIds) {
                  final long id = mAdapter.getItemId(position);
                  rec.id = id;
                  lp.id = id;
              }
  
              lp.column = nextCol;
  
              final int heightSpec;
              if (lp.height == LayoutParams.WRAP_CONTENT) {
                  heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
              } else {
                  heightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
              }
              child.measure(widthSpec, heightSpec);
  
              final int childHeight = child.getMeasuredHeight();
              if (invalidateBefore || (childHeight != rec.height && rec.height > 0)) {
                  invalidateLayoutRecordsBeforePosition(position);
              }
              rec.height = childHeight;
  
              int itemTop = mItemTops[nextCol];
  
              final int startFrom;
              if (span > 1) {
                  int highest = mItemTops[nextCol];
                  for (int i = nextCol + 1; i < nextCol + span; i++) {
                      final int top = mItemTops[i];
                      if (top < highest) {
                          highest = top;
                      }
                  }
                  startFrom = highest;
              } else {
                  startFrom = mItemTops[nextCol];
              }
  
  
              int childBottom = startFrom;
              int childTop = childBottom - childHeight;
              final int childLeft = paddingLeft + nextCol * (colWidth + itemMargin);
              final int childRight = childLeft + child.getMeasuredWidth();
  
  //            if(position == 0){
  //            	if(this.getChildCount()>1 && this.mColCount>1){
  //            		childTop = this.getChildAt(1).getTop();
  //            		childBottom = childTop + childHeight;
  //            	}
  //            }
  
              child.layout(childLeft, childTop, childRight, childBottom);
  
  
              for (int i = nextCol; i < nextCol + span; i++) {
                  mItemTops[i] = childTop - rec.getMarginAbove(i-nextCol) - itemMargin;
              }
  
              nextCol = getNextColumnUp();
              mFirstPosition = position--;
          }
  
          int highestView = getHeight();
  
          for (int i = 0; i < mColCount; i++) {
          	final View child = getFirstChildAtColumn(i);
  
          	if(child == null){
          		highestView = 0;
          		break;
          	}
              LayoutParams lp = (LayoutParams) child.getLayoutParams();
              i += Math.max(lp.span,1);
  
          	final int top = child.getTop();
  
          	if (top < highestView) {
                  highestView = top;
              }
          }
  
          return gridTop - highestView;
      }
  
  
      private View getFirstChildAtColumn(int column){
  
      	if(this.getChildCount() > column){
      		for(int i = 0; i<this.mColCount; i++){
      			final View child = getChildAt(i);
  
      			if(child!=null) {
      				final int left = child.getLeft();
          			int col = 0;
  
          			// determine the column by cycling widths
          			while( left > col*(this.mColWidth + mItemMargin*2) + getPaddingLeft() ){
          				col++;
          			}
  
          			if(col == column){
          				return child;
          			}
  
      			}
      		}
      	}
  
      	return null;
      }
  
      /**
       * Should be called with mPopulating set to true
       *
       * @param fromPosition Position to start filling from
       * @param overhang the number of extra pixels to fill beyond the current bottom edge
       * @return the max overhang beyond the end of the view of any added items at the bottom
       */
      final int fillDown(int fromPosition, int overhang) {
  
          final int paddingLeft = getPaddingLeft();
          final int paddingRight = getPaddingRight();
          final int itemMargin = mItemMargin;
          final int colWidth = (getWidth() - paddingLeft - paddingRight - itemMargin * (mColCount - 1)) / mColCount;
          final int gridBottom = getHeight() - getPaddingBottom();
          final int fillTo = gridBottom + overhang;
          int nextCol = getNextColumnDown(fromPosition);
          int position = fromPosition;
  
          while (nextCol >= 0 && mItemBottoms[nextCol] < fillTo && position < mItemCount) {
  
          	final View child = obtainView(position, null);
  
          	if(child == null) continue;
  
              LayoutParams lp = (LayoutParams) child.getLayoutParams();
              if(lp == null){
              	lp = this.generateDefaultLayoutParams();
              	child.setLayoutParams(lp);
              }
              if (child.getParent() != this) {
                  if (mInLayout) {
                      addViewInLayout(child, -1, lp);
                  } else {
                      addView(child);
                  }
              }
  
              final int span = Math.min(mColCount, lp.span);
              final int widthSize = colWidth * span + itemMargin * (span - 1);
              final int widthSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.EXACTLY);
  
              LayoutRecord rec;
              if (span > 1) {
                  rec = getNextRecordDown(position, span);
  //                nextCol = rec.column;
              } else {
                  rec = mLayoutRecords.get(position);
              }
  
              boolean invalidateAfter = false;
              if (rec == null) {
                  rec = new LayoutRecord();
                  mLayoutRecords.put(position, rec);
                  rec.column = nextCol;
                  rec.span = span;
              } else if (span != rec.span) {
                  rec.span = span;
                  rec.column = nextCol;
                  invalidateAfter = true;
              } else {
  //                nextCol = rec.column;
              }
  
              if (mHasStableIds) {
                  final long id = mAdapter.getItemId(position);
                  rec.id = id;
                  lp.id = id;
              }
  
              lp.column = nextCol;
  
              final int heightSpec;
              if (lp.height == LayoutParams.WRAP_CONTENT) {
                  heightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
              } else {
                  heightSpec = MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
              }
              child.measure(widthSpec, heightSpec);
  
              final int childHeight = child.getMeasuredHeight();
              if (invalidateAfter || (childHeight != rec.height && rec.height > 0)) {
                  invalidateLayoutRecordsAfterPosition(position);
              }
              rec.height = childHeight;
  
              final int startFrom;
              if (span > 1) {
                  int lowest = mItemBottoms[nextCol];
                  for (int i = nextCol + 1; i < nextCol + span; i++) {
                      final int bottom = mItemBottoms[i];
                      if (bottom > lowest) {
                          lowest = bottom;
                      }
                  }
                  startFrom = lowest;
              } else {
                  startFrom = mItemBottoms[nextCol];
              }
  
  
  
              final int childTop = startFrom + itemMargin;
              final int childBottom = childTop + childHeight;
              final int childLeft = paddingLeft + nextCol * (colWidth + itemMargin);
              final int childRight = childLeft + child.getMeasuredWidth();
              child.layout(childLeft, childTop, childRight, childBottom);
  
  
              // add the position to the mapping
              Integer positionInt = Integer.valueOf(position);
              if(!mColMappings.get(nextCol).contains(positionInt)){
  
              	// check to see if the mapping exists in other columns
              	// this would happen if list has been updated
              	for(HashSet<Integer> cols : mColMappings){
  					cols.remove(positionInt);
              	}
  
              	mColMappings.get(nextCol).add(positionInt);
              }
  
  
              for (int i = nextCol; i < nextCol + span; i++) {
                  mItemBottoms[i] = childBottom + rec.getMarginBelow(i - nextCol);
              }
  
  
              position++;
              nextCol = getNextColumnDown(position);
          }
  
          int lowestView = 0;
          for (int i = 0; i < mColCount; i++) {
              if (mItemBottoms[i] > lowestView) {
                  lowestView = mItemBottoms[i];
              }
          }
          return lowestView - gridBottom;
      }
  
      /**
       * for debug purposes
       */
      private void displayMapping(){
      	Log.w("DISPLAY", "MAP ****************");
      	StringBuilder sb = new StringBuilder();
      	int col = 0;
  
      	for(HashSet<Integer> map : mColMappings){
      		sb.append("COL"+col+":");
      		sb.append(' ');
      		for(Integer i: map){
      			sb.append(i);
      			sb.append(" , ");
      		}
      		Log.w("DISPLAY", sb.toString());
      		sb = new StringBuilder();
      		col++;
      	}
      	Log.w("DISPLAY", "MAP END ****************");
      }
  
      /**
       * @return column that the next view filling upwards should occupy. This is the bottom-most
       *         position available for a single-column item.
       */
      final int getNextColumnUp() {
          int result = -1;
          int bottomMost = Integer.MIN_VALUE;
  
          final int colCount = mColCount;
          for (int i = colCount - 1; i >= 0; i--) {
              final int top = mItemTops[i];
              if (top > bottomMost) {
                  bottomMost = top;
                  result = i;
              }
          }
          return result;
      }
  
      /**
       * Return a LayoutRecord for the given position
       * @param position
       * @param span
       * @return
       */
      final LayoutRecord getNextRecordUp(int position, int span) {
          LayoutRecord rec = mLayoutRecords.get(position);
          if (rec == null) {
              rec = new LayoutRecord();
              rec.span = span;
              mLayoutRecords.put(position, rec);
          } else if (rec.span != span) {
              throw new IllegalStateException("Invalid LayoutRecord! Record had span=" + rec.span +
                      " but caller requested span=" + span + " for position=" + position);
          }
          int targetCol = -1;
          int bottomMost = Integer.MIN_VALUE;
  
          final int colCount = mColCount;
          for (int i = colCount - span; i >= 0; i--) {
              int top = Integer.MAX_VALUE;
              for (int j = i; j < i + span; j++) {
                  final int singleTop = mItemTops[j];
                  if (singleTop < top) {
                      top = singleTop;
                  }
              }
              if (top > bottomMost) {
                  bottomMost = top;
                  targetCol = i;
              }
          }
  
          rec.column = targetCol;
  
          for (int i = 0; i < span; i++) {
              rec.setMarginBelow(i, mItemTops[i + targetCol] - bottomMost);
          }
  
          return rec;
      }
  
      /**
       * @return column that the next view filling downwards should occupy. This is the top-most
       *         position available.
       */
      final int getNextColumnDown(int position) {
          int result = -1;
          int topMost = Integer.MAX_VALUE;
  
          final int colCount = mColCount;
  
          for (int i = 0; i < colCount; i++) {
              final int bottom = mItemBottoms[i];
              if (bottom < topMost) {
                  topMost = bottom;
                  result = i;
              }
          }
  
          return result;
      }
  
      final LayoutRecord getNextRecordDown(int position, int span) {
          LayoutRecord rec = mLayoutRecords.get(position);
          if (rec == null) {
              rec = new LayoutRecord();
              rec.span = span;
              mLayoutRecords.put(position, rec);
          } else if (rec.span != span) {
              throw new IllegalStateException("Invalid LayoutRecord! Record had span=" + rec.span +
                      " but caller requested span=" + span + " for position=" + position);
          }
          int targetCol = -1;
          int topMost = Integer.MAX_VALUE;
  
          final int colCount = mColCount;
          for (int i = 0; i <= colCount - span; i++) {
              int bottom = Integer.MIN_VALUE;
              for (int j = i; j < i + span; j++) {
                  final int singleBottom = mItemBottoms[j];
                  if (singleBottom > bottom) {
                      bottom = singleBottom;
                  }
              }
              if (bottom < topMost) {
                  topMost = bottom;
                  targetCol = i;
              }
          }
  
          rec.column = targetCol;
  
          for (int i = 0; i < span; i++) {
              rec.setMarginAbove(i, topMost - mItemBottoms[i + targetCol]);
          }
  
          return rec;
      }
  
      /**
       * Obtain a populated view from the adapter. If optScrap is non-null and is not
       * reused it will be placed in the recycle bin.
       *
       * @param position position to get view for
       * @param optScrap Optional scrap view; will be reused if possible
       * @return A new view, a recycled view from mRecycler, or optScrap
       */
      final View obtainView(int position, View optScrap) {
          View view = mRecycler.getTransientStateView(position);
          if (view != null) {
              return view;
          }
  
          if(position >= mAdapter.getCount()){
          	view = null;
          	return null;
          }
  
          // Reuse optScrap if it's of the right type (and not null)
          final int optType = optScrap != null ?
                  ((LayoutParams) optScrap.getLayoutParams()).viewType : -1;
          final int positionViewType = mAdapter.getItemViewType(position);
          final View scrap = optType == positionViewType ?
                  optScrap : mRecycler.getScrapView(positionViewType);
  
          view = mAdapter.getView(position, scrap, this);
  
          if (view != scrap && scrap != null) {
              // The adapter didn't use it; put it back.
              mRecycler.addScrap(scrap);
          }
  
          ViewGroup.LayoutParams lp = view.getLayoutParams();
  
          if (view.getParent() != this) {
              if (lp == null) {
                  lp = generateDefaultLayoutParams();
              } else if (!checkLayoutParams(lp)) {
                  lp = generateLayoutParams(lp);
              }
          }
  
          final LayoutParams sglp = (LayoutParams) lp;
          sglp.position = position;
          sglp.viewType = positionViewType;
  
          //Set the updated LayoutParam before returning the view.
          view.setLayoutParams(sglp);
          return view;
      }
  
      public ListAdapter getAdapter() {
          return mAdapter;
      }
  
      public void setAdapter(ListAdapter adapter) {
          if (mAdapter != null) {
              mAdapter.unregisterDataSetObserver(mObserver);
          }
          // TODO: If the new adapter says that there are stable IDs, remove certain layout records
          // and onscreen views if they have changed instead of removing all of the state here.
          clearAllState();
          mAdapter = adapter;
          mDataChanged = true;
  
          mItemCount = adapter != null ? adapter.getCount() : 0;
  
          if (adapter != null) {
              adapter.registerDataSetObserver(mObserver);
              mRecycler.setViewTypeCount(adapter.getViewTypeCount());
              mHasStableIds = adapter.hasStableIds();
          } else {
              mHasStableIds = false;
          }
          populate(adapter!=null);
      }
  
      /**
       * Clear all state because the grid will be used for a completely different set of data.
       */
      private void clearAllState() {
          // Clear all layout records and views
          mLayoutRecords.clear();
          removeAllViews();
  
          // Reset to the top of the grid
          resetStateForGridTop();
  
          // Clear recycler because there could be different view types now
          mRecycler.clear();
  
          mSelectorRect.setEmpty();
          mSelectorPosition = INVALID_POSITION;
      }
  
      /**
       * Reset all internal state to be at the top of the grid.
       */
      private void resetStateForGridTop() {
          // Reset mItemTops and mItemBottoms
          final int colCount = mColCount;
          if (mItemTops == null || mItemTops.length != colCount) {
              mItemTops = new int[colCount];
              mItemBottoms = new int[colCount];
          }
          final int top = getPaddingTop();
          Arrays.fill(mItemTops, top);
          Arrays.fill(mItemBottoms, top);
  
          // Reset the first visible position in the grid to be item 0
          mFirstPosition = 0;
          if(mRestoreOffsets!=null)
          Arrays.fill(mRestoreOffsets, 0);
      }
  
      /**
       * Scroll the list so the first visible position in the grid is the first item in the adapter.
       */
      public void setSelectionToTop() {
          // Clear out the views (but don't clear out the layout records or recycler because the data
          // has not changed)
          removeAllViews();
  
          // Reset to top of grid
          resetStateForGridTop();
  
          // Start populating again
          populate(false);
      }
  
      @Override
      protected LayoutParams generateDefaultLayoutParams() {
          return new LayoutParams(LayoutParams.WRAP_CONTENT);
      }
  
      @Override
      protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
      	return new LayoutParams(lp);
      }
  
      @Override
      protected boolean checkLayoutParams(ViewGroup.LayoutParams lp) {
          return lp instanceof LayoutParams;
      }
  
      @Override
      public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
          return new LayoutParams(getContext(), attrs);
      }
  
      @Override
      public Parcelable onSaveInstanceState() {
          final Parcelable superState = super.onSaveInstanceState();
          final SavedState ss = new SavedState(superState);
          final int position = mFirstPosition;
          ss.position = mFirstPosition;
  
          if (position >= 0 && mAdapter != null && position < mAdapter.getCount()) {
              ss.firstId = mAdapter.getItemId(position);
          }
  
          if (getChildCount() > 0) {
  
          	int topOffsets[]= new int[this.mColCount];
  
          	if(this.mColWidth>0)
          	for(int i =0; i < mColCount; i++){
          		if(getChildAt(i)!=null){
          			final View child = getChildAt(i);
          			final int left = child.getLeft();
          			int col = 0;
          			Log.w("mColWidth", mColWidth+" "+left);
  
          			// determine the column by cycling widths
          			while( left > col*(this.mColWidth + mItemMargin*2) + getPaddingLeft() ){
          				col++;
          			}
  
          			topOffsets[col] = getChildAt(i).getTop() - mItemMargin - getPaddingTop();
          		}
  
          	}
  
              ss.topOffsets = topOffsets;
  
              // convert nested arraylist so it can be parcelable
              ArrayList<ColMap> convert = new ArrayList<ColMap>();
              for(HashSet<Integer> cols : mColMappings){
              	convert.add(new ColMap(new ArrayList<Integer>(cols)));
              }
  
              ss.mapping = convert;
          }
          return ss;
      }
  
      @Override
      public void onRestoreInstanceState(Parcelable state) {
          SavedState ss = (SavedState) state;
          super.onRestoreInstanceState(ss.getSuperState());
          mDataChanged = true;
          mFirstPosition = ss.position;
          mRestoreOffsets = ss.topOffsets;
  
          ArrayList<ColMap> convert = ss.mapping;
  
          if(convert != null){
          	mColMappings.clear();
          	for(ColMap colMap : convert){
          		mColMappings.add(new HashSet<Integer>(colMap.values));
          	}
          }
  
          if(ss.firstId>=0){
          	this.mFirstAdapterId = ss.firstId;
          	mSelectorPosition = INVALID_POSITION;
          }
  
          requestLayout();
      }
  
      public static class LayoutParams extends ViewGroup.LayoutParams {
          private static final int[] LAYOUT_ATTRS = new int[] {
              android.R.attr.layout_span
          };
  
          private static final int SPAN_INDEX = 0;
  
          /**
           * The number of columns this item should span
           */
          public int span = 1;
  
          /**
           * Item position this view represents
           */
          int position;
  
          /**
           * Type of this view as reported by the adapter
           */
          int viewType;
  
          /**
           * The column this view is occupying
           */
          int column;
  
          /**
           * The stable ID of the item this view displays
           */
          long id = -1;
  
          public LayoutParams(int height) {
              super(MATCH_PARENT, height);
  
              if (this.height == MATCH_PARENT) {
                  Log.w(TAG, "Constructing LayoutParams with height FILL_PARENT - " +
                          "impossible! Falling back to WRAP_CONTENT");
                  this.height = WRAP_CONTENT;
              }
          }
  
          public LayoutParams(Context c, AttributeSet attrs) {
              super(c, attrs);
  
              if (this.width != MATCH_PARENT) {
                  Log.w(TAG, "Inflation setting LayoutParams width to " + this.width +
                          " - must be MATCH_PARENT");
                  this.width = MATCH_PARENT;
              }
              if (this.height == MATCH_PARENT) {
                  Log.w(TAG, "Inflation setting LayoutParams height to MATCH_PARENT - " +
                          "impossible! Falling back to WRAP_CONTENT");
                  this.height = WRAP_CONTENT;
              }
  
              TypedArray a = c.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
              span = a.getInteger(SPAN_INDEX, 1);
              a.recycle();
          }
  
          public LayoutParams(ViewGroup.LayoutParams other) {
              super(other);
  
              if (this.width != MATCH_PARENT) {
                  Log.w(TAG, "Constructing LayoutParams with width " + this.width +
                          " - must be MATCH_PARENT");
                  this.width = MATCH_PARENT;
              }
              if (this.height == MATCH_PARENT) {
                  Log.w(TAG, "Constructing LayoutParams with height MATCH_PARENT - " +
                          "impossible! Falling back to WRAP_CONTENT");
                  this.height = WRAP_CONTENT;
              }
          }
      }
  
      private class RecycleBin {
          private ArrayList<View>[] mScrapViews;
          private int mViewTypeCount;
          private int mMaxScrap;
  
          private SparseArray<View> mTransientStateViews;
  
          public void setViewTypeCount(int viewTypeCount) {
              if (viewTypeCount < 1) {
                  throw new IllegalArgumentException("Must have at least one view type (" +
                          viewTypeCount + " types reported)");
              }
              if (viewTypeCount == mViewTypeCount) {
                  return;
              }
  
              @SuppressWarnings("unchecked")
  			ArrayList<View>[] scrapViews = new ArrayList[viewTypeCount];
  
              for (int i = 0; i < viewTypeCount; i++) {
                  scrapViews[i] = new ArrayList<View>();
              }
              mViewTypeCount = viewTypeCount;
              mScrapViews = scrapViews;
          }
  
          public void clear() {
              final int typeCount = mViewTypeCount;
              for (int i = 0; i < typeCount; i++) {
                  mScrapViews[i].clear();
              }
              if (mTransientStateViews != null) {
                  mTransientStateViews.clear();
              }
          }
  
          public void clearTransientViews() {
              if (mTransientStateViews != null) {
                  mTransientStateViews.clear();
              }
          }
  
          public void addScrap(View v) {
              final LayoutParams lp = (LayoutParams) v.getLayoutParams();
              if (ViewCompat.hasTransientState(v)) {
                  if (mTransientStateViews == null) {
                      mTransientStateViews = new SparseArray<View>();
                  }
                  mTransientStateViews.put(lp.position, v);
                  return;
              }
  
              final int childCount = getChildCount();
              if (childCount > mMaxScrap) {
                  mMaxScrap = childCount;
              }
  
              ArrayList<View> scrap = mScrapViews[lp.viewType];
              if (scrap.size() < mMaxScrap) {
                  scrap.add(v);
              }
          }
  
          public View getTransientStateView(int position) {
              if (mTransientStateViews == null) {
                  return null;
              }
  
              final View result = mTransientStateViews.get(position);
              if (result != null) {
                  mTransientStateViews.remove(position);
              }
              return result;
          }
  
          public View getScrapView(int type) {
              ArrayList<View> scrap = mScrapViews[type];
              if (scrap.isEmpty()) {
                  return null;
              }
  
              final int index = scrap.size() - 1;
              final View result = scrap.get(index);
              scrap.remove(index);
              return result;
          }
      }
  
      private class AdapterDataSetObserver extends DataSetObserver {
          @Override
          public void onChanged() {
              mDataChanged = true;
              mItemCount = mAdapter.getCount();
  
              // TODO: Consider matching these back up if we have stable IDs.
              mRecycler.clearTransientViews();
  
              if (!mHasStableIds) {
                  // Clear all layout records and recycle the views
                  mLayoutRecords.clear();
                  recycleAllViews();
  
                  // Reset item bottoms to be equal to item tops
                  final int colCount = mColCount;
                  for (int i = 0; i < colCount; i++) {
                      mItemBottoms[i] = mItemTops[i];
                  }
              }
  
              // reset list if position does not exist or id for position has changed
              if(mFirstPosition > mItemCount-1 || mAdapter.getItemId(mFirstPosition) != mFirstAdapterId){
              	mFirstPosition = 0;
              	Arrays.fill(mItemTops, 0);
              	Arrays.fill(mItemBottoms, 0);
  
              	if(mRestoreOffsets!=null)
              	Arrays.fill(mRestoreOffsets, 0);
              }
  
              // TODO: consider repopulating in a deferred runnable instead
              // (so that successive changes may still be batched)
              requestLayout();
          }
  
          @Override
          public void onInvalidated() {
          }
      }
  
      static class ColMap implements Parcelable {
      	private ArrayList<Integer> values;
      	int tempMap[];
  
      	public ColMap(ArrayList<Integer> values){
      		this.values = values;
      	}
  
      	private ColMap(Parcel in) {
      		tempMap = in.createIntArray();
      		values = new ArrayList<Integer>();
      	    for (int index = 0; index < tempMap.length; index++) {
      	    	values.add(tempMap[index]);
      	    }
      	}
  
  		@Override
  		public void writeToParcel(Parcel out, int flags) {
  			tempMap = toIntArray(values);
  			out.writeIntArray(tempMap);
  		}
  
  		public static final Creator<ColMap> CREATOR = new Creator<ColMap>() {
  			public ColMap createFromParcel(Parcel source) {
  				return new ColMap(source);
  			}
  
  			public ColMap[] newArray(int size) {
  				return new ColMap[size];
  			}
  		};
  
  		int[] toIntArray(ArrayList<Integer> list) {
  			int[] ret = new int[list.size()];
  			for (int i = 0; i < ret.length; i++)
  				ret[i] = list.get(i);
  			return ret;
  		}
  
  		@Override
  		public int describeContents() {
  			return 0;
  		}
      }
  
      static class SavedState extends BaseSavedState {
          long firstId = -1;
          int position;
          int topOffsets[];
          ArrayList<ColMap> mapping;
  
          SavedState(Parcelable superState) {
              super(superState);
          }
  
          private SavedState(Parcel in) {
              super(in);
              firstId = in.readLong();
              position = in.readInt();
              topOffsets = in.createIntArray();
              mapping = in.createTypedArrayList(ColMap.CREATOR);
          }
  
          @Override
          public void writeToParcel(Parcel out, int flags) {
              super.writeToParcel(out, flags);
              out.writeLong(firstId);
              out.writeInt(position);
              out.writeIntArray(topOffsets);
              out.writeTypedList(mapping);
          }
  
          @Override
          public String toString() {
              return "StaggereGridView.SavedState{"
                          + Integer.toHexString(System.identityHashCode(this))
                          + " firstId=" + firstId
                          + " position=" + position + "}";
          }
  
          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];
              }
          };
      }
  
      /**
       * A base class for Runnables that will check that their view is still attached to
       * the original window as when the Runnable was created.
       *
       */
      private class WindowRunnnable {
          private int mOriginalAttachCount;
  
          public void rememberWindowAttachCount() {
              mOriginalAttachCount = getWindowAttachCount();
          }
  
          public boolean sameWindow() {
              return hasWindowFocus() && getWindowAttachCount() == mOriginalAttachCount;
          }
      }
  
      private void useDefaultSelector() {
          setSelector(getResources().getDrawable(android.R.drawable.list_selector_background));
      }
  
  
  	void positionSelector(int position, View sel) {
          if (position != INVALID_POSITION) {
              mSelectorPosition = position;
          }
  
          final Rect selectorRect = mSelectorRect;
          selectorRect.set(sel.getLeft(), sel.getTop(), sel.getRight(), sel.getBottom());
          if (sel instanceof SelectionBoundsAdjuster) {
              ((SelectionBoundsAdjuster)sel).adjustListItemSelectionBounds(selectorRect);
          }
  
          positionSelector(selectorRect.left, selectorRect.top, selectorRect.right,
                  selectorRect.bottom);
  
          final boolean isChildViewEnabled = mIsChildViewEnabled;
          if (sel.isEnabled() != isChildViewEnabled) {
              mIsChildViewEnabled = !isChildViewEnabled;
              if (getSelectedItemPosition() != INVALID_POSITION) {
                  refreshDrawableState();
              }
          }
      }
  
  	/**
       * The top-level view of a list item can implement this interface to allow
       * itself to modify the bounds of the selection shown for that item.
       */
      public interface SelectionBoundsAdjuster {
          /**
           * Called to allow the list item to adjust the bounds shown for
           * its selection.
           *
           * @param bounds On call, this contains the bounds the list has
           * selected for the item (that is the bounds of the entire view).  The
           * values can be modified as desired.
           */
          public void adjustListItemSelectionBounds(Rect bounds);
      }
  
      private int getSelectedItemPosition(){
      	// TODO: setup mNextSelectedPosition
      	return this.mSelectorPosition;
      }
  
      @Override
      protected int[] onCreateDrawableState(int extraSpace) {
          // If the child view is enabled then do the default behavior.
          if (mIsChildViewEnabled) {
              // Common case
              return super.onCreateDrawableState(extraSpace);
          }
  
          // The selector uses this View's drawable state. The selected child view
          // is disabled, so we need to remove the enabled state from the drawable
          // states.
          final int enabledState = ENABLED_STATE_SET[0];
  
          // If we don't have any extra space, it will return one of the static state arrays,
          // and clearing the enabled state on those arrays is a bad thing!  If we specify
          // we need extra space, it will create+copy into a new array that safely mutable.
          int[] state = super.onCreateDrawableState(extraSpace + 1);
          int enabledPos = -1;
          for (int i = state.length - 1; i >= 0; i--) {
              if (state[i] == enabledState) {
                  enabledPos = i;
                  break;
              }
          }
  
          // Remove the enabled state
          if (enabledPos >= 0) {
              System.arraycopy(state, enabledPos + 1, state, enabledPos,
                      state.length - enabledPos - 1);
          }
  
          return state;
      }
  
  
      private void positionSelector(int l, int t, int r, int b) {
          mSelectorRect.set(l - mSelectionLeftPadding, t - mSelectionTopPadding, r
                  + mSelectionRightPadding, b + mSelectionBottomPadding);
      }
  
      final class CheckForTap implements Runnable {
          public void run() {
              if (mTouchMode == TOUCH_MODE_DOWN) {
  
                  mTouchMode = TOUCH_MODE_TAP;
                  final View child = getChildAt(mMotionPosition - mFirstPosition);
                  if (child != null && !child.hasFocusable()) {
  
                      if (!mDataChanged) {
                      	child.setSelected(true);
                      	child.setPressed(true);
  
                          setPressed(true);
                          positionSelector(mMotionPosition, child);
                          refreshDrawableState();
  
                          final int longPressTimeout = ViewConfiguration.getLongPressTimeout();
                          final boolean longClickable = isLongClickable();
  
                          if (mSelector != null) {
                              Drawable d = mSelector.getCurrent();
                              if (d instanceof TransitionDrawable) {
                                  if (longClickable) {
                                      ((TransitionDrawable) d).startTransition(longPressTimeout);
                                  } else {
                                      ((TransitionDrawable) d).resetTransition();
                                  }
                              }
                          }
  
                          if (longClickable) {
                              if (mPendingCheckForLongPress == null) {
                                  mPendingCheckForLongPress = new CheckForLongPress();
                              }
                              mPendingCheckForLongPress.rememberWindowAttachCount();
                              postDelayed(mPendingCheckForLongPress, longPressTimeout);
                          } else {
                              mTouchMode = TOUCH_MODE_DONE_WAITING;
                          }
  
                          postInvalidate();
                      } else {
                          mTouchMode = TOUCH_MODE_DONE_WAITING;
                      }
                  }
              }
          }
      }
  
      private class CheckForLongPress extends WindowRunnnable implements Runnable {
          public void run() {
              final int motionPosition = mMotionPosition;
              final View child = getChildAt(motionPosition - mFirstPosition);
              if (child != null) {
                  final int longPressPosition = mMotionPosition;
                  final long longPressId = mAdapter.getItemId(mMotionPosition);
  
                  boolean handled = false;
                  if (sameWindow() && !mDataChanged) {
                      handled = performLongPress(child, longPressPosition, longPressId);
                  }
                  if (handled) {
                      mTouchMode = TOUCH_MODE_REST;
                      setPressed(false);
                      child.setPressed(false);
                  } else {
                      mTouchMode = TOUCH_MODE_DONE_WAITING;
                  }
              }
          }
      }
  
      private class PerformClick extends WindowRunnnable implements Runnable {
          int mClickMotionPosition;
  
          public void run() {
              // The data has changed since we posted this action in the event queue,
              // bail out before bad things happen
              if (mDataChanged) return;
  
              final ListAdapter adapter = mAdapter;
              final int motionPosition = mClickMotionPosition;
              if (adapter != null && mItemCount > 0 &&
                      motionPosition != INVALID_POSITION &&
                      motionPosition < adapter.getCount() && sameWindow()) {
                  final View view = getChildAt(motionPosition - mFirstPosition);
                  // If there is no view, something bad happened (the view scrolled off the
                  // screen, etc.) and we should cancel the click
                  if (view != null) {
                      performItemClick(view, motionPosition, adapter.getItemId(motionPosition));
                  }
              }
          }
      }
  
      public boolean performItemClick(View view, int position, long id) {
          if (mOnItemClickListener != null) {
              playSoundEffect(SoundEffectConstants.CLICK);
              if (view != null) {
                  view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
              }
              mOnItemClickListener.onItemClick(this, view, position, id);
              return true;
          }
  
          return false;
      }
  
      boolean performLongPress(final View child,
              final int longPressPosition, final long longPressId) {
  
      	// TODO : add check for multiple choice mode.. currently modes are yet to be supported
  
          boolean handled = false;
          if (mOnItemLongClickListener != null) {
              handled = mOnItemLongClickListener.onItemLongClick(this, child, longPressPosition, longPressId);
          }
          if (!handled) {
              mContextMenuInfo = createContextMenuInfo(child, longPressPosition, longPressId);
              handled = super.showContextMenuForChild(this);
          }
          if (handled) {
              performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
          }
          return handled;
      }
  
      @Override
      protected ContextMenuInfo getContextMenuInfo() {
          return mContextMenuInfo;
      }
  
      /**
       * Creates the ContextMenuInfo returned from {@link #getContextMenuInfo()}. This
       * methods knows the view, position and ID of the item that received the
       * long press.
       *
       * @param view The view that received the long press.
       * @param position The position of the item that received the long press.
       * @param id The ID of the item that received the long press.
       * @return The extra information that should be returned by
       *         {@link #getContextMenuInfo()}.
       */
      ContextMenuInfo createContextMenuInfo(View view, int position, long id) {
          return new AdapterContextMenuInfo(view, position, id);
      }
  
      /**
       * Extra menu information provided to the
       * {@link android.view.View.OnCreateContextMenuListener#onCreateContextMenu(ContextMenu, View, ContextMenuInfo) }
       * callback when a context menu is brought up for this AdapterView.
       *
       */
      public static class AdapterContextMenuInfo implements ContextMenu.ContextMenuInfo {
  
          public AdapterContextMenuInfo(View targetView, int position, long id) {
              this.targetView = targetView;
              this.position = position;
              this.id = id;
          }
  
          /**
           * The child view for which the context menu is being displayed. This
           * will be one of the children of this AdapterView.
           */
          public View targetView;
  
          /**
           * The position in the adapter for which the context menu is being
           * displayed.
           */
          public int position;
  
          /**
           * The row id of the item for which the context menu is being displayed.
           */
          public long id;
      }
  
      /**
       * Returns the selector {@link android.graphics.drawable.Drawable} that is used to draw the
       * selection in the list.
       *
       * @return the drawable used to display the selector
       */
      public Drawable getSelector() {
          return mSelector;
      }
  
      /**
       * Set a Drawable that should be used to highlight the currently selected item.
       *
       * @param resID A Drawable resource to use as the selection highlight.
       *
       * @attr ref android.R.styleable#AbsListView_listSelector
       */
      public void setSelector(int resID) {
          setSelector(getResources().getDrawable(resID));
      }
  
      @Override
      public boolean verifyDrawable(Drawable dr) {
          return mSelector == dr || super.verifyDrawable(dr);
      }
  
      @TargetApi(Build.VERSION_CODES.HONEYCOMB)
  	@Override
      public void jumpDrawablesToCurrentState() {
          super.jumpDrawablesToCurrentState();
          if (mSelector != null) mSelector.jumpToCurrentState();
      }
  
      public void setSelector(Drawable sel) {
          if (mSelector != null) {
              mSelector.setCallback(null);
              unscheduleDrawable(mSelector);
          }
  
          mSelector = sel;
  
          if(mSelector==null){
          	return;
          }
  
          Rect padding = new Rect();
          sel.getPadding(padding);
          mSelectionLeftPadding = padding.left;
          mSelectionTopPadding = padding.top;
          mSelectionRightPadding = padding.right;
          mSelectionBottomPadding = padding.bottom;
          sel.setCallback(this);
          updateSelectorState();
      }
  
      void updateSelectorState() {
          if (mSelector != null) {
              if (shouldShowSelector()) {
                  mSelector.setState(getDrawableState());
              } else {
                  mSelector.setState(new int[] { 0 });
              }
          }
      }
  
      @Override
      protected void drawableStateChanged() {
          super.drawableStateChanged();
          updateSelectorState();
      }
  
      /**
       * Indicates whether this view is in a state where the selector should be drawn. This will
       * happen if we have focus but are not in touch mode, or we are in the middle of displaying
       * the pressed state for an item.
       *
       * @return True if the selector should be shown
       */
      boolean shouldShowSelector() {
          return ((hasFocus() && !isInTouchMode()) || touchModeDrawsInPressedState()) &&  ( mBeginClick ) ;
      }
  
      /**
       * @return True if the current touch mode requires that we draw the selector in the pressed
       *         state.
       */
      boolean touchModeDrawsInPressedState() {
          // FIXME use isPressed for this
          switch (mTouchMode) {
          case TOUCH_MODE_TAP:
          case TOUCH_MODE_DONE_WAITING:
              return true;
          default:
              return false;
          }
      }
  
      /**
       * Register a callback to be invoked when an item in this AdapterView has
       * been clicked.
       *
       * @param listener The callback that will be invoked.
       */
      public void setOnItemClickListener(OnItemClickListener listener) {
          mOnItemClickListener = listener;
      }
  
      /**
       * @return The callback to be invoked with an item in this AdapterView has
       *         been clicked, or null id no callback has been set.
       */
      public final OnItemClickListener getOnItemClickListener() {
          return mOnItemClickListener;
      }
  
      public interface OnItemClickListener {
  
          /**
           * Callback method to be invoked when an item in this AdapterView has
           * been clicked.
           * <p>
           * Implementers can call getItemAtPosition(position) if they need
           * to access the data associated with the selected item.
           *
           * @param parent The AdapterView where the click happened.
           * @param view The view within the AdapterView that was clicked (this
           *            will be a view provided by the adapter)
           * @param position The position of the view in the adapter.
           * @param id The row id of the item that was clicked.
           */
          void onItemClick(StaggeredGridView parent, View view, int position, long id);
      }
  
      /**
       * Register a callback to be invoked when an item in this AdapterView has
       * been clicked and held
       *
       * @param listener The callback that will run
       */
      public void setOnItemLongClickListener(OnItemLongClickListener listener) {
          if (!isLongClickable()) {
              setLongClickable(true);
          }
          mOnItemLongClickListener = listener;
      }
  
      /**
       * @return The callback to be invoked with an item in this AdapterView has
       *         been clicked and held, or null id no callback as been set.
       */
      public final OnItemLongClickListener getOnItemLongClickListener() {
          return mOnItemLongClickListener;
      }
  
      public interface OnItemLongClickListener {
          /**
           * Callback method to be invoked when an item in this view has been
           * clicked and held.
           *
           * Implementers can call getItemAtPosition(position) if they need to access
           * the data associated with the selected item.
           *
           * @param parent The AbsListView where the click happened
           * @param view The view within the AbsListView that was clicked
           * @param position The position of the view in the list
           * @param id The row id of the item that was clicked
           *
           * @return true if the callback consumed the long click, false otherwise
           */
          boolean onItemLongClick(StaggeredGridView parent, View view, int position, long id);
      }
  
      /**
       * Maps a point to a position in the list.
       *
       * @param x X in local coordinate
       * @param y Y in local coordinate
       * @return The position of the item which contains the specified point, or
       *         {@link #INVALID_POSITION} if the point does not intersect an item.
       */
      public int pointToPosition(int x, int y) {
          Rect frame = mTouchFrame;
          if (frame == null) {
              mTouchFrame = new Rect();
              frame = mTouchFrame;
          }
  
          final int count = getChildCount();
          for (int i = count - 1; i >= 0; i--) {
              final View child = getChildAt(i);
              if (child.getVisibility() == View.VISIBLE) {
                  child.getHitRect(frame);
                  if (frame.contains(x, y)) {
                      return mFirstPosition + i;
                  }
              }
          }
          return INVALID_POSITION;
      }
  
  	public boolean isDrawSelectorOnTop() {
  		return mDrawSelectorOnTop;
  	}
  
  	public void setDrawSelectorOnTop(boolean mDrawSelectorOnTop) {
  		this.mDrawSelectorOnTop = mDrawSelectorOnTop;
  	}
  }
  