Bug 1034167 - Part 1: Update TwoWayView from upstream. r=mfinkle, a=sledru
authorLucas Rocha <lucasr@mozilla.com>
Mon, 14 Jul 2014 17:31:50 +0100
changeset 208098 c82cd497bd9b0d2188e670eb3323b11b4966fbdf
parent 208097 69302cccb87799a11bb6fb84b64227d3a94b4c86
child 208099 0b4778f147a12df315d2aa2b2b86c30a71279618
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmfinkle, sledru
bugs1034167
milestone32.0a2
Bug 1034167 - Part 1: Update TwoWayView from upstream. r=mfinkle, a=sledru
mobile/android/base/resources/values/attrs.xml
mobile/android/base/widget/TwoWayView.java
--- a/mobile/android/base/resources/values/attrs.xml
+++ b/mobile/android/base/resources/values/attrs.xml
@@ -117,91 +117,20 @@
 
     <declare-styleable name="LightweightTheme">
         <attr name="state_light" format="boolean"/>
         <attr name="state_dark" format="boolean"/>
         <attr name="autoUpdateTheme" format="boolean"/>
     </declare-styleable>
 
     <declare-styleable name="TwoWayView">
-        <!-- Imported from View -->
-        <attr name="android:id"/>
-        <attr name="android:tag"/>
-        <attr name="android:scrollX"/>
-        <attr name="android:scrollY"/>
-        <attr name="android:background"/>
-        <attr name="android:padding"/>
-        <attr name="android:paddingLeft"/>
-        <attr name="android:paddingTop"/>
-        <attr name="android:paddingRight"/>
-        <attr name="android:paddingBottom"/>
-        <attr name="android:paddingStart"/>
-        <attr name="android:paddingEnd"/>
-        <attr name="android:focusable"/>
-        <attr name="android:focusableInTouchMode"/>
-        <attr name="android:visibility"/>
-        <attr name="android:fitsSystemWindows"/>
-        <attr name="android:scrollbars"/>
-        <attr name="android:scrollbarStyle"/>
-        <attr name="android:isScrollContainer"/>
-        <attr name="android:fadeScrollbars"/>
-        <attr name="android:scrollbarFadeDuration"/>
-        <attr name="android:scrollbarDefaultDelayBeforeFade"/>
-        <attr name="android:scrollbarSize"/>
-        <attr name="android:scrollbarThumbHorizontal"/>
-        <attr name="android:scrollbarThumbVertical"/>
-        <attr name="android:scrollbarTrackHorizontal"/>
-        <attr name="android:scrollbarTrackVertical"/>
-        <attr name="android:scrollbarAlwaysDrawHorizontalTrack"/>
-        <attr name="android:scrollbarAlwaysDrawVerticalTrack"/>
-        <attr name="android:fadingEdge"/>
-        <attr name="android:requiresFadingEdge"/>
-        <attr name="android:fadingEdgeLength"/>
-        <attr name="android:nextFocusLeft"/>
-        <attr name="android:nextFocusRight"/>
-        <attr name="android:nextFocusUp"/>
-        <attr name="android:nextFocusDown"/>
-        <attr name="android:nextFocusForward"/>
-        <attr name="android:clickable"/>
-        <attr name="android:longClickable"/>
-        <attr name="android:saveEnabled"/>
-        <attr name="android:filterTouchesWhenObscured"/>
-        <attr name="android:drawingCacheQuality"/>
-        <attr name="android:keepScreenOn"/>
-        <attr name="android:duplicateParentState"/>
-        <attr name="android:minHeight"/>
-        <attr name="android:minWidth"/>
-        <attr name="android:soundEffectsEnabled"/>
-        <attr name="android:hapticFeedbackEnabled"/>
-        <attr name="android:contentDescription"/>
-        <attr name="android:onClick"/>
-        <attr name="android:overScrollMode"/>
-        <attr name="android:alpha"/>
-        <attr name="android:translationX"/>
-        <attr name="android:translationY"/>
-        <attr name="android:transformPivotX"/>
-        <attr name="android:transformPivotY"/>
-        <attr name="android:rotation"/>
-        <attr name="android:rotationX"/>
-        <attr name="android:rotationY"/>
-        <attr name="android:scaleX"/>
-        <attr name="android:scaleY"/>
-        <attr name="android:verticalScrollbarPosition"/>
-        <attr name="android:layerType"/>
-        <attr name="android:layoutDirection"/>
-        <attr name="android:textDirection"/>
-        <attr name="android:textAlignment"/>
-
-        <!-- Imported from Android -->
         <attr name="android:orientation"/>
-
-        <!-- Imported from AbsListView -->
         <attr name="android:choiceMode"/>
+        <attr name="android:listSelector"/>
         <attr name="android:drawSelectorOnTop"/>
-        <attr name="android:listSelector"/>
     </declare-styleable>
 
     <declare-styleable name="HomeListView">
         <!-- Draws a divider on top of the list, if true. Defaults to false. -->
         <attr name="topDivider" format="boolean"/>
     </declare-styleable>
 
     <declare-styleable name="HomePagerTabStrip">
--- a/mobile/android/base/widget/TwoWayView.java
+++ b/mobile/android/base/widget/TwoWayView.java
@@ -18,16 +18,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 package org.mozilla.gecko.widget;
 
 import org.mozilla.gecko.R;
 
+import java.util.ArrayList;
+import java.util.List;
+
 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;
@@ -62,18 +65,17 @@ import android.view.ViewParent;
 import android.view.ViewTreeObserver;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.AdapterView;
 import android.widget.Checkable;
 import android.widget.ListAdapter;
 import android.widget.Scroller;
 
-import java.util.ArrayList;
-import java.util.List;
+import static android.os.Build.VERSION_CODES.HONEYCOMB;
 
 /*
  * Implementation Notes:
  *
  * Some terminology:
  *
  *     index    - index of the items that are currently visible
  *     position - index of the items in the cursor
@@ -131,18 +133,18 @@ public class TwoWayView extends AdapterV
     public static enum ChoiceMode {
         NONE,
         SINGLE,
         MULTIPLE
     }
 
     public static enum Orientation {
         HORIZONTAL,
-        VERTICAL;
-    };
+        VERTICAL
+    }
 
     private ListAdapter mAdapter;
 
     private boolean mIsVertical;
 
     private int mItemMargin;
 
     private boolean mInLayout;
@@ -261,17 +263,17 @@ public class TwoWayView extends AdapterV
          * animation is now coasting to a stop
          */
         public static int SCROLL_STATE_FLING = 2;
 
         /**
          * Callback method to be invoked while the list view or grid view is being scrolled. If the
          * view is being scrolled, this method will be called before the next frame of the scroll is
          * rendered. In particular, it will be called before any calls to
-         * {@link Adapter#getView(int, View, ViewGroup)}.
+         * {@link android.widget.Adapter#getView(int, View, ViewGroup)}.
          *
          * @param view The view whose scroll state is being reported
          *
          * @param scrollState The current scroll state. One of {@link #SCROLL_STATE_IDLE},
          * {@link #SCROLL_STATE_TOUCH_SCROLL} or {@link #SCROLL_STATE_IDLE}.
          */
         public void onScrollStateChanged(TwoWayView view, int scrollState);
 
@@ -383,17 +385,16 @@ public class TwoWayView extends AdapterV
         setWillNotDraw(false);
         setAlwaysDrawnWithCacheEnabled(false);
         setWillNotDraw(false);
         setClipToPadding(false);
 
         ViewCompat.setOverScrollMode(this, ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS);
 
         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TwoWayView, defStyle, 0);
-        initializeScrollbars(a);
 
         mDrawSelectorOnTop = a.getBoolean(
                 R.styleable.TwoWayView_android_drawSelectorOnTop, false);
 
         Drawable d = a.getDrawable(R.styleable.TwoWayView_android_listSelector);
         if (d != null) {
             setSelector(d);
         }
@@ -404,29 +405,26 @@ public class TwoWayView extends AdapterV
         }
 
         int choiceMode = a.getInt(R.styleable.TwoWayView_android_choiceMode, -1);
         if (choiceMode >= 0) {
             setChoiceMode(ChoiceMode.values()[choiceMode]);
         }
 
         a.recycle();
-
-        updateScrollbarsDirection();
     }
 
     public void setOrientation(Orientation orientation) {
-        final boolean isVertical = (orientation.compareTo(Orientation.VERTICAL) == 0);
+        final boolean isVertical = (orientation == Orientation.VERTICAL);
         if (mIsVertical == isVertical) {
             return;
         }
 
         mIsVertical = isVertical;
 
-        updateScrollbarsDirection();
         resetState();
         mRecycler.clear();
 
         requestLayout();
     }
 
     public Orientation getOrientation() {
         return (mIsVertical ? Orientation.VERTICAL : Orientation.HORIZONTAL);
@@ -436,37 +434,40 @@ public class TwoWayView extends AdapterV
         if (mItemMargin == itemMargin) {
             return;
         }
 
         mItemMargin = itemMargin;
         requestLayout();
     }
 
+    @SuppressWarnings("unused")
     public int getItemMargin() {
         return mItemMargin;
     }
 
     /**
      * Indicates that the views created by the ListAdapter can contain focusable
      * items.
      *
      * @param itemsCanFocus true if items can get focus, false otherwise
      */
+    @SuppressWarnings("unused")
     public void setItemsCanFocus(boolean itemsCanFocus) {
         mItemsCanFocus = itemsCanFocus;
         if (!itemsCanFocus) {
             setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
         }
     }
 
     /**
      * @return Whether the views created by the ListAdapter can contain focusable
      * items.
      */
+    @SuppressWarnings("unused")
     public boolean getItemsCanFocus() {
         return mItemsCanFocus;
     }
 
     /**
      * Set the listener that will receive notifications every time the list scrolls.
      *
      * @param l the scroll listener
@@ -476,46 +477,48 @@ public class TwoWayView extends AdapterV
         invokeOnItemScrollListener();
     }
 
     /**
      * Sets the recycler listener to be notified whenever a View is set aside in
      * the recycler for later reuse. This listener can be used to free resources
      * associated to the View.
      *
-     * @param listener The recycler listener to be notified of views set aside
+     * @param l The recycler listener to be notified of views set aside
      *        in the recycler.
      *
      * @see TwoWayView.RecycleBin
      * @see TwoWayView.RecyclerListener
      */
     public void setRecyclerListener(RecyclerListener l) {
         mRecycler.mRecyclerListener = l;
     }
 
     /**
      * Controls whether the selection highlight drawable should be drawn on top of the item or
      * behind it.
      *
-     * @param onTop If true, the selector will be drawn on the item it is highlighting. The default
-     *        is false.
+     * @param drawSelectorOnTop If true, the selector will be drawn on the item it is highlighting.
+     *                          The default is false.
      *
      * @attr ref android.R.styleable#AbsListView_drawSelectorOnTop
      */
+    @SuppressWarnings("unused")
     public void setDrawSelectorOnTop(boolean drawSelectorOnTop) {
         mDrawSelectorOnTop = drawSelectorOnTop;
     }
 
     /**
      * 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
      */
+    @SuppressWarnings("unused")
     public void setSelector(int resID) {
         setSelector(getResources().getDrawable(resID));
     }
 
     /**
      * Set a Drawable that should be used to highlight the currently selected item.
      *
      * @param selector A Drawable to use as the selection highlight.
@@ -537,16 +540,17 @@ public class TwoWayView extends AdapterV
     }
 
     /**
      * 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
      */
+    @SuppressWarnings("unused")
     public Drawable getSelector() {
         return mSelector;
     }
 
     /**
      * {@inheritDoc}
      */
     @Override
@@ -559,124 +563,124 @@ public class TwoWayView extends AdapterV
      */
     @Override
     public long getSelectedItemId() {
         return mNextSelectedRowId;
     }
 
     /**
      * Returns the number of items currently selected. This will only be valid
-     * if the choice mode is not {@link #CHOICE_MODE_NONE} (default).
+     * if the choice mode is not {@link ChoiceMode#NONE} (default).
      *
      * <p>To determine the specific items that are currently selected, use one of
      * the <code>getChecked*</code> methods.
      *
      * @return The number of items currently selected
      *
      * @see #getCheckedItemPosition()
      * @see #getCheckedItemPositions()
      * @see #getCheckedItemIds()
      */
+    @SuppressWarnings("unused")
     public int getCheckedItemCount() {
         return mCheckedItemCount;
     }
 
     /**
      * Returns the checked state of the specified position. The result is only
-     * valid if the choice mode has been set to {@link #CHOICE_MODE_SINGLE}
-     * or {@link #CHOICE_MODE_MULTIPLE}.
+     * valid if the choice mode has been set to {@link ChoiceMode#SINGLE}
+     * or {@link ChoiceMode#MULTIPLE}.
      *
      * @param position The item whose checked state to return
      * @return The item's checked state or <code>false</code> if choice mode
      *         is invalid
      *
-     * @see #setChoiceMode(int)
+     * @see #setChoiceMode(ChoiceMode)
      */
     public boolean isItemChecked(int position) {
-        if (mChoiceMode.compareTo(ChoiceMode.NONE) == 0 && mCheckStates != null) {
+        if (mChoiceMode == ChoiceMode.NONE && mCheckStates != null) {
             return mCheckStates.get(position);
         }
 
         return false;
     }
 
     /**
      * Returns the currently checked item. The result is only valid if the choice
-     * mode has been set to {@link #CHOICE_MODE_SINGLE}.
+     * mode has been set to {@link ChoiceMode#SINGLE}.
      *
      * @return The position of the currently checked item or
      *         {@link #INVALID_POSITION} if nothing is selected
      *
-     * @see #setChoiceMode(int)
+     * @see #setChoiceMode(ChoiceMode)
      */
     public int getCheckedItemPosition() {
-        if (mChoiceMode.compareTo(ChoiceMode.SINGLE) == 0 &&
-                mCheckStates != null && mCheckStates.size() == 1) {
+        if (mChoiceMode == ChoiceMode.SINGLE && mCheckStates != null && mCheckStates.size() == 1) {
             return mCheckStates.keyAt(0);
         }
 
         return INVALID_POSITION;
     }
 
     /**
      * Returns the set of checked items in the list. The result is only valid if
-     * the choice mode has not been set to {@link #CHOICE_MODE_NONE}.
+     * the choice mode has not been set to {@link ChoiceMode#NONE}.
      *
      * @return  A SparseBooleanArray which will return true for each call to
      *          get(int position) where position is a position in the list,
      *          or <code>null</code> if the choice mode is set to
-     *          {@link #CHOICE_MODE_NONE}.
+     *          {@link ChoiceMode#NONE}.
      */
     public SparseBooleanArray getCheckedItemPositions() {
-        if (mChoiceMode.compareTo(ChoiceMode.NONE) != 0) {
+        if (mChoiceMode != ChoiceMode.NONE) {
             return mCheckStates;
         }
 
         return null;
     }
 
     /**
      * Returns the set of checked items ids. The result is only valid if the
-     * choice mode has not been set to {@link #CHOICE_MODE_NONE} and the adapter
+     * choice mode has not been set to {@link ChoiceMode#NONE} and the adapter
      * has stable IDs. ({@link ListAdapter#hasStableIds()} == {@code true})
      *
      * @return A new array which contains the id of each checked item in the
      *         list.
      */
     public long[] getCheckedItemIds() {
-        if (mChoiceMode.compareTo(ChoiceMode.NONE) == 0 ||
-                mCheckedIdStates == null || mAdapter == null) {
+        if (mChoiceMode == ChoiceMode.NONE || mCheckedIdStates == null || mAdapter == null) {
             return new long[0];
         }
 
         final LongSparseArray<Integer> idStates = mCheckedIdStates;
         final int count = idStates.size();
         final long[] ids = new long[count];
 
         for (int i = 0; i < count; i++) {
             ids[i] = idStates.keyAt(i);
         }
 
         return ids;
     }
 
     /**
      * Sets the checked state of the specified position. The is only valid if
-     * the choice mode has been set to {@link #CHOICE_MODE_SINGLE} or
-     * {@link #CHOICE_MODE_MULTIPLE}.
+     * the choice mode has been set to {@link ChoiceMode#SINGLE} or
+     * {@link ChoiceMode#MULTIPLE}.
      *
      * @param position The item whose checked state is to be checked
      * @param value The new checked state for the item
      */
+    @SuppressWarnings("unused")
     public void setItemChecked(int position, boolean value) {
-        if (mChoiceMode.compareTo(ChoiceMode.NONE) == 0) {
+        if (mChoiceMode == ChoiceMode.NONE) {
             return;
         }
 
-        if (mChoiceMode.compareTo(ChoiceMode.MULTIPLE) == 0) {
+        if (mChoiceMode == ChoiceMode.MULTIPLE) {
             boolean oldValue = mCheckStates.get(position);
             mCheckStates.put(position, value);
 
             if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
                 if (value) {
                     mCheckedIdStates.put(mAdapter.getItemId(position), position);
                 } else {
                     mCheckedIdStates.delete(mAdapter.getItemId(position));
@@ -724,50 +728,52 @@ public class TwoWayView extends AdapterV
             rememberSyncState();
             requestLayout();
         }
     }
 
     /**
      * Clear any choices previously set
      */
+    @SuppressWarnings("unused")
     public void clearChoices() {
         if (mCheckStates != null) {
             mCheckStates.clear();
         }
 
         if (mCheckedIdStates != null) {
             mCheckedIdStates.clear();
         }
 
         mCheckedItemCount = 0;
     }
 
     /**
-     * @see #setChoiceMode(int)
+     * @see #setChoiceMode(ChoiceMode)
      *
      * @return The current choice mode
      */
+    @SuppressWarnings("unused")
     public ChoiceMode getChoiceMode() {
         return mChoiceMode;
     }
 
     /**
      * Defines the choice behavior for the List. By default, Lists do not have any choice behavior
-     * ({@link #CHOICE_MODE_NONE}). By setting the choiceMode to {@link #CHOICE_MODE_SINGLE}, the
+     * ({@link ChoiceMode#NONE}). By setting the choiceMode to {@link ChoiceMode#SINGLE}, the
      * List allows up to one item to  be in a chosen state. By setting the choiceMode to
-     * {@link #CHOICE_MODE_MULTIPLE}, the list allows any number of items to be chosen.
+     * {@link ChoiceMode#MULTIPLE}, the list allows any number of items to be chosen.
      *
-     * @param choiceMode One of {@link #CHOICE_MODE_NONE}, {@link #CHOICE_MODE_SINGLE}, or
-     * {@link #CHOICE_MODE_MULTIPLE}
+     * @param choiceMode One of {@link ChoiceMode#NONE}, {@link ChoiceMode#SINGLE}, or
+     * {@link ChoiceMode#MULTIPLE}
      */
     public void setChoiceMode(ChoiceMode choiceMode) {
         mChoiceMode = choiceMode;
 
-        if (mChoiceMode.compareTo(ChoiceMode.NONE) != 0) {
+        if (mChoiceMode != ChoiceMode.NONE) {
             if (mCheckStates == null) {
                 mCheckStates = new SparseBooleanArray();
             }
 
             if (mCheckedIdStates == null && mAdapter != null && mAdapter.hasStableIds()) {
                 mCheckedIdStates = new LongSparseArray<Integer>();
             }
         }
@@ -808,18 +814,17 @@ public class TwoWayView extends AdapterV
             mDataSetObserver = new AdapterDataSetObserver();
             mAdapter.registerDataSetObserver(mDataSetObserver);
 
             mRecycler.setViewTypeCount(adapter.getViewTypeCount());
 
             mHasStableIds = adapter.hasStableIds();
             mAreAllItemsSelectable = adapter.areAllItemsEnabled();
 
-            if (mChoiceMode.compareTo(ChoiceMode.NONE) != 0 && mHasStableIds &&
-                    mCheckedIdStates == null) {
+            if (mChoiceMode != ChoiceMode.NONE && mHasStableIds && mCheckedIdStates == null) {
                 mCheckedIdStates = new LongSparseArray<Integer>();
             }
 
             final int position = lookForSelectablePosition(0);
             setSelectedPositionInt(position);
             setNextSelectedPositionInt(position);
 
             if (mItemCount == 0) {
@@ -1263,17 +1268,17 @@ public class TwoWayView extends AdapterV
             recycleVelocityTracker();
         }
 
         super.requestDisallowInterceptTouchEvent(disallowIntercept);
     }
 
     @Override
     public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (!mIsAttached) {
+        if (!mIsAttached || mAdapter == null) {
             return false;
         }
 
         final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;
         switch (action) {
         case MotionEvent.ACTION_DOWN:
             initOrResetVelocityTracker();
             mVelocityTracker.addMovement(ev);
@@ -1324,17 +1329,17 @@ public class TwoWayView extends AdapterV
 
             final float diff = pos - mLastTouchPos + mTouchRemainderPos;
             final int delta = (int) diff;
             mTouchRemainderPos = diff - delta;
 
             if (maybeStartScrolling(delta)) {
                 return true;
             }
-
+            
             break;
         }
 
         case MotionEvent.ACTION_CANCEL:
         case MotionEvent.ACTION_UP:
             mActivePointerId = INVALID_POINTER;
             mTouchMode = TOUCH_MODE_REST;
             recycleVelocityTracker();
@@ -1349,17 +1354,17 @@ public class TwoWayView extends AdapterV
     @Override
     public boolean onTouchEvent(MotionEvent ev) {
         if (!isEnabled()) {
             // A disabled view that is clickable still consumes the touch
             // events, it just doesn't respond to them.
             return isClickable() || isLongClickable();
         }
 
-        if (!mIsAttached) {
+        if (!mIsAttached || mAdapter == null) {
             return false;
         }
 
         boolean needsInvalidate = false;
 
         initVelocityTrackerIfNotExists();
         mVelocityTracker.addMovement(ev);
 
@@ -1443,18 +1448,16 @@ public class TwoWayView extends AdapterV
                 maybeScroll(delta);
                 break;
             }
 
             break;
         }
 
         case MotionEvent.ACTION_CANCEL:
-            // If this MotionEvent has been created by us, be sure not to store
-            // pointers to it outside of this method call because we recycle it.
             cancelCheckForTap();
             mTouchMode = TOUCH_MODE_REST;
             reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
 
             setPressed(false);
             View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
             if (motionView != null) {
                 motionView.setPressed(false);
@@ -1474,17 +1477,17 @@ public class TwoWayView extends AdapterV
             case TOUCH_MODE_TAP:
             case TOUCH_MODE_DONE_WAITING: {
                 final int motionPosition = mMotionPosition;
                 final View child = getChildAt(motionPosition - mFirstPosition);
 
                 final float x = ev.getX();
                 final float y = ev.getY();
 
-                boolean inList = false;
+                final boolean inList;
                 if (mIsVertical) {
                     inList = x > getPaddingLeft() && x < getWidth() - getPaddingRight();
                 } else {
                     inList = y > getPaddingTop() && y < getHeight() - getPaddingBottom();
                 }
 
                 if (child != null && !child.hasFocusable() && inList) {
                     if (mTouchMode != TOUCH_MODE_DOWN) {
@@ -1728,32 +1731,32 @@ public class TwoWayView extends AdapterV
                 final int viewportSize;
                 if (mIsVertical) {
                     viewportSize = getHeight() - getPaddingTop() - getPaddingBottom();
                 } else {
                     viewportSize = getWidth() - getPaddingLeft() - getPaddingRight();
                 }
 
                 // TODO: Use some form of smooth scroll instead
-                trackMotionScroll(viewportSize);
+                scrollListItemsBy(viewportSize);
                 return true;
             }
             return false;
 
         case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
             if (isEnabled() && mFirstPosition > 0) {
                 final int viewportSize;
                 if (mIsVertical) {
                     viewportSize = getHeight() - getPaddingTop() - getPaddingBottom();
                 } else {
                     viewportSize = getWidth() - getPaddingLeft() - getPaddingRight();
                 }
 
                 // TODO: Use some form of smooth scroll instead
-                trackMotionScroll(-viewportSize);
+                scrollListItemsBy(-viewportSize);
                 return true;
             }
             return false;
         }
 
         return false;
     }
 
@@ -2074,33 +2077,32 @@ public class TwoWayView extends AdapterV
         final View newFocus;
         final int searchPoint;
 
         if (selectedView != null && selectedView.hasFocus()) {
             View oldFocus = selectedView.findFocus();
             newFocus = FocusFinder.getInstance().findNextFocus(this, oldFocus, direction);
         } else {
             if (direction == View.FOCUS_DOWN || direction == View.FOCUS_RIGHT) {
-                final int start = (mIsVertical ? getPaddingTop() : getPaddingLeft());
+                final int start = getStartEdge();
 
                 final int selectedStart;
                 if (selectedView != null) {
                     selectedStart = (mIsVertical ? selectedView.getTop() : selectedView.getLeft());
                 } else {
                     selectedStart = start;
                 }
 
                 searchPoint = Math.max(selectedStart, start);
             } else {
-                final int end = (mIsVertical ? getHeight() - getPaddingBottom() :
-                    getWidth() - getPaddingRight());
+                final int end = getEndEdge();
 
                 final int selectedEnd;
                 if (selectedView != null) {
-                    selectedEnd = (mIsVertical ? selectedView.getBottom() : selectedView.getRight());
+                    selectedEnd = getChildEndEdge(selectedView);
                 } else {
                     selectedEnd = end;
                 }
 
                 searchPoint = Math.min(selectedEnd, end);
             }
 
             final int x = (mIsVertical ? 0 : searchPoint);
@@ -2239,17 +2241,17 @@ public class TwoWayView extends AdapterV
                 }
             }
 
             needToRedraw = true;
             checkSelectionChanged();
         }
 
         if (amountToScroll > 0) {
-            trackMotionScroll(direction == View.FOCUS_UP || direction == View.FOCUS_LEFT ?
+            scrollListItemsBy(direction == View.FOCUS_UP || direction == View.FOCUS_LEFT ?
                     amountToScroll : -amountToScroll);
             needToRedraw = true;
         }
 
         // If we didn't find a new focusable, make sure any existing focused
         // item that was panned off screen gives up focus.
         if (mItemsCanFocus && focusResult == null &&
                 selectedView != null && selectedView.hasFocus()) {
@@ -2301,97 +2303,90 @@ public class TwoWayView extends AdapterV
      *         needs to be taken into account when actually scrolling.
      */
     private int amountToScroll(int direction, int nextSelectedPosition) {
         forceValidFocusDirection(direction);
 
         final int numChildren = getChildCount();
 
         if (direction == View.FOCUS_DOWN || direction == View.FOCUS_RIGHT) {
-            final int end = (mIsVertical ? getHeight() - getPaddingBottom() :
-                getWidth() - getPaddingRight());
+            final int end = getEndEdge();
 
             int indexToMakeVisible = numChildren - 1;
             if (nextSelectedPosition != INVALID_POSITION) {
                 indexToMakeVisible = nextSelectedPosition - mFirstPosition;
             }
 
             final int positionToMakeVisible = mFirstPosition + indexToMakeVisible;
             final View viewToMakeVisible = getChildAt(indexToMakeVisible);
 
             int goalEnd = end;
             if (positionToMakeVisible < mItemCount - 1) {
                 goalEnd -= getArrowScrollPreviewLength();
             }
 
-            final int viewToMakeVisibleStart =
-                    (mIsVertical ? viewToMakeVisible.getTop() : viewToMakeVisible.getLeft());
-            final int viewToMakeVisibleEnd =
-                    (mIsVertical ? viewToMakeVisible.getBottom() : viewToMakeVisible.getRight());
+            final int viewToMakeVisibleStart = getChildStartEdge(viewToMakeVisible);
+            final int viewToMakeVisibleEnd = getChildEndEdge(viewToMakeVisible);
 
             if (viewToMakeVisibleEnd <= goalEnd) {
                 // Target item is fully visible
                 return 0;
             }
 
             if (nextSelectedPosition != INVALID_POSITION &&
                     (goalEnd - viewToMakeVisibleStart) >= getMaxScrollAmount()) {
                 // Item already has enough of it visible, changing selection is good enough
                 return 0;
             }
 
             int amountToScroll = (viewToMakeVisibleEnd - goalEnd);
 
             if (mFirstPosition + numChildren == mItemCount) {
-                final View lastChild = getChildAt(numChildren - 1);
-                final int lastChildEnd = (mIsVertical ? lastChild.getBottom() : lastChild.getRight());
+                final int lastChildEnd = getChildEndEdge(getChildAt(numChildren - 1));
 
                 // Last is last in list -> Make sure we don't scroll past it
                 final int max = lastChildEnd - end;
                 amountToScroll = Math.min(amountToScroll, max);
             }
 
             return Math.min(amountToScroll, getMaxScrollAmount());
         } else {
-            final int start = (mIsVertical ? getPaddingTop() : getPaddingLeft());
+            final int start = getStartEdge();
 
             int indexToMakeVisible = 0;
             if (nextSelectedPosition != INVALID_POSITION) {
                 indexToMakeVisible = nextSelectedPosition - mFirstPosition;
             }
 
             final int positionToMakeVisible = mFirstPosition + indexToMakeVisible;
             final View viewToMakeVisible = getChildAt(indexToMakeVisible);
 
             int goalStart = start;
             if (positionToMakeVisible > 0) {
                 goalStart += getArrowScrollPreviewLength();
             }
 
-            final int viewToMakeVisibleStart =
-                    (mIsVertical ? viewToMakeVisible.getTop() : viewToMakeVisible.getLeft());
-            final int viewToMakeVisibleEnd =
-                    (mIsVertical ? viewToMakeVisible.getBottom() : viewToMakeVisible.getRight());
+            final int viewToMakeVisibleStart = getChildStartEdge(viewToMakeVisible);
+            final int viewToMakeVisibleEnd = getChildEndEdge(viewToMakeVisible);
 
             if (viewToMakeVisibleStart >= goalStart) {
                 // Item is fully visible
                 return 0;
             }
 
             if (nextSelectedPosition != INVALID_POSITION &&
                     (viewToMakeVisibleEnd - goalStart) >= getMaxScrollAmount()) {
                 // Item already has enough of it visible, changing selection is good enough
                 return 0;
             }
 
             int amountToScroll = (goalStart - viewToMakeVisibleStart);
 
             if (mFirstPosition == 0) {
-                final View firstChild = getChildAt(0);
-                final int firstChildStart = (mIsVertical ? firstChild.getTop() : firstChild.getLeft());
+                final int firstChildStart = getChildStartEdge(getChildAt(0));
 
                 // First is first in list -> make sure we don't scroll past it
                 final int max = start - firstChildStart;
                 amountToScroll = Math.min(amountToScroll,  max);
             }
 
             return Math.min(amountToScroll, getMaxScrollAmount());
         }
@@ -2413,28 +2408,27 @@ public class TwoWayView extends AdapterV
         forceValidFocusDirection(direction);
 
         int amountToScroll = 0;
 
         newFocus.getDrawingRect(mTempRect);
         offsetDescendantRectToMyCoords(newFocus, mTempRect);
 
         if (direction == View.FOCUS_UP || direction == View.FOCUS_LEFT) {
-            final int start = (mIsVertical ? getPaddingTop() : getPaddingLeft());
+            final int start = getStartEdge();
             final int newFocusStart = (mIsVertical ? mTempRect.top : mTempRect.left);
 
             if (newFocusStart < start) {
                 amountToScroll = start - newFocusStart;
                 if (positionOfNewFocus > 0) {
                     amountToScroll += getArrowScrollPreviewLength();
                 }
             }
         } else {
-            final int end = (mIsVertical ? getHeight() - getPaddingBottom() :
-                getWidth() - getPaddingRight());
+            final int end = getEndEdge();
             final int newFocusEnd = (mIsVertical ? mTempRect.bottom : mTempRect.right);
 
             if (newFocusEnd > end) {
                 amountToScroll = newFocusEnd - end;
                 if (positionOfNewFocus < mItemCount - 1) {
                     amountToScroll += getArrowScrollPreviewLength();
                 }
             }
@@ -2449,19 +2443,18 @@ public class TwoWayView extends AdapterV
      *
      * @param descendant A descendant of this list.
      * @return The distance, or 0 if the nearest edge is already on screen.
      */
     private int distanceToView(View descendant) {
         descendant.getDrawingRect(mTempRect);
         offsetDescendantRectToMyCoords(descendant, mTempRect);
 
-        final int start = (mIsVertical ? getPaddingTop() : getPaddingLeft());
-        final int end = (mIsVertical ? getHeight() - getPaddingBottom() :
-            getWidth() - getPaddingRight());
+        final int start = getStartEdge();
+        final int end = getEndEdge();
 
         final int viewStart = (mIsVertical ? mTempRect.top : mTempRect.left);
         final int viewEnd = (mIsVertical ? mTempRect.bottom : mTempRect.right);
 
         int distance = 0;
         if (viewEnd < start) {
             distance = start - viewEnd;
         } else if (viewStart > end) {
@@ -2738,17 +2731,17 @@ public class TwoWayView extends AdapterV
         }
 
         int motionViewPrevStart = 0;
         View motionView = this.getChildAt(motionIndex);
         if (motionView != null) {
             motionViewPrevStart = (mIsVertical ? motionView.getTop() : motionView.getLeft());
         }
 
-        boolean atEdge = trackMotionScroll(delta);
+        boolean atEdge = scrollListItemsBy(delta);
 
         motionView = this.getChildAt(motionIndex);
         if (motionView != null) {
             final int motionViewRealStart =
                     (mIsVertical ? motionView.getTop() : motionView.getLeft());
 
             if (atEdge) {
                 final int overscroll = -delta - (motionViewRealStart - motionViewPrevStart);
@@ -2818,17 +2811,17 @@ public class TwoWayView extends AdapterV
         }
 
         if (delta != 0) {
             if (mOverScroll != 0) {
                 mOverScroll = 0;
                 ViewCompat.postInvalidateOnAnimation(this);
             }
 
-            trackMotionScroll(delta);
+            scrollListItemsBy(delta);
             mTouchMode = TOUCH_MODE_DRAGGING;
 
             // We did not scroll the full amount. Treat this essentially like the
             // start of a new touch scroll
             mMotionPosition = findClosestMotionRowOrColumn((int) mLastTouchPos);
             mTouchRemainderPos = 0;
         }
     }
@@ -2898,20 +2891,18 @@ public class TwoWayView extends AdapterV
 
     private int findMotionRowOrColumn(int motionPos) {
         int childCount = getChildCount();
         if (childCount == 0) {
             return INVALID_POSITION;
         }
 
         for (int i = 0; i < childCount; i++) {
-            View v = getChildAt(i);
-
-            if ((mIsVertical && motionPos <= v.getBottom()) ||
-                    (!mIsVertical && motionPos <= v.getRight())) {
+            final View v = getChildAt(i);
+            if (motionPos <= getChildEndEdge(v)) {
                 return mFirstPosition + i;
             }
         }
 
         return INVALID_POSITION;
     }
 
     private int findClosestMotionRowOrColumn(int motionPos) {
@@ -2932,41 +2923,51 @@ public class TwoWayView extends AdapterV
     private int getScaledOverscrollDistance(ViewConfiguration vc) {
         if (Build.VERSION.SDK_INT < 9) {
             return 0;
         }
 
         return vc.getScaledOverscrollDistance();
     }
 
+    private int getStartEdge() {
+        return (mIsVertical ? getPaddingTop() : getPaddingLeft());
+    }
+
+    private int getEndEdge() {
+        if (mIsVertical) {
+            return (getHeight() - getPaddingBottom());
+        } else {
+            return (getWidth() - getPaddingRight());
+        }
+    }
+
+    private int getChildStartEdge(View child) {
+        return (mIsVertical ? child.getTop() : child.getLeft());
+    }
+
+    private int getChildEndEdge(View child) {
+        return (mIsVertical ? child.getBottom() : child.getRight());
+    }
+
     private boolean contentFits() {
         final int childCount = getChildCount();
         if (childCount == 0) {
             return true;
         }
 
         if (childCount != mItemCount) {
             return false;
         }
 
         View first = getChildAt(0);
         View last = getChildAt(childCount - 1);
 
-        if (mIsVertical) {
-            return first.getTop() >= getPaddingTop() &&
-                    last.getBottom() <= getHeight() - getPaddingBottom();
-        } else {
-            return first.getLeft() >= getPaddingLeft() &&
-                    last.getRight() <= getWidth() - getPaddingRight();
-        }
-    }
-
-    private void updateScrollbarsDirection() {
-        setHorizontalScrollBarEnabled(!mIsVertical);
-        setVerticalScrollBarEnabled(mIsVertical);
+        return (getChildStartEdge(first) >= getStartEdge() &&
+                getChildEndEdge(last) <= getEndEdge());
     }
 
     private void triggerCheckForTap() {
         if (mPendingCheckForTap == null) {
             mPendingCheckForTap = new CheckForTap();
         }
 
         postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
@@ -2994,38 +2995,34 @@ public class TwoWayView extends AdapterV
     private void cancelCheckForLongPress() {
         if (mPendingCheckForLongPress == null) {
             return;
         }
 
         removeCallbacks(mPendingCheckForLongPress);
     }
 
-    boolean trackMotionScroll(int incrementalDelta) {
+    private boolean scrollListItemsBy(int incrementalDelta) {
         final int childCount = getChildCount();
         if (childCount == 0) {
             return true;
         }
 
-        final View first = getChildAt(0);
-        final int firstStart = (mIsVertical ? first.getTop() : first.getLeft());
-
-        final View last = getChildAt(childCount - 1);
-        final int lastEnd = (mIsVertical ? last.getBottom() : last.getRight());
+        final int firstStart = getChildStartEdge(getChildAt(0));
+        final int lastEnd = getChildEndEdge(getChildAt(childCount - 1));
 
         final int paddingTop = getPaddingTop();
         final int paddingBottom = getPaddingBottom();
         final int paddingLeft = getPaddingLeft();
         final int paddingRight = getPaddingRight();
 
         final int paddingStart = (mIsVertical ? paddingTop : paddingLeft);
 
         final int spaceBefore = paddingStart - firstStart;
-        final int end = (mIsVertical ? getHeight() - paddingBottom :
-            getWidth() - paddingRight);
+        final int end = getEndEdge();
         final int spaceAfter = lastEnd - end;
 
         final int size;
         if (mIsVertical) {
             size = getHeight() - paddingBottom - paddingTop;
         } else {
             size = getWidth() - paddingRight - paddingLeft;
         }
@@ -3056,31 +3053,31 @@ public class TwoWayView extends AdapterV
         int count = 0;
 
         final boolean down = (incrementalDelta < 0);
         if (down) {
             int childrenStart = -incrementalDelta + paddingStart;
 
             for (int i = 0; i < childCount; i++) {
                 final View child = getChildAt(i);
-                final int childEnd = (mIsVertical ? child.getBottom() : child.getRight());
+                final int childEnd = getChildEndEdge(child);
 
                 if (childEnd >= childrenStart) {
                     break;
                 }
 
                 count++;
                 mRecycler.addScrapView(child, firstPosition + i);
             }
         } else {
             int childrenEnd = end - incrementalDelta;
 
             for (int i = childCount - 1; i >= 0; i--) {
                 final View child = getChildAt(i);
-                final int childStart = (mIsVertical ? child.getTop() : child.getLeft());
+                final int childStart = getChildStartEdge(child);
 
                 if (childStart <= childrenEnd) {
                     break;
                 }
 
                 start = i;
                 count++;
                 mRecycler.addScrapView(child, firstPosition + i);
@@ -3137,21 +3134,17 @@ public class TwoWayView extends AdapterV
             return mScroller.getCurrVelocity();
         }
 
         return 0;
     }
 
     @TargetApi(5)
     private boolean awakenScrollbarsInternal() {
-        if (Build.VERSION.SDK_INT >= 5) {
-            return super.awakenScrollBars();
-        } else {
-            return false;
-        }
+        return (Build.VERSION.SDK_INT >= 5) && super.awakenScrollBars();
     }
 
     @Override
     public void computeScroll() {
         if (!mScroller.computeScrollOffset()) {
             return;
         }
 
@@ -3160,17 +3153,17 @@ public class TwoWayView extends AdapterV
             pos = mScroller.getCurrY();
         } else {
             pos = mScroller.getCurrX();
         }
 
         final int diff = (int) (pos - mLastTouchPos);
         mLastTouchPos = pos;
 
-        final boolean stopped = trackMotionScroll(diff);
+        final boolean stopped = scrollListItemsBy(diff);
 
         if (!stopped && !mScroller.isFinished()) {
             ViewCompat.postInvalidateOnAnimation(this);
         } else {
             if (stopped) {
                 final int overScrollMode = ViewCompat.getOverScrollMode(this);
                 if (overScrollMode != ViewCompat.OVER_SCROLL_NEVER) {
                     final EdgeEffectCompat edge =
@@ -3207,34 +3200,34 @@ public class TwoWayView extends AdapterV
             return false;
         }
 
         if (mIsVertical) {
             return mStartEdge.draw(canvas);
         }
 
         final int restoreCount = canvas.save();
-        final int height = getHeight() - getPaddingTop() - getPaddingBottom();
+        final int height = getHeight();
 
         canvas.translate(0, height);
         canvas.rotate(270);
 
         final boolean needsInvalidate = mStartEdge.draw(canvas);
         canvas.restoreToCount(restoreCount);
         return needsInvalidate;
     }
 
     private boolean drawEndEdge(Canvas canvas) {
         if (mEndEdge.isFinished()) {
             return false;
         }
 
         final int restoreCount = canvas.save();
-        final int width = getWidth() - getPaddingLeft() - getPaddingRight();
-        final int height = getHeight() - getPaddingTop() - getPaddingBottom();
+        final int width = getWidth();
+        final int height = getHeight();
 
         if (mIsVertical) {
             canvas.translate(-width, height);
             canvas.rotate(180, width, 0);
         } else {
             canvas.translate(width, 0);
             canvas.rotate(90);
         }
@@ -3680,16 +3673,20 @@ public class TwoWayView extends AdapterV
                 mSyncPosition = position;
                 mSyncRowId = mAdapter.getItemId(position);
             }
 
             requestLayout();
         }
     }
 
+    public void scrollBy(int offset) {
+        scrollListItemsBy(offset);
+    }
+
     @Override
     public boolean dispatchKeyEvent(KeyEvent event) {
         // Dispatch in the normal way
         boolean handled = super.dispatchKeyEvent(event);
         if (!handled) {
             // If we didn't handle it...
             final View focused = getFocusedChild();
             if (focused != null && event.getAction() == KeyEvent.ACTION_DOWN) {
@@ -3811,19 +3808,18 @@ public class TwoWayView extends AdapterV
         try {
             invalidate();
 
             if (mAdapter == null) {
                 resetState();
                 return;
             }
 
-            final int start = (mIsVertical ? getPaddingTop() : getPaddingLeft());
-            final int end =
-                    (mIsVertical ? getHeight() - getPaddingBottom() : getWidth() - getPaddingRight());
+            final int start = getStartEdge();
+            final int end = getEndEdge();
 
             int childCount = getChildCount();
             int index = 0;
             int delta = 0;
 
             View focusLayoutRestoreView = null;
 
             View selected = null;
@@ -4081,18 +4077,18 @@ public class TwoWayView extends AdapterV
             }
         }
     }
 
     private View moveSelection(View oldSelected, View newSelected, int delta, int start,
             int end) {
         final int selectedPosition = mSelectedPosition;
 
-        final int oldSelectedStart = (mIsVertical ? oldSelected.getTop() : oldSelected.getLeft());
-        final int oldSelectedEnd = (mIsVertical ? oldSelected.getBottom() : oldSelected.getRight());
+        final int oldSelectedStart = getChildStartEdge(oldSelected);
+        final int oldSelectedEnd = getChildEndEdge(oldSelected);
 
         View selected = null;
 
         if (delta > 0) {
             /*
              * Case 1: Scrolling down.
              */
 
@@ -4115,18 +4111,18 @@ public class TwoWayView extends AdapterV
             // Put oldSelected (A) where it belongs
             oldSelected = makeAndAddView(selectedPosition - 1, oldSelectedStart, true, false);
 
             final int itemMargin = mItemMargin;
 
             // Now put the new selection (B) below that
             selected = makeAndAddView(selectedPosition, oldSelectedEnd + itemMargin, true, true);
 
-            final int selectedStart = (mIsVertical ? selected.getTop() : selected.getLeft());
-            final int selectedEnd = (mIsVertical ? selected.getBottom() : selected.getRight());
+            final int selectedStart = getChildStartEdge(selected);
+            final int selectedEnd = getChildEndEdge(selected);
 
             // Some of the newly selected item extends below the bottom of the list
             if (selectedEnd > end) {
                 // Find space available above the selection into which we can scroll upwards
                 final int spaceBefore = selectedStart - start;
 
                 // Find space required to bring the bottom of the selected item fully into view
                 final int spaceAfter = selectedEnd - end;
@@ -4175,18 +4171,18 @@ public class TwoWayView extends AdapterV
                 final int newSelectedStart = (mIsVertical ? newSelected.getTop() : newSelected.getLeft());
                 selected = makeAndAddView(selectedPosition, newSelectedStart, true, true);
             } else {
                 // If (A) was not on screen and so did not have a view, position
                 // it above the oldSelected (B)
                 selected = makeAndAddView(selectedPosition, oldSelectedStart, false, true);
             }
 
-            final int selectedStart = (mIsVertical ? selected.getTop() : selected.getLeft());
-            final int selectedEnd = (mIsVertical ? selected.getBottom() : selected.getRight());
+            final int selectedStart = getChildStartEdge(selected);
+            final int selectedEnd = getChildEndEdge(selected);
 
             // Some of the newly selected item extends above the top of the list
             if (selectedStart < start) {
                 // Find space required to bring the top of the selected item fully into view
                 final int spaceBefore = start - selectedStart;
 
                // Find space available below the selection into which we can scroll downwards
                 final int spaceAfter = end - selectedEnd;
@@ -4207,18 +4203,18 @@ public class TwoWayView extends AdapterV
             fillBeforeAndAfter(selected, selectedPosition);
         } else {
             /*
              * Case 3: Staying still
              */
 
             selected = makeAndAddView(selectedPosition, oldSelectedStart, true, true);
 
-            final int selectedStart = (mIsVertical ? selected.getTop() : selected.getLeft());
-            final int selectedEnd = (mIsVertical ? selected.getBottom() : selected.getRight());
+            final int selectedStart = getChildStartEdge(selected);
+            final int selectedEnd = getChildEndEdge(selected);
 
             // We're staying still...
             if (oldSelectedStart < start) {
                 // ... but the top of the old selection was off screen.
                 // (This can happen if the data changes size out from under us)
                 int newEnd = selectedEnd;
                 if (newEnd < start + 20) {
                     // Not enough visible -- bring it onscreen
@@ -4269,17 +4265,17 @@ public class TwoWayView extends AdapterV
                 }
             } else {
                 mCheckStates.put(lastPos, true);
             }
         }
     }
 
     private void handleDataChanged() {
-        if (mChoiceMode.compareTo(ChoiceMode.NONE) != 0 && mAdapter != null && mAdapter.hasStableIds()) {
+        if (mChoiceMode != ChoiceMode.NONE && mAdapter != null && mAdapter.hasStableIds()) {
             confirmCheckedPositionsById();
         }
 
         mRecycler.clearTransientStateViews();
 
         final int itemCount = mItemCount;
         if (itemCount > 0) {
             int newPos;
@@ -4403,19 +4399,18 @@ public class TwoWayView extends AdapterV
         final int childCount = getChildCount();
         if (childCount <= 0) {
             return false;
         }
 
         int selectedStart = 0;
         int selectedPosition;
 
-        final int start = (mIsVertical ? getPaddingTop() : getPaddingLeft());
-        final int end =
-                (mIsVertical ? getHeight() - getPaddingBottom() : getWidth() - getPaddingRight());
+        final int start = getStartEdge();
+        final int end = getEndEdge();
 
         final int firstPosition = mFirstPosition;
         final int toPosition = mResurrectToPosition;
         boolean down = true;
 
         if (toPosition >= firstPosition && toPosition < firstPosition + childCount) {
             selectedPosition = toPosition;
 
@@ -4442,18 +4437,18 @@ public class TwoWayView extends AdapterV
                 }
             }
         } else {
             selectedPosition = firstPosition + childCount - 1;
             down = false;
 
             for (int i = childCount - 1; i >= 0; i--) {
                 final View child = getChildAt(i);
-                final int childStart = (mIsVertical ? child.getTop() : child.getLeft());
-                final int childEnd = (mIsVertical ? child.getBottom() : child.getRight());
+                final int childStart = getChildStartEdge(child);
+                final int childEnd = getChildEndEdge(child);
 
                 if (i == childCount - 1) {
                     selectedStart = childStart;
                 }
 
                 if (childEnd <= end) {
                     selectedPosition = firstPosition + i;
                     selectedStart = childStart;
@@ -4715,17 +4710,17 @@ public class TwoWayView extends AdapterV
                 returnedWidth += itemMargin;
             }
 
             // Recycle the view before we possibly return from the method
             if (shouldRecycle) {
                 recycleBin.addScrapView(child, -1);
             }
 
-            returnedWidth += child.getMeasuredHeight();
+            returnedWidth += child.getMeasuredWidth();
 
             if (returnedWidth >= maxWidth) {
                 // We went over, figure out which width to return.  If returnedWidth > maxWidth,
                 // then the i'th position did not fit completely.
                 return (disallowPartialChildPosition >= 0) // Disallowing is enabled (> -1)
                             && (i > disallowPartialChildPosition) // We've past the min pos
                             && (prevWidthWithoutPartialChild > 0) // We have a prev width
                             && (returnedWidth != maxWidth) // i'th child did not fit completely
@@ -4807,21 +4802,20 @@ public class TwoWayView extends AdapterV
         if (updateChildSelected) {
             child.setSelected(isSelected);
         }
 
         if (updateChildPressed) {
             child.setPressed(isPressed);
         }
 
-        if (mChoiceMode.compareTo(ChoiceMode.NONE) != 0 && mCheckStates != null) {
+        if (mChoiceMode != ChoiceMode.NONE && mCheckStates != null) {
             if (child instanceof Checkable) {
                 ((Checkable) child).setChecked(mCheckStates.get(position));
-            } else if (getContext().getApplicationInfo().targetSdkVersion
-                    >= Build.VERSION_CODES.HONEYCOMB) {
+            } else if (Build.VERSION.SDK_INT >= HONEYCOMB) {
                 child.setActivated(mCheckStates.get(position));
             }
         }
 
         if (needToMeasure) {
             measureChild(child, lp);
         } else {
             cleanupLayoutState(child);
@@ -4844,49 +4838,40 @@ public class TwoWayView extends AdapterV
         }
     }
 
     void fillGap(boolean down) {
         final int childCount = getChildCount();
 
         if (down) {
             final int paddingStart = (mIsVertical ? getPaddingTop() : getPaddingLeft());
-
-            final int lastEnd;
-            if (mIsVertical) {
-                lastEnd = getChildAt(childCount - 1).getBottom();
-            } else {
-                lastEnd = getChildAt(childCount - 1).getRight();
-            }
+            final int lastEnd = getChildEndEdge(getChildAt(childCount - 1));
 
             final int offset = (childCount > 0 ? lastEnd + mItemMargin : paddingStart);
             fillAfter(mFirstPosition + childCount, offset);
             correctTooHigh(getChildCount());
         } else {
-            final int end;
+            final int end = getEndEdge();
             final int firstStart;
-
             if (mIsVertical) {
-                end = getHeight() - getPaddingBottom();
                 firstStart = getChildAt(0).getTop();
             } else {
-                end = getWidth() - getPaddingRight();
                 firstStart = getChildAt(0).getLeft();
             }
 
             final int offset = (childCount > 0 ? firstStart - mItemMargin : end);
             fillBefore(mFirstPosition - 1, offset);
             correctTooLow(getChildCount());
         }
     }
 
     private View fillBefore(int pos, int nextOffset) {
         View selectedView = null;
 
-        final int start = (mIsVertical ? getPaddingTop() : getPaddingLeft());
+        final int start = getStartEdge();
 
         while (nextOffset > start && pos >= 0) {
             boolean isSelected = (pos == mSelectedPosition);
             View child = makeAndAddView(pos, nextOffset, false, isSelected);
 
             if (mIsVertical) {
                 nextOffset = child.getTop() - mItemMargin;
             } else {
@@ -4903,29 +4888,23 @@ public class TwoWayView extends AdapterV
         mFirstPosition = pos + 1;
 
         return selectedView;
     }
 
     private View fillAfter(int pos, int nextOffset) {
         View selectedView = null;
 
-        final int end =
-                (mIsVertical ? getHeight() - getPaddingBottom() : getWidth() - getPaddingRight());
+        final int end = getEndEdge();
 
         while (nextOffset < end && pos < mItemCount) {
             boolean selected = (pos == mSelectedPosition);
 
             View child = makeAndAddView(pos, nextOffset, true, selected);
-
-            if (mIsVertical) {
-                nextOffset = child.getBottom() + mItemMargin;
-            } else {
-                nextOffset = child.getRight() + mItemMargin;
-            }
+            nextOffset = getChildEndEdge(child) + mItemMargin;
 
             if (selected) {
                 selectedView = child;
             }
 
             pos++;
         }
 
@@ -4934,35 +4913,23 @@ public class TwoWayView extends AdapterV
 
     private View fillSpecific(int position, int offset) {
         final boolean tempIsSelected = (position == mSelectedPosition);
         View temp = makeAndAddView(position, offset, true, tempIsSelected);
 
         // Possibly changed again in fillBefore if we add rows above this one.
         mFirstPosition = position;
 
-        final int itemMargin = mItemMargin;
-
-        final int offsetBefore;
-        if (mIsVertical) {
-            offsetBefore = temp.getTop() - itemMargin;
-        } else {
-            offsetBefore = temp.getLeft() - itemMargin;
-        }
+        final int offsetBefore = getChildStartEdge(temp) + mItemMargin;
         final View before = fillBefore(position - 1, offsetBefore);
 
         // This will correct for the top of the first view not touching the top of the list
         adjustViewsStartOrEnd();
 
-        final int offsetAfter;
-        if (mIsVertical) {
-            offsetAfter = temp.getBottom() + itemMargin;
-        } else {
-            offsetAfter = temp.getRight() + itemMargin;
-        }
+        final int offsetAfter = getChildEndEdge(temp) + mItemMargin;
         final View after = fillAfter(position + 1, offsetAfter);
 
         final int childCount = getChildCount();
         if (childCount > 0) {
             correctTooHigh(childCount);
         }
 
         if (tempIsSelected) {
@@ -5006,47 +4973,32 @@ public class TwoWayView extends AdapterV
 
         fillBeforeAndAfter(selected, position);
         correctTooHigh(getChildCount());
 
         return selected;
     }
 
     private void fillBeforeAndAfter(View selected, int position) {
-        final int itemMargin = mItemMargin;
-
-        final int offsetBefore;
-        if (mIsVertical) {
-            offsetBefore = selected.getTop() - itemMargin;
-        } else {
-            offsetBefore = selected.getLeft() - itemMargin;
-        }
-
+        final int offsetBefore = getChildStartEdge(selected) + mItemMargin;
         fillBefore(position - 1, offsetBefore);
 
         adjustViewsStartOrEnd();
 
-        final int offsetAfter;
-        if (mIsVertical) {
-            offsetAfter = selected.getBottom() + itemMargin;
-        } else {
-            offsetAfter = selected.getRight() + itemMargin;
-        }
-
+        final int offsetAfter = getChildEndEdge(selected) + mItemMargin;
         fillAfter(position + 1, offsetAfter);
     }
 
     private View fillFromSelection(int selectedTop, int start, int end) {
         final int selectedPosition = mSelectedPosition;
-        View selected;
-
-        selected = makeAndAddView(selectedPosition, selectedTop, true, true);
-
-        final int selectedStart = (mIsVertical ? selected.getTop() : selected.getLeft());
-        final int selectedEnd = (mIsVertical ? selected.getBottom() : selected.getRight());
+
+        View selected = makeAndAddView(selectedPosition, selectedTop, true, true);
+
+        final int selectedStart = getChildStartEdge(selected);
+        final int selectedEnd = getChildEndEdge(selected);
 
         // Some of the newly selected item extends below the bottom of the list
         if (selectedEnd > end) {
             // Find space available above the selection into which we can scroll
             // upwards
             final int spaceAbove = selectedStart - start;
 
             // Find space required to bring the bottom of the selected item
@@ -5082,52 +5034,43 @@ public class TwoWayView extends AdapterV
     private void correctTooHigh(int childCount) {
         // First see if the last item is visible. If it is not, it is OK for the
         // top of the list to be pushed up.
         final int lastPosition = mFirstPosition + childCount - 1;
         if (lastPosition != mItemCount - 1 || childCount == 0) {
             return;
         }
 
-        // Get the last child ...
-        final View lastChild = getChildAt(childCount - 1);
-
-        // ... and its end edge
-        final int lastEnd;
-        if (mIsVertical) {
-            lastEnd = lastChild.getBottom();
-        } else {
-            lastEnd = lastChild.getRight();
-        }
+        // Get the last child end edge
+        final int lastEnd = getChildEndEdge(getChildAt(childCount - 1));
 
         // This is bottom of our drawable area
-        final int start = (mIsVertical ? getPaddingTop() : getPaddingLeft());
-        final int end =
-                (mIsVertical ? getHeight() - getPaddingBottom() : getWidth() - getPaddingRight());
+        final int start = getStartEdge();
+        final int end = getEndEdge();
 
         // This is how far the end edge of the last view is from the end of the
         // drawable area
         int endOffset = end - lastEnd;
 
         View firstChild = getChildAt(0);
-        int firstStart = (mIsVertical ? firstChild.getTop() : firstChild.getLeft());
+        int firstStart = getChildStartEdge(firstChild);
 
         // Make sure we are 1) Too high, and 2) Either there are more rows above the
         // first row or the first row is scrolled off the top of the drawable area
         if (endOffset > 0 && (mFirstPosition > 0 || firstStart < start))  {
             if (mFirstPosition == 0) {
                 // Don't pull the top too far down
                 endOffset = Math.min(endOffset, start - firstStart);
             }
 
             // Move everything down
             offsetChildren(endOffset);
 
             if (mFirstPosition > 0) {
-                firstStart = (mIsVertical ? firstChild.getTop() : firstChild.getLeft());
+                firstStart = getChildStartEdge(firstChild);
 
                 // Fill the gap that was opened above mFirstPosition with more rows, if
                 // possible
                 fillBefore(mFirstPosition - 1, firstStart - mItemMargin);
 
                 // Close up the remaining gap
                 adjustViewsStartOrEnd();
             }
@@ -5139,31 +5082,25 @@ public class TwoWayView extends AdapterV
         // bottom of the list to be pushed down.
         if (mFirstPosition != 0 || childCount == 0) {
             return;
         }
 
         final View first = getChildAt(0);
         final int firstStart = (mIsVertical ? first.getTop() : first.getLeft());
 
-        final int start = (mIsVertical ? getPaddingTop() : getPaddingLeft());
-
-        final int end;
-        if (mIsVertical) {
-            end = getHeight() - getPaddingBottom();
-        } else {
-            end = getWidth() - getPaddingRight();
-        }
+        final int start = getStartEdge();
+        final int end = getEndEdge();
 
         // This is how far the start edge of the first view is from the start of the
         // drawable area
         int startOffset = firstStart - start;
 
         View last = getChildAt(childCount - 1);
-        int lastEnd = (mIsVertical ? last.getBottom() : last.getRight());
+        int lastEnd = getChildEndEdge(last);
 
         int lastPosition = mFirstPosition + childCount - 1;
 
         // Make sure we are 1) Too low, and 2) Either there are more columns/rows below the
         // last column/row or the last column/row is scrolled off the end of the
         // drawable area
         if (startOffset > 0) {
             if (lastPosition < mItemCount - 1 || lastEnd > end)  {
@@ -5171,17 +5108,17 @@ public class TwoWayView extends AdapterV
                     // Don't pull the bottom too far up
                     startOffset = Math.min(startOffset, lastEnd - end);
                 }
 
                 // Move everything up
                 offsetChildren(-startOffset);
 
                 if (lastPosition < mItemCount - 1) {
-                    lastEnd = (mIsVertical ? last.getBottom() : last.getRight());
+                    lastEnd = getChildEndEdge(last);
 
                     // Fill the gap that was opened below the last position with more rows, if
                     // possible
                     fillAfter(lastPosition + 1, lastEnd + mItemMargin);
 
                     // Close up the remaining gap
                     adjustViewsStartOrEnd();
                 }
@@ -5363,16 +5300,18 @@ public class TwoWayView extends AdapterV
         }
 
         ViewCompat.setAccessibilityDelegate(child, mAccessibilityDelegate);
 
         return child;
     }
 
     void resetState() {
+        mScroller.forceFinished(true);
+
         removeAllViewsInLayout();
 
         mSelectedStart = 0;
         mFirstPosition = 0;
         mDataChanged = false;
         mNeedSync = false;
         mPendingSync = null;
         mOldSelectedPosition = INVALID_POSITION;
@@ -5416,52 +5355,49 @@ public class TwoWayView extends AdapterV
                 mSyncRowId = adapter.getItemId(mFirstPosition);
             } else {
                 mSyncRowId = NO_ID;
             }
 
             mSyncPosition = mFirstPosition;
 
             if (child != null) {
-                mSpecificStart = child.getTop();
+                mSpecificStart = (mIsVertical ? child.getTop() : child.getLeft());
             }
 
             mSyncMode = SYNC_FIRST_POSITION;
         }
     }
 
     private ContextMenuInfo createContextMenuInfo(View view, int position, long id) {
         return new AdapterContextMenuInfo(view, position, id);
     }
 
     @TargetApi(11)
     private void updateOnScreenCheckedViews() {
         final int firstPos = mFirstPosition;
         final int count = getChildCount();
 
-        final boolean useActivated = getContext().getApplicationInfo().targetSdkVersion
-                >= Build.VERSION_CODES.HONEYCOMB;
-
         for (int i = 0; i < count; i++) {
             final View child = getChildAt(i);
             final int position = firstPos + i;
 
             if (child instanceof Checkable) {
                 ((Checkable) child).setChecked(mCheckStates.get(position));
-            } else if (useActivated) {
+            } else if (Build.VERSION.SDK_INT >= HONEYCOMB) {
                 child.setActivated(mCheckStates.get(position));
             }
         }
     }
 
     @Override
     public boolean performItemClick(View view, int position, long id) {
         boolean checkedStateChanged = false;
 
-        if (mChoiceMode.compareTo(ChoiceMode.MULTIPLE) == 0) {
+        if (mChoiceMode == ChoiceMode.MULTIPLE) {
             boolean checked = !mCheckStates.get(position, false);
             mCheckStates.put(position, checked);
 
             if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
                 if (checked) {
                     mCheckedIdStates.put(mAdapter.getItemId(position), position);
                 } else {
                     mCheckedIdStates.delete(mAdapter.getItemId(position));
@@ -5470,17 +5406,17 @@ public class TwoWayView extends AdapterV
 
             if (checked) {
                 mCheckedItemCount++;
             } else {
                 mCheckedItemCount--;
             }
 
             checkedStateChanged = true;
-        } else if (mChoiceMode.compareTo(ChoiceMode.SINGLE) == 0) {
+        } else if (mChoiceMode == ChoiceMode.SINGLE) {
             boolean checked = !mCheckStates.get(position, false);
             if (checked) {
                 mCheckStates.clear();
                 mCheckStates.put(position, true);
 
                 if (mCheckedIdStates != null && mAdapter.hasStableIds()) {
                     mCheckedIdStates.clear();
                     mCheckedIdStates.put(mAdapter.getItemId(position), position);
@@ -5732,17 +5668,17 @@ public class TwoWayView extends AdapterV
                 this.height = WRAP_CONTENT;
             }
         }
 
         public LayoutParams(ViewGroup.LayoutParams other) {
             super(other);
 
             if (this.width == MATCH_PARENT) {
-                Log.w(LOGTAG, "Constructing LayoutParams with height MATCH_PARENT - " +
+                Log.w(LOGTAG, "Constructing LayoutParams with width MATCH_PARENT - " +
                         "does not make much sense as the view might change orientation. " +
                         "Falling back to WRAP_CONTENT");
                 this.width = WRAP_CONTENT;
             }
 
             if (this.height == MATCH_PARENT) {
                 Log.w(LOGTAG, "Constructing LayoutParams with height MATCH_PARENT - " +
                         "does not make much sense as the view might change orientation. " +
@@ -5783,21 +5719,18 @@ public class TwoWayView extends AdapterV
                 final int scrapCount = scrap.size();
 
                 for (int i = 0; i < scrapCount; i++) {
                     scrap.get(i).forceLayout();
                 }
             } else {
                 final int typeCount = mViewTypeCount;
                 for (int i = 0; i < typeCount; i++) {
-                    final ArrayList<View> scrap = mScrapViews[i];
-                    final int scrapCount = scrap.size();
-
-                    for (int j = 0; j < scrapCount; j++) {
-                        scrap.get(j).forceLayout();
+                    for (View scrap : mScrapViews[i]) {
+                        scrap.forceLayout();
                     }
                 }
             }
 
             if (mTransientStateViews != null) {
                 final int count = mTransientStateViews.size();
                 for (int i = 0; i < count; i++) {
                     mTransientStateViews.valueAt(i).forceLayout();
@@ -6117,17 +6050,17 @@ public class TwoWayView extends AdapterV
                 // view is visible
                 setVisibility(View.VISIBLE);
             }
 
             // We are now GONE, so pending layouts will not be dispatched.
             // Force one here to make sure that the state of the list matches
             // the state of the adapter.
             if (mDataChanged) {
-                onLayout(false, getLeft(), getTop(), getRight(), getBottom());
+                layout(getLeft(), getTop(), getRight(), getBottom());
             }
         } else {
             if (mEmptyView != null) {
                 mEmptyView.setVisibility(View.GONE);
             }
 
             setVisibility(View.VISIBLE);
         }
@@ -6522,24 +6455,18 @@ public class TwoWayView extends AdapterV
             case AccessibilityNodeInfoCompat.ACTION_SELECT:
                 if (getSelectedItemPosition() != position) {
                     setSelection(position);
                     return true;
                 }
                 return false;
 
             case AccessibilityNodeInfoCompat.ACTION_CLICK:
-                if (isClickable()) {
-                    return performItemClick(host, position, id);
-                }
-                return false;
+                return isClickable() && performItemClick(host, position, id);
 
             case AccessibilityNodeInfoCompat.ACTION_LONG_CLICK:
-                if (isLongClickable()) {
-                    return performLongPress(host, position, id);
-                }
-                return false;
+                return isLongClickable() && performLongPress(host, position, id);
             }
 
             return false;
         }
     }
 }