[
  {
    "path": ".gitignore",
    "content": "#Android generated\nbin\ngen\n\n#Eclipse\n.project\n.classpath\n.settings\n\n#IntelliJ IDEA\n.idea\n*.iml\n*.ipr\n*.iws\nout\n\n#Maven\ntarget\nrelease.properties\npom.xml.*\n\n#Ant\nbuild.xml\nlocal.properties\nproguard.cfg\n\n#OSX\n.DS_Store\n"
  },
  {
    "path": "AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"com.jess.ui\"\n    android:versionCode=\"1\"\n    android:versionName=\"1.0\">\n    <application\n        android:icon=\"@drawable/icon\"\n        android:label=\"@string/app_name\">\n        <activity\n            android:name=\"com.jess.demo.MainActivity\"\n            android:label=\"@string/app_name\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n    </application>\n    <uses-sdk\n        android:minSdkVersion=\"7\"\n        android:targetSdkVersion=\"17\"/>\n</manifest>"
  },
  {
    "path": "README.md",
    "content": "TwoWayGridView\n==============\n\nAn Android GridView that can be configured to scroll horizontally or vertically.\n\nI should have posted this over a year and a half ago, but never got around to it.  I needed a grid view that in portrait would scroll vertically, but in landscape, would scroll horizontally.  I thought I could try hacking up the Gallery, but that never works out well, and if GridView could really be configured to scroll any direction, it would just be so much easier.\n\nSo I built it one weekend.  Lots of left, right, top, bottom changes, but the end result is a really useful UI widget.\n\nFeel free to use it in your apps, according to the Apache 2.0 license.  Also feel free to fork it and improve it.  You could fairly easily create a horizontal listview by extending TwoWayAbsListView\n\nUsage\n-----\n\nThe TwoWayGridView can be used as a drop-in replacement for the normal Android GridView.  It just has a few more configurable attributes:\n\n* `scrollDirectionPortrait` (vertical | horizontal) The direction the grid will scroll when the device is in portrait orientation\n* `scrollDirectionLandscape` (vertical | horizontal) The direction the grid will scroll when the device is in landscape orientation\n* `numRows` (integer) Number of rows in grid view when in horizontal scrolling mode\n* `verticalSpacing` (dimension) Height of vertical spacing between grid rows\n* `rowHeight` (dimension) Height of each grid row\n\nHere is an example from the demo layout where it is configured to scroll vertically in portrait and horizontally in landscape :\n\n    <?xml version=\"1.0\" encoding=\"utf-8\"?>\n    <com.jess.ui.TwoWayGridView\n        xmlns:android=\"http://schemas.android.com/apk/res/android\" \n        xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n        android:background=\"#E8E8E8\"\n        android:id=\"@+id/gridview\"\n        android:layout_width=\"fill_parent\" \n        android:layout_height=\"fill_parent\"\n        app:cacheColorHint=\"#E8E8E8\"\n        app:columnWidth=\"80dp\"\n        app:rowHeight=\"80dp\"\n        app:numColumns=\"auto_fit\"\n        app:numRows=\"auto_fit\"\n        app:verticalSpacing=\"16dp\"\n        app:horizontalSpacing=\"16dp\"\n        app:stretchMode=\"spacingWidthUniform\"\n        app:scrollDirectionPortrait=\"vertical\"\n        app:scrollDirectionLandscape=\"horizontal\"\n        app:gravity=\"center\"/>"
  },
  {
    "path": "lib/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tpackage=\"com.jess.ui\"\n\tandroid:versionCode=\"1\"\n\tandroid:versionName=\"1.0\" >\n\n\t<application\n\t\tandroid:icon=\"@drawable/icon\"\n\t\tandroid:label=\"@string/app_name\" >\n\t</application>\n\n\t<uses-sdk\n\t\tandroid:minSdkVersion=\"3\" />\n\n</manifest>"
  },
  {
    "path": "lib/project.properties",
    "content": "# This file is automatically generated by Android Tools.\n# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n#\n# This file must be checked in Version Control Systems.\n#\n# To customize properties used by the Ant build system edit\n# \"ant.properties\", and override values to adapt the script to your\n# project structure.\n#\n# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):\n#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt\n\n# Indicates whether an apk should be generated for each density.\nsplit.density=false\n# Project target.\ntarget=android-4\nandroid.library=true\n"
  },
  {
    "path": "lib/res/layout/main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.jess.ui.TwoWayGridView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:id=\"@+id/gridview\"\n    android:layout_width=\"fill_parent\"\n    android:layout_height=\"fill_parent\"\n    android:background=\"#E8E8E8\"\n    app:cacheColorHint=\"#E8E8E8\"\n    app:columnWidth=\"370dp\"\n    app:gravity=\"center\"\n    app:horizontalSpacing=\"16dp\"\n    app:numColumns=\"auto_fit\"\n    app:numRows=\"auto_fit\"\n    app:rowHeight=\"144dp\"\n    app:scrollDirectionLandscape=\"horizontal\"\n    app:scrollDirectionPortrait=\"vertical\"\n    app:stretchMode=\"spacingWidthUniform\"\n    app:verticalSpacing=\"16dp\"/>\n"
  },
  {
    "path": "lib/res/values/attrs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<resources>\n    <!-- Specifies how to place the content of an object, both\n         on the x- and y-axis, within the object itself. -->\n    <attr name=\"gravity\">\n        <!-- Push object to the top of its container, not changing its size. -->\n        <flag name=\"top\" value=\"0x30\" />\n        <!-- Push object to the bottom of its container, not changing its size. -->\n        <flag name=\"bottom\" value=\"0x50\" />\n        <!-- Push object to the left of its container, not changing its size. -->\n        <flag name=\"left\" value=\"0x03\" />\n        <!-- Push object to the right of its container, not changing its size. -->\n        <flag name=\"right\" value=\"0x05\" />\n        <!-- Place object in the vertical center of its container, not changing its size. -->\n        <flag name=\"center_vertical\" value=\"0x10\" />\n        <!-- Grow the vertical size of the object if needed so it completely fills its container. -->\n        <flag name=\"fill_vertical\" value=\"0x70\" />\n        <!-- Place object in the horizontal center of its container, not changing its size. -->\n        <flag name=\"center_horizontal\" value=\"0x01\" />\n        <!-- Grow the horizontal size of the object if needed so it completely fills its container. -->\n        <flag name=\"fill_horizontal\" value=\"0x07\" />\n        <!-- Place the object in the center of its container in both the vertical and horizontal axis, not changing its size. -->\n        <flag name=\"center\" value=\"0x11\" />\n        <!-- Grow the horizontal and vertical size of the object if needed so it completely fills its container. -->\n        <flag name=\"fill\" value=\"0x77\" />\n        <!-- Additional option that can be set to have the top and/or bottom edges of\n             the child clipped to its container's bounds.\n             The clip will be based on the vertical gravity: a top gravity will clip the bottom\n             edge, a bottom gravity will clip the top edge, and neither will clip both edges. -->\n        <flag name=\"clip_vertical\" value=\"0x80\" />\n        <!-- Additional option that can be set to have the left and/or right edges of\n             the child clipped to its container's bounds.\n             The clip will be based on the horizontal gravity: a left gravity will clip the right\n             edge, a right gravity will clip the left edge, and neither will clip both edges. -->\n        <flag name=\"clip_horizontal\" value=\"0x08\" />\n    </attr>\n    <!-- Default GridView style. -->\n        <attr name=\"gridViewStyle\" format=\"reference\" />\n    <declare-styleable name=\"TwoWayAbsListView\">\n         <!-- Drawable used to indicate the currently selected item in the list. -->\n        <attr name=\"listSelector\" format=\"color|reference\" />\n        <!-- When set to true, the selector will be drawn over the selected item.\n             Otherwise the selector is drawn behind the selected item. The default\n             value is false. -->\n        <attr name=\"drawSelectorOnTop\" format=\"boolean\" />\n        <!-- Used by ListView and GridView to stack their content from the bottom. -->\n        <attr name=\"stackFromBottom\" format=\"boolean\" />\n        <!-- When set to true, the list uses a drawing cache during scrolling.\n             This makes the rendering faster but uses more memory. The default\n             value is true. -->\n        <attr name=\"scrollingCache\" format=\"boolean\" />\n        <!-- When set to true, the list will filter results as the user types. The\n             List's adapter must support the Filterable interface for this to work. -->\n        <!-- <attr name=\"textFilterEnabled\" format=\"boolean\" /> -->\n        <!-- Sets the transcript mode for the list. In transcript mode, the list\n             scrolls to the bottom to make new items visible when they are added. -->\n        <attr name=\"transcriptMode\">\n            <!-- Disables transcript mode. This is the default value. -->\n            <enum name=\"disabled\" value=\"0\"/>\n            <!-- The list will automatically scroll to the bottom when\n                 a data set change notification is received and only if the last item is\n                 already visible on screen. -->\n            <enum name=\"normal\" value=\"1\" />\n            <!-- The list will automatically scroll to the bottom, no matter what items\n                 are currently visible. -->\n            <enum name=\"alwaysScroll\" value=\"2\" />\n        </attr>\n        <!-- Indicates that this list will always be drawn on top of solid, single-color\n             opaque background. This allows the list to optimize drawing. -->\n        <attr name=\"cacheColorHint\" format=\"color\" />\n        <!-- Enables the fast scroll thumb that can be dragged to quickly scroll through\n             the list. -->\n        <!-- <attr name=\"fastScrollEnabled\" format=\"boolean\" /> -->\n        <!-- When set to true, the list will use a more refined calculation\n             method based on the pixels height of the items visible on screen. This\n             property is set to true by default but should be set to false if your adapter\n             will display items of varying heights. When this property is set to true and\n             your adapter displays items of varying heights, the scrollbar thumb will\n             change size as the user scrolls through the list. When set to fale, the list\n             will use only the number of items in the adapter and the number of items visible\n             on screen to determine the scrollbar's properties. -->\n        <attr name=\"smoothScrollbar\" format=\"boolean\" />\n\n        <!-- Use this attribute to control which direction the GridView scrolls when in\n             portrait orientation -->\n        <attr name=\"scrollDirectionPortrait\">\n            <!-- Scroll up vertically. This is the default value. -->\n            <enum name=\"vertical\" value=\"0\"/>\n            <!-- Scroll horizontally. -->\n            <enum name=\"horizontal\" value=\"1\" />\n        </attr>\n        <attr name=\"scrollDirectionLandscape\">\n            <!-- Scroll up vertically. This is the default value. -->\n            <enum name=\"vertical\" value=\"0\"/>\n            <!-- Scroll horizontally. -->\n            <enum name=\"horizontal\" value=\"1\" />\n        </attr>\n    </declare-styleable>\n    \n    <declare-styleable name=\"TwoWayGridView\">\n        <attr name=\"horizontalSpacing\" format=\"dimension\" />\n        <attr name=\"verticalSpacing\" format=\"dimension\" />\n        <attr name=\"stretchMode\">\n            <enum name=\"none\" value=\"0\"/>\n            <enum name=\"spacingWidth\" value=\"1\" />\n            <enum name=\"columnWidth\" value=\"2\" />\n            <enum name=\"spacingWidthUniform\" value=\"3\" />\n        </attr>\n        <attr name=\"columnWidth\" format=\"dimension\" />\n        <attr name=\"rowHeight\" format=\"dimension\" />\n        <attr name=\"numColumns\" format=\"integer\" min=\"0\">\n            <enum name=\"auto_fit\" value=\"-1\" />\n        </attr>\n        <attr name=\"numRows\" format=\"integer\" min=\"0\">\n            <enum name=\"auto_fit\" value=\"-1\" />\n        </attr>\n        <attr name=\"gravity\" />\n    </declare-styleable>\n    \n</resources>"
  },
  {
    "path": "lib/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">EverGrid</string>\n</resources>\n"
  },
  {
    "path": "lib/src/com/jess/ui/ScrollBarDrawable.java",
    "content": "package com.jess.ui;\n/*\n * A modified version of the Android ScrollBarDrawable\n *\n * Copyright 2012 Jess Anders\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport android.graphics.Canvas;\nimport android.graphics.ColorFilter;\nimport android.graphics.PixelFormat;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\n\n/**\n * This is only used by View for displaying its scroll bars.  It should probably\n * be moved in to the view package since it is used in that lower-level layer.\n * For now, we'll hide it so it can be cleaned up later.\n * {@hide}\n */\npublic class ScrollBarDrawable extends Drawable {\n    private Drawable mVerticalTrack;\n    private Drawable mHorizontalTrack;\n    private Drawable mVerticalThumb;\n    private Drawable mHorizontalThumb;\n    private int mRange;\n    private int mOffset;\n    private int mExtent;\n    private boolean mVertical;\n    private boolean mChanged;\n    private boolean mRangeChanged;\n    private final Rect mTempBounds = new Rect();\n    private boolean mAlwaysDrawHorizontalTrack;\n    private boolean mAlwaysDrawVerticalTrack;\n\n    public ScrollBarDrawable() {\n    }\n\n    /**\n     * Indicate whether the horizontal scrollbar track should always be drawn regardless of the\n     * extent. Defaults to false.\n     *\n     * @param alwaysDrawTrack Set to true if the track should always be drawn\n     */\n    public void setAlwaysDrawHorizontalTrack(boolean alwaysDrawTrack) {\n        mAlwaysDrawHorizontalTrack = alwaysDrawTrack;\n    }\n\n    /**\n     * Indicate whether the vertical scrollbar track should always be drawn regardless of the\n     * extent. Defaults to false.\n     *\n     * @param alwaysDrawTrack Set to true if the track should always be drawn\n     */\n    public void setAlwaysDrawVerticalTrack(boolean alwaysDrawTrack) {\n        mAlwaysDrawVerticalTrack = alwaysDrawTrack;\n    }\n\n    /**\n     * Indicates whether the vertical scrollbar track should always be drawn regardless of the\n     * extent.\n     */\n    public boolean getAlwaysDrawVerticalTrack() {\n        return mAlwaysDrawVerticalTrack;\n    }\n\n    /**\n     * Indicates whether the horizontal scrollbar track should always be drawn regardless of the\n     * extent.\n     */\n    public boolean getAlwaysDrawHorizontalTrack() {\n        return mAlwaysDrawHorizontalTrack;\n    }\n\n    public void setParameters(int range, int offset, int extent, boolean vertical) {\n        if (mVertical != vertical) {\n            mChanged = true;\n        }\n\n        if (mRange != range || mOffset != offset || mExtent != extent) {\n            mRangeChanged = true;\n        }\n\n        mRange = range;\n        mOffset = offset;\n        mExtent = extent;\n        mVertical = vertical;\n    }\n\n    @Override\n    public void draw(Canvas canvas) {\n        final boolean vertical = mVertical;\n        final int extent = mExtent;\n        final int range = mRange;\n\n        boolean drawTrack = true;\n        boolean drawThumb = true;\n        if (extent <= 0 || range <= extent) {\n            drawTrack = vertical ? mAlwaysDrawVerticalTrack : mAlwaysDrawHorizontalTrack;\n            drawThumb = false;\n        }\n\n        Rect r = getBounds();\n        if (canvas.quickReject(r.left, r.top, r.right, r.bottom, \n                Canvas.EdgeType.AA)) {\n            return;\n        }\n        if (drawTrack) {\n            drawTrack(canvas, r, vertical);\n        }\n\n        if (drawThumb) {\n            int size = vertical ? r.height() : r.width();\n            int thickness = vertical ? r.width() : r.height();\n            int length = Math.round((float) size * extent / range);\n            int offset = Math.round((float) (size - length) * mOffset / (range - extent));\n\n            // avoid the tiny thumb\n            int minLength = thickness * 2;\n            if (length < minLength) {\n                length = minLength;\n            }\n            // avoid the too-big thumb\n            if (offset + length > size) {\n                offset = size - length;\n            }\n\n            drawThumb(canvas, r, offset, length, vertical);\n        }\n    }\n\n    @Override\n    protected void onBoundsChange(Rect bounds) {\n        super.onBoundsChange(bounds);\n        mChanged = true;\n    }\n\n    protected void drawTrack(Canvas canvas, Rect bounds, boolean vertical) {\n        Drawable track;\n        if (vertical) {\n            track = mVerticalTrack;\n        } else {\n            track = mHorizontalTrack;\n        }\n        if (track != null) {\n            if (mChanged) {\n                track.setBounds(bounds);\n            }\n            track.draw(canvas);\n        }\n    }\n\n    protected void drawThumb(Canvas canvas, Rect bounds, int offset, int length, boolean vertical) {\n        final Rect thumbRect = mTempBounds;\n        final boolean changed = mRangeChanged || mChanged;\n        if (changed) {\n            if (vertical) {\n                thumbRect.set(bounds.left,  bounds.top + offset,\n                        bounds.right, bounds.top + offset + length);\n            } else {\n                thumbRect.set(bounds.left + offset, bounds.top,\n                        bounds.left + offset + length, bounds.bottom);\n            }\n        }\n\n        if (vertical) {\n            final Drawable thumb = mVerticalThumb;\n            if (changed) thumb.setBounds(thumbRect);\n            thumb.draw(canvas);\n        } else {\n            final Drawable thumb = mHorizontalThumb;\n            if (changed) thumb.setBounds(thumbRect);\n            thumb.draw(canvas);\n        }\n    }\n\n    public void setVerticalThumbDrawable(Drawable thumb) {\n        if (thumb != null) {\n            mVerticalThumb = thumb;\n        }\n    }\n\n    public void setVerticalTrackDrawable(Drawable track) {\n        mVerticalTrack = track;\n    }\n\n    public void setHorizontalThumbDrawable(Drawable thumb) {\n        if (thumb != null) {\n            mHorizontalThumb = thumb;\n        }\n    }\n\n    public void setHorizontalTrackDrawable(Drawable track) {\n        mHorizontalTrack = track;\n    }\n\n    public int getSize(boolean vertical) {\n        if (vertical) {\n            return (mVerticalTrack != null ?\n                    mVerticalTrack : mVerticalThumb).getIntrinsicWidth();\n        } else {\n            return (mHorizontalTrack != null ?\n                    mHorizontalTrack : mHorizontalThumb).getIntrinsicHeight();\n        }\n    }\n\n    @Override\n    public void setAlpha(int alpha) {\n        if (mVerticalTrack != null) {\n            mVerticalTrack.setAlpha(alpha);\n        }\n        mVerticalThumb.setAlpha(alpha);\n        if (mHorizontalTrack != null) {\n            mHorizontalTrack.setAlpha(alpha);\n        }\n        mHorizontalThumb.setAlpha(alpha);\n    }\n\n    @Override\n    public void setColorFilter(ColorFilter cf) {\n        if (mVerticalTrack != null) {\n            mVerticalTrack.setColorFilter(cf);\n        }\n        mVerticalThumb.setColorFilter(cf);\n        if (mHorizontalTrack != null) {\n            mHorizontalTrack.setColorFilter(cf);\n        }\n        mHorizontalThumb.setColorFilter(cf);\n    }\n\n    @Override\n    public int getOpacity() {\n        return PixelFormat.TRANSLUCENT;\n    }\n\n    @Override\n    public String toString() {\n        return \"ScrollBarDrawable: range=\" + mRange + \" offset=\" + mOffset +\n               \" extent=\" + mExtent + (mVertical ? \" V\" : \" H\");\n    }\n}\n\n\n"
  },
  {
    "path": "lib/src/com/jess/ui/TwoWayAbsListView.java",
    "content": "/*\n * A modified version of the Android AbsListView\n *\n * Copyright 2012 Jess Anders\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.jess.ui;\n\n\nimport java.util.ArrayList;\nimport java.util.List;\n\nimport android.content.Context;\nimport android.content.res.Configuration;\nimport android.content.res.TypedArray;\nimport android.graphics.Canvas;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport android.graphics.drawable.TransitionDrawable;\nimport android.os.Debug;\nimport android.os.Handler;\nimport android.os.Parcel;\nimport android.os.Parcelable;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.HapticFeedbackConstants;\nimport android.view.KeyEvent;\nimport android.view.MotionEvent;\nimport android.view.VelocityTracker;\nimport android.view.View;\nimport android.view.ViewConfiguration;\nimport android.view.ViewDebug;\nimport android.view.ViewGroup;\nimport android.view.ViewTreeObserver;\nimport android.view.ContextMenu.ContextMenuInfo;\nimport android.widget.Adapter;\nimport android.widget.EditText;\nimport android.widget.ListAdapter;\nimport android.widget.Scroller;\n\n/**\n * Base class that can be used to implement virtualized lists of items. A list does\n * not have a spatial definition here. For instance, subclases of this class can\n * display the content of the list in a grid, in a carousel, as stack, etc.\n *\n * @attr ref android.R.styleable#JessAbsListView_listSelector\n * @attr ref android.R.styleable#JessAbsListView_drawSelectorOnTop\n * @attr ref android.R.styleable#JessAbsListView_stackFromBottom\n * @attr ref android.R.styleable#JessAbsListView_scrollingCache\n * @attr ref android.R.styleable#JessAbsListView_textFilterEnabled\n * @attr ref android.R.styleable#JessAbsListView_transcriptMode\n * @attr ref android.R.styleable#JessAbsListView_cacheColorHint\n * @attr ref android.R.styleable#JessAbsListView_smoothScrollbar\n */\npublic abstract class TwoWayAbsListView extends TwoWayAdapterView<ListAdapter> implements\nViewTreeObserver.OnTouchModeChangeListener {\n\tprivate static final String TAG = \"TwoWayAbsListView\";\n\tprivate static final boolean DEBUG = false;\n\n\n\t/**\n\t * Disables the transcript mode.\n\t *\n\t * @see #setTranscriptMode(int)\n\t */\n\tpublic static final int TRANSCRIPT_MODE_DISABLED = 0;\n\t/**\n\t * The list will automatically scroll to the bottom when a data set change\n\t * notification is received and only if the last item is already visible\n\t * on screen.\n\t *\n\t * @see #setTranscriptMode(int)\n\t */\n\tpublic static final int TRANSCRIPT_MODE_NORMAL = 1;\n\t/**\n\t * The list will automatically scroll to the bottom, no matter what items\n\t * are currently visible.\n\t *\n\t * @see #setTranscriptMode(int)\n\t */\n\tpublic static final int TRANSCRIPT_MODE_ALWAYS_SCROLL = 2;\n\n\t/**\n\t * Indicates that we are not in the middle of a touch gesture\n\t */\n\tstatic final int TOUCH_MODE_REST = -1;\n\n\t/**\n\t * Indicates we just received the touch event and we are waiting to see if the it is a tap or a\n\t * scroll gesture.\n\t */\n\tstatic final int TOUCH_MODE_DOWN = 0;\n\n\t/**\n\t * Indicates the touch has been recognized as a tap and we are now waiting to see if the touch\n\t * is a longpress\n\t */\n\tstatic final int TOUCH_MODE_TAP = 1;\n\n\t/**\n\t * Indicates we have waited for everything we can wait for, but the user's finger is still down\n\t */\n\tstatic final int TOUCH_MODE_DONE_WAITING = 2;\n\n\t/**\n\t * Indicates the touch gesture is a scroll\n\t */\n\tstatic final int TOUCH_MODE_SCROLL = 3;\n\n\t/**\n\t * Indicates the view is in the process of being flung\n\t */\n\tstatic final int TOUCH_MODE_FLING = 4;\n\n\t/**\n\t * Regular layout - usually an unsolicited layout from the view system\n\t */\n\tstatic final int LAYOUT_NORMAL = 0;\n\n\t/**\n\t * Show the first item\n\t */\n\tstatic final int LAYOUT_FORCE_TOP = 1;\n\n\t/**\n\t * Force the selected item to be on somewhere on the screen\n\t */\n\tstatic final int LAYOUT_SET_SELECTION = 2;\n\n\t/**\n\t * Show the last item\n\t */\n\tstatic final int LAYOUT_FORCE_BOTTOM = 3;\n\n\t/**\n\t * Make a mSelectedItem appear in a specific location and build the rest of\n\t * the views from there. The top is specified by mSpecificTop.\n\t */\n\tstatic final int LAYOUT_SPECIFIC = 4;\n\n\t/**\n\t * Layout to sync as a result of a data change. Restore mSyncPosition to have its top\n\t * at mSpecificTop\n\t */\n\tstatic final int LAYOUT_SYNC = 5;\n\n\t/**\n\t * Layout as a result of using the navigation keys\n\t */\n\tstatic final int LAYOUT_MOVE_SELECTION = 6;\n\n\t/**\n\t * Sets the View to Scroll Vertically.\n\t *\n\t * @see #setScrollDirectionPortrait(int)\n\t * @see #setScrollDirectionLandscape(int)\n\t */\n\tstatic final int SCROLL_VERTICAL = 0;\n\n\t/**\n\t * Sets the View to Scroll Horizontally.\n\t *\n\t * @see #setScrollDirectionPortrait(int)\n\t * @see #setScrollDirectionLandscape(int)\n\t */\n\tstatic final int SCROLL_HORIZONTAL = 1;\n\n\n\t/**\n\t * Controls how the next layout will happen\n\t */\n\tint mLayoutMode = LAYOUT_NORMAL;\n\n\t/**\n\t * Should be used by subclasses to listen to changes in the dataset\n\t */\n\tAdapterDataSetObserver mDataSetObserver;\n\n\t/**\n\t * The adapter containing the data to be displayed by this view\n\t */\n\tListAdapter mAdapter;\n\n\t/**\n\t * Indicates whether the list selector should be drawn on top of the children or behind\n\t */\n\tboolean mDrawSelectorOnTop = false;\n\n\t/**\n\t * The drawable used to draw the selector\n\t */\n\tDrawable mSelector;\n\n\t/**\n\t * Defines the selector's location and dimension at drawing time\n\t */\n\tRect mSelectorRect = new Rect();\n\n\t/**\n\t * The data set used to store unused views that should be reused during the next layout\n\t * to avoid creating new ones\n\t */\n\tfinal RecycleBin mRecycler = new RecycleBin();\n\n\t/**\n\t * The selection's left padding\n\t */\n\tint mSelectionLeftPadding = 0;\n\n\t/**\n\t * The selection's top padding\n\t */\n\tint mSelectionTopPadding = 0;\n\n\t/**\n\t * The selection's right padding\n\t */\n\tint mSelectionRightPadding = 0;\n\n\t/**\n\t * The selection's bottom padding\n\t */\n\tint mSelectionBottomPadding = 0;\n\n\t/**\n\t * This view's padding\n\t */\n\tRect mListPadding = new Rect();\n\n\t/**\n\t * Subclasses must retain their measure spec from onMeasure() into this member\n\t */\n\tint mWidthMeasureSpec = 0;\n\n\t/**\n\t * The top scroll indicator\n\t */\n\tView mScrollUp;\n\n\t/**\n\t * The down scroll indicator\n\t */\n\tView mScrollDown;\n\n\t/**\n\t * The left scroll indicator\n\t */\n\tView mScrollLeft;\n\n\t/**\n\t * The right scroll indicator\n\t */\n\tView mScrollRight;\n\n\t/**\n\t * When the view is scrolling, this flag is set to true to indicate subclasses that\n\t * the drawing cache was enabled on the children\n\t */\n\tboolean mCachingStarted;\n\n\t/**\n\t * The position of the view that received the down motion event\n\t */\n\tint mMotionPosition;\n\n\n\n\t/**\n\t * The X value associated with the the down motion event\n\t */\n\tint mMotionX;\n\n\t/**\n\t * The Y value associated with the the down motion event\n\t */\n\tint mMotionY;\n\n\t/**\n\t * One of TOUCH_MODE_REST, TOUCH_MODE_DOWN, TOUCH_MODE_TAP, TOUCH_MODE_SCROLL, or\n\t * TOUCH_MODE_DONE_WAITING\n\t */\n\tint mTouchMode = TOUCH_MODE_REST;\n\n\t/**\n\t * Determines speed during touch scrolling\n\t */\n\tprivate VelocityTracker mVelocityTracker;\n\n\t/**\n\t * The offset in pixels form the top of the AdapterView to the top\n\t * of the currently selected view. Used to save and restore state.\n\t */\n\tint mSelectedTop = 0;\n\n\t/**\n\t * Indicates whether the list is stacked from the bottom edge or\n\t * the top edge.\n\t */\n\tboolean mStackFromBottom;\n\n\t/**\n\t * When set to true, the list automatically discards the children's\n\t * bitmap cache after scrolling.\n\t */\n\tboolean mScrollingCacheEnabled;\n\n\t/**\n\t * Whether or not to enable the fast scroll feature on this list\n\t */\n\t//boolean mFastScrollEnabled;\n\n\t/**\n\t * Optional callback to notify client when scroll position has changed\n\t */\n\tprivate OnScrollListener mOnScrollListener;\n\n\t/**\n\t * Keeps track of our accessory window\n\t */\n\t//PopupWindow mPopup;\n\n\t/**\n\t * Used with type filter window\n\t */\n\tEditText mTextFilter;\n\n\t/**\n\t * Indicates whether to use pixels-based or position-based scrollbar\n\t * properties.\n\t */\n\tprivate boolean mSmoothScrollbarEnabled = true;\n\n\t/**\n\t * Indicates that this view supports filtering\n\t */\n\t//private boolean mTextFilterEnabled;\n\n\t/**\n\t * Indicates that this view is currently displaying a filtered view of the data\n\t */\n\t//private boolean mFiltered;\n\n\t/**\n\t * Rectangle used for hit testing children\n\t */\n\tprivate Rect mTouchFrame;\n\n\t/**\n\t * The position to resurrect the selected position to.\n\t */\n\tint mResurrectToPosition = INVALID_POSITION;\n\n\tprivate ContextMenuInfo mContextMenuInfo = null;\n\n\t/**\n\t * Used to request a layout when we changed touch mode\n\t */\n\tprivate static final int TOUCH_MODE_UNKNOWN = -1;\n\tprivate static final int TOUCH_MODE_ON = 0;\n\tprivate static final int TOUCH_MODE_OFF = 1;\n\n\tprivate int mLastTouchMode = TOUCH_MODE_UNKNOWN;\n\n\tprivate static final boolean PROFILE_SCROLLING = false;\n\tprivate boolean mScrollProfilingStarted = false;\n\n\tprivate static final boolean PROFILE_FLINGING = false;\n\tprivate boolean mFlingProfilingStarted = false;\n\n\t/**\n\t * The last CheckForLongPress runnable we posted, if any\n\t */\n\tprivate CheckForLongPress mPendingCheckForLongPress;\n\n\t/**\n\t * The last CheckForTap runnable we posted, if any\n\t */\n\tprivate Runnable mPendingCheckForTap;\n\n\t/**\n\t * The last CheckForKeyLongPress runnable we posted, if any\n\t */\n\tprivate CheckForKeyLongPress mPendingCheckForKeyLongPress;\n\n\t/**\n\t * Acts upon click\n\t */\n\tprivate TwoWayAbsListView.PerformClick mPerformClick;\n\n\t/**\n\t * This view is in transcript mode -- it shows the bottom of the list when the data\n\t * changes\n\t */\n\tprivate int mTranscriptMode;\n\n\t/**\n\t * Indicates that this list is always drawn on top of a solid, single-color, opaque\n\t * background\n\t */\n\tprivate int mCacheColorHint;\n\n\t/**\n\t * The select child's view (from the adapter's getView) is enabled.\n\t */\n\tprivate boolean mIsChildViewEnabled;\n\n\t/**\n\t * The last scroll state reported to clients through {@link OnScrollListener}.\n\t */\n\tprivate int mLastScrollState = OnScrollListener.SCROLL_STATE_IDLE;\n\n\t/**\n\t * Helper object that renders and controls the fast scroll thumb.\n\t */\n\t//private FastScroller mFastScroller;\n\n\t//private boolean mGlobalLayoutListenerAddedFilter;\n\n\tprivate int mTouchSlop;\n\tprivate float mDensityScale;\n\n\t//private InputConnection mDefInputConnection;\n\t//private InputConnectionWrapper mPublicInputConnection;\n\n\tprivate Runnable mClearScrollingCache;\n\tprivate int mMinimumVelocity;\n\tprivate int mMaximumVelocity;\n\tprivate boolean mScrollVerticallyPortrait;\n\tprivate boolean mScrollVerticallyLandscape;\n\n\tprotected boolean mScrollVertically;\n\n\tprotected boolean mPortraitOrientation;\n\n\tprotected TouchHandler mTouchHandler;\n\n\tfinal boolean[] mIsScrap = new boolean[1];\n\n\t// True when the popup should be hidden because of a call to\n\t// dispatchDisplayHint()\n\t//private boolean mPopupHidden;\n\n\t/**\n\t * ID of the active pointer. This is used to retain consistency during\n\t * drags/flings if multiple pointers are used.\n\t */\n\tprivate int mActivePointerId = INVALID_POINTER;\n\n\t/**\n\t * Sentinel value for no current active pointer.\n\t * Used by {@link #mActivePointerId}.\n\t */\n\tprivate static final int INVALID_POINTER = -1;\n\n\t/**\n\t * Interface definition for a callback to be invoked when the list or grid\n\t * has been scrolled.\n\t */\n\tpublic interface OnScrollListener {\n\n\t\t/**\n\t\t * The view is not scrolling. Note navigating the list using the trackball counts as\n\t\t * being in the idle state since these transitions are not animated.\n\t\t */\n\t\tpublic static int SCROLL_STATE_IDLE = 0;\n\n\t\t/**\n\t\t * The user is scrolling using touch, and their finger is still on the screen\n\t\t */\n\t\tpublic static int SCROLL_STATE_TOUCH_SCROLL = 1;\n\n\t\t/**\n\t\t * The user had previously been scrolling using touch and had performed a fling. The\n\t\t * animation is now coasting to a stop\n\t\t */\n\t\tpublic static int SCROLL_STATE_FLING = 2;\n\n\t\t/**\n\t\t * Callback method to be invoked while the list view or grid view is being scrolled. If the\n\t\t * view is being scrolled, this method will be called before the next frame of the scroll is\n\t\t * rendered. In particular, it will be called before any calls to\n\t\t * {@link Adapter#getView(int, View, ViewGroup)}.\n\t\t *\n\t\t * @param view The view whose scroll state is being reported\n\t\t *\n\t\t * @param scrollState The current scroll state. One of {@link #SCROLL_STATE_IDLE},\n\t\t * {@link #SCROLL_STATE_TOUCH_SCROLL} or {@link #SCROLL_STATE_IDLE}.\n\t\t */\n\t\tpublic void onScrollStateChanged(TwoWayAbsListView view, int scrollState);\n\n\t\t/**\n\t\t * Callback method to be invoked when the list or grid has been scrolled. This will be\n\t\t * called after the scroll has completed\n\t\t * @param view The view whose scroll state is being reported\n\t\t * @param firstVisibleItem the index of the first visible cell (ignore if\n\t\t *        visibleItemCount == 0)\n\t\t * @param visibleItemCount the number of visible cells\n\t\t * @param totalItemCount the number of items in the list adaptor\n\t\t */\n\t\tpublic void onScroll(TwoWayAbsListView view, int firstVisibleItem, int visibleItemCount,\n\t\t\t\tint totalItemCount);\n\t}\n\n\tpublic TwoWayAbsListView(Context context) {\n\t\tsuper(context);\n\t\tinitAbsListView();\n\t\tsetupScrollInfo();\n\t\t//TypedArray a = context.obtainStyledAttributes(android.R.styleable.View);\n\t\t//initializeScrollbars(a);\n\t\t//a.recycle();\n\t}\n\n\tpublic TwoWayAbsListView(Context context, AttributeSet attrs) {\n\t\tthis(context, attrs, android.R.attr.absListViewStyle);\n\t}\n\n\tpublic TwoWayAbsListView(Context context, AttributeSet attrs, int defStyle) {\n\t\tsuper(context, attrs, defStyle);\n\t\tinitAbsListView();\n\n\t\tTypedArray a = context.obtainStyledAttributes(attrs,\n\t\t\t\tR.styleable.TwoWayAbsListView, defStyle, 0);\n\n\t\tDrawable d = a.getDrawable(R.styleable.TwoWayAbsListView_listSelector);\n\t\tif (d != null) {\n\t\t\tsetSelector(d);\n\t\t}\n\n\t\tmDrawSelectorOnTop = a.getBoolean(\n\t\t\t\tR.styleable.TwoWayAbsListView_drawSelectorOnTop, false);\n\n\t\tboolean stackFromBottom = a.getBoolean(R.styleable.TwoWayAbsListView_stackFromBottom, false);\n\t\tsetStackFromBottom(stackFromBottom);\n\n\t\tboolean scrollingCacheEnabled = a.getBoolean(R.styleable.TwoWayAbsListView_scrollingCache, true);\n\t\tsetScrollingCacheEnabled(scrollingCacheEnabled);\n\n\t\t//boolean useTextFilter = a.getBoolean(R.styleable.JessAbsListView_textFilterEnabled, false);\n\t\t//setTextFilterEnabled(useTextFilter);\n\n\t\tint transcriptMode = a.getInt(R.styleable.TwoWayAbsListView_transcriptMode,\n\t\t\t\tTRANSCRIPT_MODE_DISABLED);\n\t\tsetTranscriptMode(transcriptMode);\n\n\t\tint color = a.getColor(R.styleable.TwoWayAbsListView_cacheColorHint, 0);\n\t\tsetCacheColorHint(color);\n\n\t\t//boolean enableFastScroll = a.getBoolean(R.styleable.JessAbsListView_fastScrollEnabled, false);\n\t\t//setFastScrollEnabled(enableFastScroll);\n\n\t\tboolean smoothScrollbar = a.getBoolean(R.styleable.TwoWayAbsListView_smoothScrollbar, true);\n\t\tsetSmoothScrollbarEnabled(smoothScrollbar);\n\n\t\tint scrollDirection = a.getInt(R.styleable.TwoWayAbsListView_scrollDirectionPortrait, SCROLL_VERTICAL);\n\t\tmScrollVerticallyPortrait = (scrollDirection == SCROLL_VERTICAL);\n\n\t\tscrollDirection = a.getInt(R.styleable.TwoWayAbsListView_scrollDirectionLandscape, SCROLL_VERTICAL);\n\t\tmScrollVerticallyLandscape = (scrollDirection == SCROLL_VERTICAL);\n\n\t\ta.recycle();\n\t\tsetupScrollInfo();\n\t}\n\n\tprivate void initAbsListView() {\n\t\t// Setting focusable in touch mode will set the focusable property to true\n\t\tsetClickable(true);\n\t\tsetFocusableInTouchMode(true);\n\t\tsetWillNotDraw(false);\n\t\tsetAlwaysDrawnWithCacheEnabled(false);\n\t\tsetScrollingCacheEnabled(true);\n\n\t\tfinal ViewConfiguration configuration = ViewConfiguration.get(mContext);\n\t\tmTouchSlop = configuration.getScaledTouchSlop();\n\t\tmMinimumVelocity = configuration.getScaledMinimumFlingVelocity();\n\t\tmMaximumVelocity = configuration.getScaledMaximumFlingVelocity();\n\t\tmDensityScale = getContext().getResources().getDisplayMetrics().density;\n\t\tmPortraitOrientation = (getResources().getConfiguration().orientation !=\n\t\t\tConfiguration.ORIENTATION_LANDSCAPE);\n\t\tmScrollVertically = true;\n\n\n\t}\n\n\n\n\tprivate void setupScrollInfo() {\n\t\tmScrollVertically = mPortraitOrientation ? mScrollVerticallyPortrait: mScrollVerticallyLandscape;\n\t\tif (mScrollVertically) {\n\t\t\tmTouchHandler = new VerticalTouchHandler();\n\t\t\tsetVerticalScrollBarEnabled(true);\n\t\t\tsetHorizontalScrollBarEnabled(false);\n            setIsVertical(true);\n\t\t} else {\n\t\t\tmTouchHandler = new HorizontalTouchHandler();\n\t\t\tsetVerticalScrollBarEnabled(false);\n\t\t\tsetHorizontalScrollBarEnabled(true);\n            setIsVertical(false);\n\t\t}\n\n\t}\n\n\tprivate boolean orientationChanged() {\n\t\tboolean temp = mPortraitOrientation;\n\t\tmPortraitOrientation = (getResources().getConfiguration().orientation !=\n\t\t\tConfiguration.ORIENTATION_LANDSCAPE);\n\n\t\tboolean result = (temp != mPortraitOrientation);\n\t\tif (result) {\n\t\t\tsetupScrollInfo();\n\t\t\tmRecycler.scrapActiveViews();\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Enables fast scrolling by letting the user quickly scroll through lists by\n\t * dragging the fast scroll thumb. The adapter attached to the list may want\n\t * to implement {@link SectionIndexer} if it wishes to display alphabet preview and\n\t * jump between sections of the list.\n\t * @see SectionIndexer\n\t * @see #isFastScrollEnabled()\n\t * @param enabled whether or not to enable fast scrolling\n\t */\n\t/*\n    public void setFastScrollEnabled(boolean enabled) {\n        mFastScrollEnabled = enabled;\n        if (enabled) {\n            if (mFastScroller == null) {\n                mFastScroller = new FastScroller(getContext(), this);\n            }\n        } else {\n            if (mFastScroller != null) {\n                mFastScroller.stop();\n                mFastScroller = null;\n            }\n        }\n    }*/\n\n\t/**\n\t * Returns the current state of the fast scroll feature.\n\t * @see #setFastScrollEnabled(boolean)\n\t * @return true if fast scroll is enabled, false otherwise\n\t */\n\t/*\n    @ViewDebug.ExportedProperty\n    public boolean isFastScrollEnabled() {\n        return mFastScrollEnabled;\n    }\n\n    protected boolean isVerticalScrollBarHidden() {\n        return mFastScroller != null && mFastScroller.isVisible();\n    }*/\n\n\t/**\n\t * When smooth scrollbar is enabled, the position and size of the scrollbar thumb\n\t * is computed based on the number of visible pixels in the visible items. This\n\t * however assumes that all list items have the same height. If you use a list in\n\t * which items have different heights, the scrollbar will change appearance as the\n\t * user scrolls through the list. To avoid this issue, you need to disable this\n\t * property.\n\t *\n\t * When smooth scrollbar is disabled, the position and size of the scrollbar thumb\n\t * is based solely on the number of items in the adapter and the position of the\n\t * visible items inside the adapter. This provides a stable scrollbar as the user\n\t * navigates through a list of items with varying heights.\n\t *\n\t * @param enabled Whether or not to enable smooth scrollbar.\n\t *\n\t * @see #setSmoothScrollbarEnabled(boolean)\n\t * @attr ref android.R.styleable#JessAbsListView_smoothScrollbar\n\t */\n\tpublic void setSmoothScrollbarEnabled(boolean enabled) {\n\t\tmSmoothScrollbarEnabled = enabled;\n\t}\n\n\t/**\n\t * Returns the current state of the fast scroll feature.\n\t *\n\t * @return True if smooth scrollbar is enabled is enabled, false otherwise.\n\t *\n\t * @see #setSmoothScrollbarEnabled(boolean)\n\t */\n\t@ViewDebug.ExportedProperty\n\tpublic boolean isSmoothScrollbarEnabled() {\n\t\treturn mSmoothScrollbarEnabled;\n\t}\n\n\t/**\n\t * Set the listener that will receive notifications every time the list scrolls.\n\t *\n\t * @param l the scroll listener\n\t */\n\tpublic void setOnScrollListener(OnScrollListener l) {\n\t\tmOnScrollListener = l;\n\t\tinvokeOnItemScrollListener();\n\t}\n\n\t/**\n\t * Notify our scroll listener (if there is one) of a change in scroll state\n\t */\n\tvoid invokeOnItemScrollListener() {\n\t\t//if (mFastScroller != null) {\n\t\t//    mFastScroller.onScroll(this, mFirstPosition, getChildCount(), mItemCount);\n\t\t//}\n\t\tif (mOnScrollListener != null) {\n\t\t\tmOnScrollListener.onScroll(this, mFirstPosition, getChildCount(), mItemCount);\n\t\t}\n\t}\n\n\t/**\n\t * Indicates whether the children's drawing cache is used during a scroll.\n\t * By default, the drawing cache is enabled but this will consume more memory.\n\t *\n\t * @return true if the scrolling cache is enabled, false otherwise\n\t *\n\t * @see #setScrollingCacheEnabled(boolean)\n\t * @see View#setDrawingCacheEnabled(boolean)\n\t */\n\t@ViewDebug.ExportedProperty\n\tpublic boolean isScrollingCacheEnabled() {\n\t\treturn mScrollingCacheEnabled;\n\t}\n\n\t/**\n\t * Enables or disables the children's drawing cache during a scroll.\n\t * By default, the drawing cache is enabled but this will use more memory.\n\t *\n\t * When the scrolling cache is enabled, the caches are kept after the\n\t * first scrolling. You can manually clear the cache by calling\n\t * {@link android.view.ViewGroup#setChildrenDrawingCacheEnabled(boolean)}.\n\t *\n\t * @param enabled true to enable the scroll cache, false otherwise\n\t *\n\t * @see #isScrollingCacheEnabled()\n\t * @see View#setDrawingCacheEnabled(boolean)\n\t */\n\tpublic void setScrollingCacheEnabled(boolean enabled) {\n\t\tif (mScrollingCacheEnabled && !enabled) {\n\t\t\tmTouchHandler.clearScrollingCache();\n\t\t}\n\t\tmScrollingCacheEnabled = enabled;\n\t}\n\n//\t/**\n//\t * Enables or disables the type filter window. If enabled, typing when\n//\t * this view has focus will filter the children to match the users input.\n//\t * Note that the {@link Adapter} used by this view must implement the\n//\t * {@link Filterable} interface.\n//\t *\n//\t * @param textFilterEnabled true to enable type filtering, false otherwise\n//\t *\n//\t * @see Filterable\n//\t */\n//\t//public void setTextFilterEnabled(boolean textFilterEnabled) {\n//\t//    mTextFilterEnabled = textFilterEnabled;\n//\t//}\n//\n//\t/**\n//\t * Indicates whether type filtering is enabled for this view\n//\t *\n//\t * @return true if type filtering is enabled, false otherwise\n//\t *\n//\t * @see #setTextFilterEnabled(boolean)\n//\t * @see Filterable\n//\t */\n//\t//@ViewDebug.ExportedProperty\n//\t//public boolean isTextFilterEnabled() {\n//\t//    return mTextFilterEnabled;\n//\t//}\n\n\t@Override\n\tpublic void getFocusedRect(Rect r) {\n\t\tView view = getSelectedView();\n\t\tif (view != null && view.getParent() == this) {\n\t\t\t// the focused rectangle of the selected view offset into the\n\t\t\t// coordinate space of this view.\n\t\t\tview.getFocusedRect(r);\n\t\t\toffsetDescendantRectToMyCoords(view, r);\n\t\t} else {\n\t\t\t// otherwise, just the norm\n\t\t\tsuper.getFocusedRect(r);\n\t\t}\n\t}\n\n\tprivate void useDefaultSelector() {\n\t\tsetSelector(getResources().getDrawable(\n\t\t\t\tandroid.R.drawable.list_selector_background));\n\t}\n\n\t/**\n\t * Indicates whether the content of this view is pinned to, or stacked from,\n\t * the bottom edge.\n\t *\n\t * @return true if the content is stacked from the bottom edge, false otherwise\n\t */\n\t@ViewDebug.ExportedProperty\n\tpublic boolean isStackFromBottom() {\n\t\treturn mStackFromBottom;\n\t}\n\n\t/**\n\t * When stack from bottom is set to true, the list fills its content starting from\n\t * the bottom of the view.\n\t *\n\t * @param stackFromBottom true to pin the view's content to the bottom edge,\n\t *        false to pin the view's content to the top edge\n\t */\n\tpublic void setStackFromBottom(boolean stackFromBottom) {\n\t\tif (mStackFromBottom != stackFromBottom) {\n\t\t\tmStackFromBottom = stackFromBottom;\n\t\t\trequestLayoutIfNecessary();\n\t\t}\n\t}\n\n\tvoid requestLayoutIfNecessary() {\n\t\tif (getChildCount() > 0) {\n\t\t\tresetList();\n\t\t\trequestLayout();\n\t\t\tinvalidate();\n\t\t}\n\t}\n\n\tstatic class SavedState extends BaseSavedState {\n\t\tlong selectedId;\n\t\tlong firstId;\n\t\tint viewTop;\n\t\tint position;\n\t\tint height;\n\t\t//String filter;\n\n\t\t/**\n\t\t * Constructor called from {@link TwoWayAbsListView#onSaveInstanceState()}\n\t\t */\n\t\tSavedState(Parcelable superState) {\n\t\t\tsuper(superState);\n\t\t}\n\n\t\t/**\n\t\t * Constructor called from {@link #CREATOR}\n\t\t */\n\t\tprivate SavedState(Parcel in) {\n\t\t\tsuper(in);\n\t\t\tselectedId = in.readLong();\n\t\t\tfirstId = in.readLong();\n\t\t\tviewTop = in.readInt();\n\t\t\tposition = in.readInt();\n\t\t\theight = in.readInt();\n\t\t\t//filter = in.readString();\n\t\t}\n\n\t\t@Override\n\t\tpublic void writeToParcel(Parcel out, int flags) {\n\t\t\tsuper.writeToParcel(out, flags);\n\t\t\tout.writeLong(selectedId);\n\t\t\tout.writeLong(firstId);\n\t\t\tout.writeInt(viewTop);\n\t\t\tout.writeInt(position);\n\t\t\tout.writeInt(height);\n\t\t\t//out.writeString(filter);\n\t\t}\n\n\t\t@Override\n\t\tpublic String toString() {\n\t\t\treturn \"TwoWayAbsListView.SavedState{\"\n\t\t\t+ Integer.toHexString(System.identityHashCode(this))\n\t\t\t+ \" selectedId=\" + selectedId\n\t\t\t+ \" firstId=\" + firstId\n\t\t\t+ \" viewTop=\" + viewTop\n\t\t\t+ \" position=\" + position\n\t\t\t+ \" height=\" + height + \"}\";\n\t\t\t//+ \" filter=\" + filter + \"}\";\n\t\t}\n\n\t\tpublic static final Parcelable.Creator<SavedState> CREATOR\n\t\t= new Parcelable.Creator<SavedState>() {\n\t\t\tpublic SavedState createFromParcel(Parcel in) {\n\t\t\t\treturn new SavedState(in);\n\t\t\t}\n\n\t\t\tpublic SavedState[] newArray(int size) {\n\t\t\t\treturn new SavedState[size];\n\t\t\t}\n\t\t};\n\t}\n\n\t@Override\n\tpublic Parcelable onSaveInstanceState() {\n\t\t/*\n\t\t * This doesn't really make sense as the place to dismiss the\n\t\t * popups, but there don't seem to be any other useful hooks\n\t\t * that happen early enough to keep from getting complaints\n\t\t * about having leaked the window.\n\t\t */\n\t\t//dismissPopup();\n\n\t\tParcelable superState = super.onSaveInstanceState();\n\n\t\tSavedState ss = new SavedState(superState);\n\n\t\tboolean haveChildren = getChildCount() > 0;\n\t\tlong selectedId = getSelectedItemId();\n\t\tss.selectedId = selectedId;\n\t\tss.height = getHeight();\n\n\t\tif (selectedId >= 0) {\n\t\t\t// Remember the selection\n\t\t\tss.viewTop = mSelectedTop;\n\t\t\tss.position = getSelectedItemPosition();\n\t\t\tss.firstId = INVALID_POSITION;\n\t\t} else {\n\t\t\tif (haveChildren) {\n\t\t\t\t// Remember the position of the first child\n\t\t\t\tView v = getChildAt(0);\n\t\t\t\tif(mScrollVertically) {\n\t\t\t\t\tss.viewTop = v.getTop();\n\t\t\t\t} else {\n\t\t\t\t\tss.viewTop = v.getLeft();\n\t\t\t\t}\n\t\t\t\tss.position = mFirstPosition;\n\t\t\t\tss.firstId = mAdapter.getItemId(mFirstPosition);\n\t\t\t} else {\n\t\t\t\tss.viewTop = 0;\n\t\t\t\tss.firstId = INVALID_POSITION;\n\t\t\t\tss.position = 0;\n\t\t\t}\n\t\t}\n\t\t/*\n        ss.filter = null;\n        if (mFiltered) {\n            final EditText textFilter = mTextFilter;\n            if (textFilter != null) {\n                Editable filterText = textFilter.getText();\n                if (filterText != null) {\n                    ss.filter = filterText.toString();\n                }\n            }\n        }*/\n\n\t\treturn ss;\n\t}\n\n\t@Override\n\tpublic void onRestoreInstanceState(Parcelable state) {\n\t\tSavedState ss = (SavedState) state;\n\n\t\tsuper.onRestoreInstanceState(ss.getSuperState());\n\t\tmDataChanged = true;\n\n\t\tmSyncSize = ss.height;\n\n\t\tif (ss.selectedId >= 0) {\n\t\t\tmNeedSync = true;\n\t\t\tmSyncRowId = ss.selectedId;\n\t\t\tmSyncPosition = ss.position;\n\t\t\tmSpecificTop = ss.viewTop;\n\t\t\tmSyncMode = SYNC_SELECTED_POSITION;\n\t\t} else if (ss.firstId >= 0) {\n\t\t\tsetSelectedPositionInt(INVALID_POSITION);\n\t\t\t// Do this before setting mNeedSync since setNextSelectedPosition looks at mNeedSync\n\t\t\tsetNextSelectedPositionInt(INVALID_POSITION);\n\t\t\tmNeedSync = true;\n\t\t\tmSyncRowId = ss.firstId;\n\t\t\tmSyncPosition = ss.position;\n\t\t\tmSpecificTop = ss.viewTop;\n\t\t\tmSyncMode = SYNC_FIRST_POSITION;\n\t\t}\n\n\t\t//setFilterText(ss.filter);\n\n\t\trequestLayout();\n\t}\n\n\t//    private boolean acceptFilter() {\n\t//        return mTextFilterEnabled && getAdapter() instanceof Filterable &&\n\t//                ((Filterable) getAdapter()).getFilter() != null;\n\t//    }\n\t//\n\t//    /**\n\t//     * Sets the initial value for the text filter.\n\t//     * @param filterText The text to use for the filter.\n\t//     *\n\t//     * @see #setTextFilterEnabled\n\t//     */\n\t//    public void setFilterText(String filterText) {\n\t//        // TODO: Should we check for acceptFilter()?\n\t//        if (mTextFilterEnabled && !TextUtils.isEmpty(filterText)) {\n\t//            createTextFilter(false);\n\t//            // This is going to call our listener onTextChanged, but we might not\n\t//            // be ready to bring up a window yet\n\t//            mTextFilter.setText(filterText);\n\t//            mTextFilter.setSelection(filterText.length());\n\t//            if (mAdapter instanceof Filterable) {\n\t//                // if mPopup is non-null, then onTextChanged will do the filtering\n\t//                if (mPopup == null) {\n\t//                    Filter f = ((Filterable) mAdapter).getFilter();\n\t//                    f.filter(filterText);\n\t//                }\n\t//                // Set filtered to true so we will display the filter window when our main\n\t//                // window is ready\n\t//                mFiltered = true;\n\t//                mDataSetObserver.clearSavedState();\n\t//            }\n\t//        }\n\t//    }\n\t//\n\t//    /**\n\t//     * Returns the list's text filter, if available.\n\t//     * @return the list's text filter or null if filtering isn't enabled\n\t//     */\n\t//    public CharSequence getTextFilter() {\n\t//        if (mTextFilterEnabled && mTextFilter != null) {\n\t//            return mTextFilter.getText();\n\t//        }\n\t//        return null;\n\t//    }\n\n\t@Override\n\tprotected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {\n\t\tsuper.onFocusChanged(gainFocus, direction, previouslyFocusedRect);\n\t\tif (gainFocus && mSelectedPosition < 0 && !isInTouchMode()) {\n\t\t\tresurrectSelection();\n\t\t}\n\t}\n\n\t@Override\n\tpublic void requestLayout() {\n\t\tif (!mBlockLayoutRequests && !mInLayout) {\n\t\t\tsuper.requestLayout();\n\t\t}\n\t}\n\n\t/**\n\t * The list is empty. Clear everything out.\n\t */\n\tvoid resetList() {\n\t\tremoveAllViewsInLayout();\n\t\tmFirstPosition = 0;\n\t\tmDataChanged = false;\n\t\tmNeedSync = false;\n\t\tmOldSelectedPosition = INVALID_POSITION;\n\t\tmOldSelectedRowId = INVALID_ROW_ID;\n\t\tsetSelectedPositionInt(INVALID_POSITION);\n\t\tsetNextSelectedPositionInt(INVALID_POSITION);\n\t\tmSelectedTop = 0;\n\t\tmSelectorRect.setEmpty();\n\t\tinvalidate();\n\t}\n\n\t@Override\n\tprotected int computeVerticalScrollExtent() {\n\t\tfinal int count = getChildCount();\n\t\tif (count > 0 && mScrollVertically) {\n\t\t\tif (mSmoothScrollbarEnabled) {\n\t\t\t\tint extent = count * 100;\n\n\t\t\t\tView view = getChildAt(0);\n\t\t\t\tfinal int top = view.getTop();\n\t\t\t\tint height = view.getHeight();\n\t\t\t\tif (height > 0) {\n\t\t\t\t\textent += (top * 100) / height;\n\t\t\t\t}\n\n\t\t\t\tview = getChildAt(count - 1);\n\t\t\t\tfinal int bottom = view.getBottom();\n\t\t\t\theight = view.getHeight();\n\t\t\t\tif (height > 0) {\n\t\t\t\t\textent -= ((bottom - getHeight()) * 100) / height;\n\t\t\t\t}\n\n\t\t\t\treturn extent;\n\t\t\t} else {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\t@Override\n\tprotected int computeVerticalScrollOffset() {\n\t\tfinal int firstPosition = mFirstPosition;\n\t\tfinal int childCount = getChildCount();\n\t\tif (firstPosition >= 0 && childCount > 0 && mScrollVertically) {\n\t\t\tif (mSmoothScrollbarEnabled) {\n\t\t\t\tfinal View view = getChildAt(0);\n\t\t\t\tfinal int top = view.getTop();\n\t\t\t\tint height = view.getHeight();\n\t\t\t\tif (height > 0) {\n\t\t\t\t\treturn Math.max(firstPosition * 100 - (top * 100) / height +\n\t\t\t\t\t\t\t(int)((float)getScrollY() / getHeight() * mItemCount * 100), 0);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tint index;\n\t\t\t\tfinal int count = mItemCount;\n\t\t\t\tif (firstPosition == 0) {\n\t\t\t\t\tindex = 0;\n\t\t\t\t} else if (firstPosition + childCount == count) {\n\t\t\t\t\tindex = count;\n\t\t\t\t} else {\n\t\t\t\t\tindex = firstPosition + childCount / 2;\n\t\t\t\t}\n\t\t\t\treturn (int) (firstPosition + childCount * (index / (float) count));\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\t@Override\n\tprotected int computeVerticalScrollRange() {\n\t\tint result;\n\t\tif (!mScrollVertically) {\n\t\t\tresult = 0;\n\t\t} else if (mSmoothScrollbarEnabled) {\n\t\t\tresult = Math.max(mItemCount * 100, 0);\n\t\t} else {\n\t\t\tresult = mItemCount;\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tprotected int computeHorizontalScrollExtent() {\n\t\tfinal int count = getChildCount();\n\t\tif (count > 0 && !mScrollVertically) {\n\t\t\tif (mSmoothScrollbarEnabled) {\n\t\t\t\tint extent = count * 100;\n\n\t\t\t\tView view = getChildAt(0);\n\t\t\t\tfinal int left = view.getLeft();\n\t\t\t\tint width = view.getWidth();\n\t\t\t\tif (width > 0) {\n\t\t\t\t\textent += (left * 100) / width;\n\t\t\t\t}\n\n\t\t\t\tview = getChildAt(count - 1);\n\t\t\t\tfinal int right = view.getRight();\n\t\t\t\twidth = view.getWidth();\n\t\t\t\tif (width > 0) {\n\t\t\t\t\textent -= ((right - getWidth()) * 100) / width;\n\t\t\t\t}\n\n\t\t\t\treturn extent;\n\t\t\t} else {\n\t\t\t\treturn 1;\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\t@Override\n\tprotected int computeHorizontalScrollOffset() {\n\t\tfinal int firstPosition = mFirstPosition;\n\t\tfinal int childCount = getChildCount();\n\t\tif (firstPosition >= 0 && childCount > 0 && !mScrollVertically) {\n\t\t\tif (mSmoothScrollbarEnabled) {\n\t\t\t\tfinal View view = getChildAt(0);\n\t\t\t\tfinal int left = view.getLeft();\n\t\t\t\tint width = view.getWidth();\n\t\t\t\tif (width > 0) {\n\t\t\t\t\treturn Math.max(firstPosition * 100 - (left * 100) / width +\n\t\t\t\t\t\t\t(int)((float)getScrollX() / getWidth() * mItemCount * 100), 0);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tint index;\n\t\t\t\tfinal int count = mItemCount;\n\t\t\t\tif (firstPosition == 0) {\n\t\t\t\t\tindex = 0;\n\t\t\t\t} else if (firstPosition + childCount == count) {\n\t\t\t\t\tindex = count;\n\t\t\t\t} else {\n\t\t\t\t\tindex = firstPosition + childCount / 2;\n\t\t\t\t}\n\t\t\t\treturn (int) (firstPosition + childCount * (index / (float) count));\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\t@Override\n\tprotected int computeHorizontalScrollRange() {\n\t\tint result;\n\t\tif (mScrollVertically) {\n\t\t\tresult = 0;\n\t\t} else if (mSmoothScrollbarEnabled) {\n\t\t\tresult = Math.max(mItemCount * 100, 0);\n\t\t} else {\n\t\t\tresult = mItemCount;\n\t\t}\n\t\treturn result;\n\t}\n\n\t@Override\n\tprotected float getTopFadingEdgeStrength() {\n\t\tfinal int count = getChildCount();\n\t\tfinal float fadeEdge = super.getTopFadingEdgeStrength();\n\t\tif (count == 0 || !mScrollVertically) {\n\t\t\treturn fadeEdge;\n\t\t} else {\n\t\t\tif (mFirstPosition > 0) {\n\t\t\t\treturn 1.0f;\n\t\t\t}\n\n\t\t\tfinal int top = getChildAt(0).getTop();\n\t\t\tfinal float fadeLength = getVerticalFadingEdgeLength();\n\t\t\tint paddintTop = getPaddingTop();\n\t\t\treturn top < paddintTop ? -(top - paddintTop) / fadeLength : fadeEdge;\n\t\t}\n\t}\n\n\t@Override\n\tprotected float getBottomFadingEdgeStrength() {\n\t\tfinal int count = getChildCount();\n\t\tfinal float fadeEdge = super.getBottomFadingEdgeStrength();\n\t\tif (count == 0 || !mScrollVertically) {\n\t\t\treturn fadeEdge;\n\t\t} else {\n\t\t\tif (mFirstPosition + count - 1 < mItemCount - 1) {\n\t\t\t\treturn 1.0f;\n\t\t\t}\n\n\t\t\tfinal int bottom = getChildAt(count - 1).getBottom();\n\t\t\tfinal int height = getHeight();\n\t\t\tfinal float fadeLength = getVerticalFadingEdgeLength();\n\t\t\tint paddingBottom = getPaddingBottom();\n\t\t\treturn bottom > height - paddingBottom ?\n\t\t\t\t\t(bottom - height + paddingBottom) / fadeLength : fadeEdge;\n\t\t}\n\t}\n\n\t@Override\n\tprotected float getLeftFadingEdgeStrength() {\n\t\tfinal int count = getChildCount();\n\t\tfinal float fadeEdge = super.getLeftFadingEdgeStrength();\n\t\tif (count == 0 || mScrollVertically) {\n\t\t\treturn fadeEdge;\n\t\t} else {\n\t\t\tif (mFirstPosition > 0) {\n\t\t\t\treturn 1.0f;\n\t\t\t}\n\n\t\t\tfinal int left = getChildAt(0).getLeft();\n\t\t\tfinal float fadeLength = getHorizontalFadingEdgeLength();\n\t\t\tint paddingLeft = getPaddingLeft();\n\t\t\treturn left < paddingLeft ? -(left - paddingLeft) / fadeLength : fadeEdge;\n\t\t}\n\t}\n\n\t@Override\n\tprotected float getRightFadingEdgeStrength() {\n\t\tfinal int count = getChildCount();\n\t\tfinal float fadeEdge = super.getRightFadingEdgeStrength();\n\t\tif (count == 0 || mScrollVertically) {\n\t\t\treturn fadeEdge;\n\t\t} else {\n\t\t\tif (mFirstPosition + count - 1 < mItemCount - 1) {\n\t\t\t\treturn 1.0f;\n\t\t\t}\n\n\t\t\tfinal int right = getChildAt(count - 1).getRight();\n\t\t\tfinal int width = getWidth();\n\t\t\tfinal float fadeLength = getHorizontalFadingEdgeLength();\n\t\t\tint paddingRight = getPaddingRight();\n\t\t\treturn right > width - paddingRight ?\n\t\t\t\t\t(right - width + paddingRight) / fadeLength : fadeEdge;\n\t\t}\n\t}\n\n\t@Override\n\tprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n\t\torientationChanged();\n\n\t\tif (mSelector == null) {\n\t\t\tuseDefaultSelector();\n\t\t}\n\t\tfinal Rect listPadding = mListPadding;\n\t\tlistPadding.left = mSelectionLeftPadding + getPaddingLeft();\n\t\tlistPadding.top = mSelectionTopPadding + getPaddingTop();\n\t\tlistPadding.right = mSelectionRightPadding + getPaddingRight();\n\t\tlistPadding.bottom = mSelectionBottomPadding + getPaddingBottom();\n\t}\n\n\t/**\n\t * Subclasses should NOT override this method but\n\t *  {@link #layoutChildren()} instead.\n\t */\n\t@Override\n\tprotected void onLayout(boolean changed, int l, int t, int r, int b) {\n\t\tif (orientationChanged()) {\n\t\t\tsetupScrollInfo();\n\t\t}\n\t\tsuper.onLayout(changed, l, t, r, b);\n\t\tmInLayout = true;\n\t\tif (changed) {\n\t\t\tint childCount = getChildCount();\n\t\t\tfor (int i = 0; i < childCount; i++) {\n\t\t\t\tgetChildAt(i).forceLayout();\n\t\t\t}\n\t\t\tmRecycler.markChildrenDirty();\n\t\t}\n\n\t\tlayoutChildren();\n\t\tmInLayout = false;\n\t}\n\n\t/*\n    protected boolean setFrame(int left, int top, int right, int bottom) {\n        final boolean changed = super.setFrame(left, top, right, bottom);\n\n        if (changed) {\n            // Reposition the popup when the frame has changed. This includes\n            // translating the widget, not just changing its dimension. The\n            // filter popup needs to follow the widget.\n            final boolean visible = getWindowVisibility() == View.VISIBLE;\n            if (mFiltered && visible && mPopup != null && mPopup.isShowing()) {\n                positionPopup();\n            }\n        }\n\n        return changed;\n    }*/\n\n\t/**\n\t * Subclasses must override this method to layout their children.\n\t */\n\tprotected void layoutChildren() {\n\t}\n\n\tvoid updateScrollIndicators() {\n\t\tif (mScrollUp != null && mScrollVertically) {\n\t\t\tboolean canScrollUp;\n\t\t\t// 0th element is not visible\n\t\t\tcanScrollUp = mFirstPosition > 0;\n\n\t\t\t// ... Or top of 0th element is not visible\n\t\t\tif (!canScrollUp) {\n\t\t\t\tif (getChildCount() > 0) {\n\t\t\t\t\tView child = getChildAt(0);\n\t\t\t\t\tcanScrollUp = child.getTop() < mListPadding.top;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmScrollUp.setVisibility(canScrollUp ? View.VISIBLE : View.INVISIBLE);\n\t\t}\n\n\t\tif (mScrollDown != null && mScrollVertically) {\n\t\t\tboolean canScrollDown;\n\t\t\tint count = getChildCount();\n\n\t\t\t// Last item is not visible\n\t\t\tcanScrollDown = (mFirstPosition + count) < mItemCount;\n\n\t\t\t// ... Or bottom of the last element is not visible\n\t\t\tif (!canScrollDown && count > 0) {\n\t\t\t\tView child = getChildAt(count - 1);\n\t\t\t\tcanScrollDown = child.getBottom() > getBottom() - mListPadding.bottom;\n\t\t\t}\n\n\t\t\tmScrollDown.setVisibility(canScrollDown ? View.VISIBLE : View.INVISIBLE);\n\t\t}\n\n\t\tif (mScrollLeft != null && !mScrollVertically) {\n\t\t\tboolean canScrollLeft;\n\t\t\t// 0th element is not visible\n\t\t\tcanScrollLeft = mFirstPosition > 0;\n\n\t\t\t// ... Or top of 0th element is not visible\n\t\t\tif (!canScrollLeft) {\n\t\t\t\tif (getChildCount() > 0) {\n\t\t\t\t\tView child = getChildAt(0);\n\t\t\t\t\tcanScrollLeft = child.getLeft() < mListPadding.left;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmScrollLeft.setVisibility(canScrollLeft ? View.VISIBLE : View.INVISIBLE);\n\t\t}\n\n\t\tif (mScrollRight != null && !mScrollVertically) {\n\t\t\tboolean canScrollRight;\n\t\t\tint count = getChildCount();\n\n\t\t\t// Last item is not visible\n\t\t\tcanScrollRight = (mFirstPosition + count) < mItemCount;\n\n\t\t\t// ... Or bottom of the last element is not visible\n\t\t\tif (!canScrollRight && count > 0) {\n\t\t\t\tView child = getChildAt(count - 1);\n\t\t\t\tcanScrollRight = child.getRight() > getRight() - mListPadding.right;\n\t\t\t}\n\n\t\t\tmScrollRight.setVisibility(canScrollRight ? View.VISIBLE : View.INVISIBLE);\n\t\t}\n\t}\n\n\t@Override\n\t@ViewDebug.ExportedProperty\n\tpublic View getSelectedView() {\n\t\tif (mItemCount > 0 && mSelectedPosition >= 0) {\n\t\t\treturn getChildAt(mSelectedPosition - mFirstPosition);\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * List padding is the maximum of the normal view's padding and the padding of the selector.\n\t *\n\t * @see android.view.View#getPaddingTop()\n\t * @see #getSelector()\n\t *\n\t * @return The top list padding.\n\t */\n\tpublic int getListPaddingTop() {\n\t\treturn mListPadding.top;\n\t}\n\n\t/**\n\t * List padding is the maximum of the normal view's padding and the padding of the selector.\n\t *\n\t * @see android.view.View#getPaddingBottom()\n\t * @see #getSelector()\n\t *\n\t * @return The bottom list padding.\n\t */\n\tpublic int getListPaddingBottom() {\n\t\treturn mListPadding.bottom;\n\t}\n\n\t/**\n\t * List padding is the maximum of the normal view's padding and the padding of the selector.\n\t *\n\t * @see android.view.View#getPaddingLeft()\n\t * @see #getSelector()\n\t *\n\t * @return The left list padding.\n\t */\n\tpublic int getListPaddingLeft() {\n\t\treturn mListPadding.left;\n\t}\n\n\t/**\n\t * List padding is the maximum of the normal view's padding and the padding of the selector.\n\t *\n\t * @see android.view.View#getPaddingRight()\n\t * @see #getSelector()\n\t *\n\t * @return The right list padding.\n\t */\n\tpublic int getListPaddingRight() {\n\t\treturn mListPadding.right;\n\t}\n\n\t/**\n\t * Get a view and have it show the data associated with the specified\n\t * position. This is called when we have already discovered that the view is\n\t * not available for reuse in the recycle bin. The only choices left are\n\t * converting an old view or making a new one.\n\t *\n\t * @param position The position to display\n\t * @param isScrap Array of at least 1 boolean, the first entry will become true if\n\t *                the returned view was taken from the scrap heap, false if otherwise.\n\t * \n\t * @return A view displaying the data associated with the specified position\n\t */\n\tView obtainView(int position, boolean[] isScrap) {\n\t\tisScrap[0] = false;\n\t\tView scrapView;\n\n\t\tscrapView = mRecycler.getScrapView(position);\n\n\t\tView child;\n\t\tif (scrapView != null) {\n\t\t\tif (ViewDebug.TRACE_RECYCLER) {\n\t\t\t\tViewDebug.trace(scrapView, ViewDebug.RecyclerTraceType.RECYCLE_FROM_SCRAP_HEAP,\n\t\t\t\t\t\tposition, -1);\n\t\t\t}\n\n\t\t\tchild = mAdapter.getView(position, scrapView, this);\n\n\t\t\tif (ViewDebug.TRACE_RECYCLER) {\n\t\t\t\tViewDebug.trace(child, ViewDebug.RecyclerTraceType.BIND_VIEW,\n\t\t\t\t\t\tposition, getChildCount());\n\t\t\t}\n\n\t\t\tif (child != scrapView) {\n\t\t\t\tmRecycler.addScrapView(scrapView);\n\t\t\t\tif (mCacheColorHint != 0) {\n\t\t\t\t\tchild.setDrawingCacheBackgroundColor(mCacheColorHint);\n\t\t\t\t}\n\t\t\t\tif (ViewDebug.TRACE_RECYCLER) {\n\t\t\t\t\tViewDebug.trace(scrapView, ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,\n\t\t\t\t\t\t\tposition, -1);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tisScrap[0] = true;\n\t\t\t\tchild.onFinishTemporaryDetach();\n\t\t\t}\n\t\t} else {\n\t\t\tchild = mAdapter.getView(position, null, this);\n\t\t\tif (mCacheColorHint != 0) {\n\t\t\t\tchild.setDrawingCacheBackgroundColor(mCacheColorHint);\n\t\t\t}\n\t\t\tif (ViewDebug.TRACE_RECYCLER) {\n\t\t\t\tViewDebug.trace(child, ViewDebug.RecyclerTraceType.NEW_VIEW,\n\t\t\t\t\t\tposition, getChildCount());\n\t\t\t}\n\t\t}\n\n\t\treturn child;\n\t}\n\n\tvoid positionSelector(View sel) {\n\t\tfinal Rect selectorRect = mSelectorRect;\n\t\tselectorRect.set(sel.getLeft(), sel.getTop(), sel.getRight(), sel.getBottom());\n\t\tpositionSelector(selectorRect.left, selectorRect.top, selectorRect.right,\n\t\t\t\tselectorRect.bottom);\n\n\t\tfinal boolean isChildViewEnabled = mIsChildViewEnabled;\n\t\tif (sel.isEnabled() != isChildViewEnabled) {\n\t\t\tmIsChildViewEnabled = !isChildViewEnabled;\n\t\t\trefreshDrawableState();\n\t\t}\n\t}\n\n\tprivate void positionSelector(int l, int t, int r, int b) {\n\t\tmSelectorRect.set(l - mSelectionLeftPadding, t - mSelectionTopPadding, r\n\t\t\t\t+ mSelectionRightPadding, b + mSelectionBottomPadding);\n\t}\n\n\t@Override\n\tprotected void dispatchDraw(Canvas canvas) {\n\t\tint saveCount = 0;\n\t\t//TODO????\n\t\t/*\n        final boolean clipToPadding = (mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;\n        if (clipToPadding) {\n            saveCount = canvas.save();\n            final int scrollX = getScrollX();\n            final int scrollY = getScrollY();\n            canvas.clipRect(scrollX + getPaddingLeft(), scrollY + getPaddingTop(),\n                    scrollX + getRight() - getLeft() - getPaddingRight(),\n                    scrollY + getBottom() - getTop() - getPaddingBottom());\n            mGroupFlags &= ~CLIP_TO_PADDING_MASK;\n        }*/\n\n\t\tfinal boolean drawSelectorOnTop = mDrawSelectorOnTop;\n\t\tif (!drawSelectorOnTop) {\n\t\t\tdrawSelector(canvas);\n\t\t}\n\n\t\tsuper.dispatchDraw(canvas);\n\n\t\tif (drawSelectorOnTop) {\n\t\t\tdrawSelector(canvas);\n\t\t}\n\t\t/*\n        if (clipToPadding) {\n            canvas.restoreToCount(saveCount);\n            mGroupFlags |= CLIP_TO_PADDING_MASK;\n        }*/\n\t}\n\n\t@Override\n\tprotected void onSizeChanged(int w, int h, int oldw, int oldh) {\n\t\tif (getChildCount() > 0) {\n\t\t\tmDataChanged = true;\n\t\t\trememberSyncState();\n\t\t}\n\n\t\t//if (mFastScroller != null) {\n\t\t//    mFastScroller.onSizeChanged(w, h, oldw, oldh);\n\t\t//}\n\t}\n\n\t/**\n\t * @return True if the current touch mode requires that we draw the selector in the pressed\n\t *         state.\n\t */\n\tboolean touchModeDrawsInPressedState() {\n\t\t// FIXME use isPressed for this\n\t\tswitch (mTouchMode) {\n\t\tcase TOUCH_MODE_TAP:\n\t\tcase TOUCH_MODE_DONE_WAITING:\n\t\t\treturn true;\n\t\tdefault:\n\t\t\treturn false;\n\t\t}\n\t}\n\n\t/**\n\t * Indicates whether this view is in a state where the selector should be drawn. This will\n\t * happen if we have focus but are not in touch mode, or we are in the middle of displaying\n\t * the pressed state for an item.\n\t *\n\t * @return True if the selector should be shown\n\t */\n\tboolean shouldShowSelector() {\n\t\treturn (hasFocus() && !isInTouchMode()) || touchModeDrawsInPressedState();\n\t}\n\n\tprivate void drawSelector(Canvas canvas) {\n\t\tif (shouldShowSelector() && mSelectorRect != null && !mSelectorRect.isEmpty()) {\n\t\t\tfinal Drawable selector = mSelector;\n\t\t\tselector.setBounds(mSelectorRect);\n\t\t\tselector.draw(canvas);\n\t\t}\n\t}\n\n\t/**\n\t * Controls whether the selection highlight drawable should be drawn on top of the item or\n\t * behind it.\n\t *\n\t * @param onTop If true, the selector will be drawn on the item it is highlighting. The default\n\t *        is false.\n\t *\n\t * @attr ref android.R.styleable#JessAbsListView_drawSelectorOnTop\n\t */\n\tpublic void setDrawSelectorOnTop(boolean onTop) {\n\t\tmDrawSelectorOnTop = onTop;\n\t}\n\n\t/**\n\t * Set a Drawable that should be used to highlight the currently selected item.\n\t *\n\t * @param resID A Drawable resource to use as the selection highlight.\n\t *\n\t * @attr ref android.R.styleable#JessAbsListView_listSelector\n\t */\n\tpublic void setSelector(int resID) {\n\t\tsetSelector(getResources().getDrawable(resID));\n\t}\n\n\tpublic void setSelector(Drawable sel) {\n\t\tif (mSelector != null) {\n\t\t\tmSelector.setCallback(null);\n\t\t\tunscheduleDrawable(mSelector);\n\t\t}\n\t\tmSelector = sel;\n\t\tRect padding = new Rect();\n\t\tsel.getPadding(padding);\n\t\tmSelectionLeftPadding = padding.left;\n\t\tmSelectionTopPadding = padding.top;\n\t\tmSelectionRightPadding = padding.right;\n\t\tmSelectionBottomPadding = padding.bottom;\n\t\tsel.setCallback(this);\n\t\tsel.setState(getDrawableState());\n\t}\n\n\t/**\n\t * Returns the selector {@link android.graphics.drawable.Drawable} that is used to draw the\n\t * selection in the list.\n\t *\n\t * @return the drawable used to display the selector\n\t */\n\tpublic Drawable getSelector() {\n\t\treturn mSelector;\n\t}\n\n\t/**\n\t * Sets the selector state to \"pressed\" and posts a CheckForKeyLongPress to see if\n\t * this is a long press.\n\t */\n\tvoid keyPressed() {\n\t\tif (!isEnabled() || !isClickable()) {\n\t\t\treturn;\n\t\t}\n\n\t\tDrawable selector = mSelector;\n\t\tRect selectorRect = mSelectorRect;\n\t\tif (selector != null && (isFocused() || touchModeDrawsInPressedState())\n\t\t\t\t&& selectorRect != null && !selectorRect.isEmpty()) {\n\n\t\t\tfinal View v = getChildAt(mSelectedPosition - mFirstPosition);\n\n\t\t\tif (v != null) {\n\t\t\t\tif (v.hasFocusable()) return;\n\t\t\t\tv.setPressed(true);\n\t\t\t}\n\t\t\tsetPressed(true);\n\n\t\t\tfinal boolean longClickable = isLongClickable();\n\t\t\tDrawable d = selector.getCurrent();\n\t\t\tif (d != null && d instanceof TransitionDrawable) {\n\t\t\t\tif (longClickable) {\n\t\t\t\t\t((TransitionDrawable) d).startTransition(\n\t\t\t\t\t\t\tViewConfiguration.getLongPressTimeout());\n\t\t\t\t} else {\n\t\t\t\t\t((TransitionDrawable) d).resetTransition();\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (longClickable && !mDataChanged) {\n\t\t\t\tif (mPendingCheckForKeyLongPress == null) {\n\t\t\t\t\tmPendingCheckForKeyLongPress = new CheckForKeyLongPress();\n\t\t\t\t}\n\t\t\t\tmPendingCheckForKeyLongPress.rememberWindowAttachCount();\n\t\t\t\tpostDelayed(mPendingCheckForKeyLongPress, ViewConfiguration.getLongPressTimeout());\n\t\t\t}\n\t\t}\n\t}\n\n\tpublic void setScrollIndicators(View up, View down, View left, View right) {\n\t\tmScrollUp = up;\n\t\tmScrollDown = down;\n\t\tmScrollLeft = left;\n\t\tmScrollRight = right;\n\t}\n\n\t@Override\n\tprotected void drawableStateChanged() {\n\t\tsuper.drawableStateChanged();\n\t\tif (mSelector != null) {\n\t\t\tmSelector.setState(getDrawableState());\n\t\t}\n\t}\n\n\t@Override\n\tprotected int[] onCreateDrawableState(int extraSpace) {\n\t\t// If the child view is enabled then do the default behavior.\n\t\tif (mIsChildViewEnabled) {\n\t\t\t// Common case\n\t\t\treturn super.onCreateDrawableState(extraSpace);\n\t\t}\n\n\t\t// The selector uses this View's drawable state. The selected child view\n\t\t// is disabled, so we need to remove the enabled state from the drawable\n\t\t// states.\n\t\tfinal int enabledState = ENABLED_STATE_SET[0];\n\n\t\t// If we don't have any extra space, it will return one of the static state arrays,\n\t\t// and clearing the enabled state on those arrays is a bad thing!  If we specify\n\t\t// we need extra space, it will create+copy into a new array that safely mutable.\n\t\tint[] state = super.onCreateDrawableState(extraSpace + 1);\n\t\tint enabledPos = -1;\n\t\tfor (int i = state.length - 1; i >= 0; i--) {\n\t\t\tif (state[i] == enabledState) {\n\t\t\t\tenabledPos = i;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t// Remove the enabled state\n\t\tif (enabledPos >= 0) {\n\t\t\tSystem.arraycopy(state, enabledPos + 1, state, enabledPos,\n\t\t\t\t\tstate.length - enabledPos - 1);\n\t\t}\n\n\t\treturn state;\n\t}\n\n\t@Override\n\tpublic boolean verifyDrawable(Drawable dr) {\n\t\treturn mSelector == dr || super.verifyDrawable(dr);\n\t}\n\n\t@Override\n\tprotected void onAttachedToWindow() {\n\t\tsuper.onAttachedToWindow();\n\n\t\tfinal ViewTreeObserver treeObserver = getViewTreeObserver();\n\t\tif (treeObserver != null) {\n\t\t\ttreeObserver.addOnTouchModeChangeListener(this);\n\t\t\t/*\n            if (mTextFilterEnabled && mPopup != null && !mGlobalLayoutListenerAddedFilter) {\n                treeObserver.addOnGlobalLayoutListener(this);\n            }*/\n\t\t}\n\t}\n\n\t@Override\n\tprotected void onDetachedFromWindow() {\n\t\tsuper.onDetachedFromWindow();\n\n\t\t// Dismiss the popup in case onSaveInstanceState() was not invoked\n\t\t//dismissPopup();\n\n\t\t// Detach any view left in the scrap heap\n\t\tmRecycler.clear();\n\n\t\tfinal ViewTreeObserver treeObserver = getViewTreeObserver();\n\t\tif (treeObserver != null) {\n\t\t\ttreeObserver.removeOnTouchModeChangeListener(this);\n\t\t\t/*\n            if (mTextFilterEnabled && mPopup != null) {\n                treeObserver.removeGlobalOnLayoutListener(this);\n                mGlobalLayoutListenerAddedFilter = false;\n            }*/\n\t\t}\n\t}\n\n\n\n\t/**\n\t * Creates the ContextMenuInfo returned from {@link #getContextMenuInfo()}. This\n\t * methods knows the view, position and ID of the item that received the\n\t * long press.\n\t *\n\t * @param view The view that received the long press.\n\t * @param position The position of the item that received the long press.\n\t * @param id The ID of the item that received the long press.\n\t * @return The extra information that should be returned by\n\t *         {@link #getContextMenuInfo()}.\n\t */\n\tContextMenuInfo createContextMenuInfo(View view, int position, long id) {\n\t\treturn new AdapterContextMenuInfo(view, position, id);\n\t}\n\n\t/**\n\t * A base class for Runnables that will check that their view is still attached to\n\t * the original window as when the Runnable was created.\n\t *\n\t */\n\tprivate class WindowRunnnable {\n\t\tprivate int mOriginalAttachCount;\n\n\t\tpublic void rememberWindowAttachCount() {\n\t\t\tmOriginalAttachCount = getWindowAttachCount();\n\t\t}\n\n\t\tpublic boolean sameWindow() {\n\t\t\treturn hasWindowFocus() && getWindowAttachCount() == mOriginalAttachCount;\n\t\t}\n\t}\n\n\tprivate class PerformClick extends WindowRunnnable implements Runnable {\n\t\tView mChild;\n\t\tint mClickMotionPosition;\n\n\t\tpublic void run() {\n\t\t\t// The data has changed since we posted this action in the event queue,\n\t\t\t// bail out before bad things happen\n\t\t\tif (mDataChanged) return;\n\n\t\t\tfinal ListAdapter adapter = mAdapter;\n\t\t\tfinal int motionPosition = mClickMotionPosition;\n\t\t\tif (adapter != null && mItemCount > 0 &&\n\t\t\t\t\tmotionPosition != INVALID_POSITION &&\n\t\t\t\t\tmotionPosition < adapter.getCount() && sameWindow()) {\n\t\t\t\tperformItemClick(mChild, motionPosition, adapter.getItemId(motionPosition));\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate class CheckForLongPress extends WindowRunnnable implements Runnable {\n\t\tpublic void run() {\n\t\t\tfinal int motionPosition = mMotionPosition;\n\t\t\tfinal View child = getChildAt(motionPosition - mFirstPosition);\n\t\t\tif (child != null) {\n\t\t\t\tfinal int longPressPosition = mMotionPosition;\n\t\t\t\tfinal long longPressId = mAdapter.getItemId(mMotionPosition);\n\n\t\t\t\tboolean handled = false;\n\t\t\t\tif (sameWindow() && !mDataChanged) {\n\t\t\t\t\thandled = performLongPress(child, longPressPosition, longPressId);\n\t\t\t\t}\n\t\t\t\tif (handled) {\n\t\t\t\t\tmTouchMode = TOUCH_MODE_REST;\n\t\t\t\t\tsetPressed(false);\n\t\t\t\t\tchild.setPressed(false);\n\t\t\t\t} else {\n\t\t\t\t\tmTouchMode = TOUCH_MODE_DONE_WAITING;\n\t\t\t\t}\n\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate class CheckForKeyLongPress extends WindowRunnnable implements Runnable {\n\t\tpublic void run() {\n\t\t\tif (isPressed() && mSelectedPosition >= 0) {\n\t\t\t\tint index = mSelectedPosition - mFirstPosition;\n\t\t\t\tView v = getChildAt(index);\n\n\t\t\t\tif (!mDataChanged) {\n\t\t\t\t\tboolean handled = false;\n\t\t\t\t\tif (sameWindow()) {\n\t\t\t\t\t\thandled = performLongPress(v, mSelectedPosition, mSelectedRowId);\n\t\t\t\t\t}\n\t\t\t\t\tif (handled) {\n\t\t\t\t\t\tsetPressed(false);\n\t\t\t\t\t\tv.setPressed(false);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tsetPressed(false);\n\t\t\t\t\tif (v != null) v.setPressed(false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate boolean performLongPress(final View child,\n\t\t\tfinal int longPressPosition, final long longPressId) {\n\t\tboolean handled = false;\n\n\t\tif (mOnItemLongClickListener != null) {\n\t\t\thandled = mOnItemLongClickListener.onItemLongClick(TwoWayAbsListView.this, child,\n\t\t\t\t\tlongPressPosition, longPressId);\n\t\t}\n\t\tif (!handled) {\n\t\t\tmContextMenuInfo = createContextMenuInfo(child, longPressPosition, longPressId);\n\t\t\thandled = super.showContextMenuForChild(TwoWayAbsListView.this);\n\t\t}\n\t\tif (handled) {\n\t\t\tperformHapticFeedback(HapticFeedbackConstants.LONG_PRESS);\n\t\t}\n\t\treturn handled;\n\t}\n\n\t@Override\n\tprotected ContextMenuInfo getContextMenuInfo() {\n\t\treturn mContextMenuInfo;\n\t}\n\n\t@Override\n\tpublic boolean showContextMenuForChild(View originalView) {\n\t\tfinal int longPressPosition = getPositionForView(originalView);\n\t\tif (longPressPosition >= 0) {\n\t\t\tfinal long longPressId = mAdapter.getItemId(longPressPosition);\n\t\t\tboolean handled = false;\n\n\t\t\tif (mOnItemLongClickListener != null) {\n\t\t\t\thandled = mOnItemLongClickListener.onItemLongClick(TwoWayAbsListView.this, originalView,\n\t\t\t\t\t\tlongPressPosition, longPressId);\n\t\t\t}\n\t\t\tif (!handled) {\n\t\t\t\tmContextMenuInfo = createContextMenuInfo(\n\t\t\t\t\t\tgetChildAt(longPressPosition - mFirstPosition),\n\t\t\t\t\t\tlongPressPosition, longPressId);\n\t\t\t\thandled = super.showContextMenuForChild(originalView);\n\t\t\t}\n\n\t\t\treturn handled;\n\t\t}\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean onKeyDown(int keyCode, KeyEvent event) {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic boolean onKeyUp(int keyCode, KeyEvent event) {\n\t\tswitch (keyCode) {\n\t\tcase KeyEvent.KEYCODE_DPAD_CENTER:\n\t\tcase KeyEvent.KEYCODE_ENTER:\n\t\t\tif (!isEnabled()) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tif (isClickable() && isPressed() &&\n\t\t\t\t\tmSelectedPosition >= 0 && mAdapter != null &&\n\t\t\t\t\tmSelectedPosition < mAdapter.getCount()) {\n\n\t\t\t\tfinal View view = getChildAt(mSelectedPosition - mFirstPosition);\n\t\t\t\tif (view != null) {\n\t\t\t\t\tperformItemClick(view, mSelectedPosition, mSelectedRowId);\n\t\t\t\t\tview.setPressed(false);\n\t\t\t\t}\n\t\t\t\tsetPressed(false);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\treturn super.onKeyUp(keyCode, event);\n\t}\n\n\t@Override\n\tprotected void dispatchSetPressed(boolean pressed) {\n\t\t// Don't dispatch setPressed to our children. We call setPressed on ourselves to\n\t\t// get the selector in the right state, but we don't want to press each child.\n\t}\n\n\t/**\n\t * Maps a point to a position in the list.\n\t *\n\t * @param x X in local coordinate\n\t * @param y Y in local coordinate\n\t * @return The position of the item which contains the specified point, or\n\t *         {@link #INVALID_POSITION} if the point does not intersect an item.\n\t */\n\tpublic int pointToPosition(int x, int y) {\n\t\tRect frame = mTouchFrame;\n\t\tif (frame == null) {\n\t\t\tmTouchFrame = new Rect();\n\t\t\tframe = mTouchFrame;\n\t\t}\n\n\t\tfinal int count = getChildCount();\n\t\tfor (int i = count - 1; i >= 0; i--) {\n\t\t\tfinal View child = getChildAt(i);\n\t\t\tif (child.getVisibility() == View.VISIBLE) {\n\t\t\t\tchild.getHitRect(frame);\n\t\t\t\tif (frame.contains(x, y)) {\n\t\t\t\t\treturn mFirstPosition + i;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn INVALID_POSITION;\n\t}\n\n\n\t/**\n\t * Maps a point to a the rowId of the item which intersects that point.\n\t *\n\t * @param x X in local coordinate\n\t * @param y Y in local coordinate\n\t * @return The rowId of the item which contains the specified point, or {@link #INVALID_ROW_ID}\n\t *         if the point does not intersect an item.\n\t */\n\tpublic long pointToRowId(int x, int y) {\n\t\tint position = pointToPosition(x, y);\n\t\tif (position >= 0) {\n\t\t\treturn mAdapter.getItemId(position);\n\t\t}\n\t\treturn INVALID_ROW_ID;\n\t}\n\n\tfinal class CheckForTap implements Runnable {\n\t\tpublic void run() {\n\t\t\tif (mTouchMode == TOUCH_MODE_DOWN) {\n\t\t\t\tmTouchMode = TOUCH_MODE_TAP;\n\t\t\t\tfinal View child = getChildAt(mMotionPosition - mFirstPosition);\n\t\t\t\tif (child != null && !child.hasFocusable()) {\n\t\t\t\t\tmLayoutMode = LAYOUT_NORMAL;\n\n\t\t\t\t\tif (!mDataChanged) {\n\t\t\t\t\t\tlayoutChildren();\n\t\t\t\t\t\tchild.setPressed(true);\n\t\t\t\t\t\tpositionSelector(child);\n\t\t\t\t\t\tsetPressed(true);\n\n\t\t\t\t\t\tfinal int longPressTimeout = ViewConfiguration.getLongPressTimeout();\n\t\t\t\t\t\tfinal boolean longClickable = isLongClickable();\n\n\t\t\t\t\t\tif (mSelector != null) {\n\t\t\t\t\t\t\tDrawable d = mSelector.getCurrent();\n\t\t\t\t\t\t\tif (d != null && d instanceof TransitionDrawable) {\n\t\t\t\t\t\t\t\tif (longClickable) {\n\t\t\t\t\t\t\t\t\t((TransitionDrawable) d).startTransition(longPressTimeout);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t((TransitionDrawable) d).resetTransition();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (longClickable) {\n\t\t\t\t\t\t\tif (mPendingCheckForLongPress == null) {\n\t\t\t\t\t\t\t\tmPendingCheckForLongPress = new CheckForLongPress();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tmPendingCheckForLongPress.rememberWindowAttachCount();\n\t\t\t\t\t\t\tpostDelayed(mPendingCheckForLongPress, longPressTimeout);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tmTouchMode = TOUCH_MODE_DONE_WAITING;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmTouchMode = TOUCH_MODE_DONE_WAITING;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\tpublic boolean startScrollIfNeeded(int delta) {\n\t\treturn mTouchHandler.startScrollIfNeeded(delta);\n\t}\n\n\n\tpublic void onTouchModeChanged(boolean isInTouchMode) {\n\t\tmTouchHandler.onTouchModeChanged(isInTouchMode);\n\t}\n\n\t@Override\n\tpublic boolean onTouchEvent(MotionEvent ev) {\n\t\treturn mTouchHandler.onTouchEvent(ev);\n\t}\n\n\t/*\n    @Override\n    public void draw(Canvas canvas) {\n        super.draw(canvas);\n        if (mFastScroller != null) {\n            mFastScroller.draw(canvas);\n        }\n    }*/\n\n\t@Override\n\tpublic boolean onInterceptTouchEvent(MotionEvent ev) {\n\t\treturn mTouchHandler.onInterceptTouchEvent(ev);\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tpublic void addTouchables(ArrayList<View> views) {\n\t\tfinal int count = getChildCount();\n\t\tfinal int firstPosition = mFirstPosition;\n\t\tfinal ListAdapter adapter = mAdapter;\n\n\t\tif (adapter == null) {\n\t\t\treturn;\n\t\t}\n\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tfinal View child = getChildAt(i);\n\t\t\tif (adapter.isEnabled(firstPosition + i)) {\n\t\t\t\tviews.add(child);\n\t\t\t}\n\t\t\tchild.addTouchables(views);\n\t\t}\n\t}\n\n\n\t/**\n\t * Fires an \"on scroll state changed\" event to the registered\n\t * {@link android.widget.AbsListView.OnScrollListener}, if any. The state change\n\t * is fired only if the specified state is different from the previously known state.\n\t *\n\t * @param newState The new scroll state.\n\t */\n\tvoid reportScrollStateChange(int newState) {\n\t\tif (newState != mLastScrollState) {\n\t\t\tif (mOnScrollListener != null) {\n\t\t\t\tmOnScrollListener.onScrollStateChanged(this, newState);\n\t\t\t\tmLastScrollState = newState;\n\t\t\t}\n\t\t}\n\t}\n\n\t@Override\n\tpublic void onWindowFocusChanged(boolean hasWindowFocus) {\n\t\tsuper.onWindowFocusChanged(hasWindowFocus);\n\t\tmTouchHandler.onWindowFocusChanged(hasWindowFocus);\n\t}\n\n\n\t/**\n\t * Smoothly scroll to the specified adapter position. The view will\n\t * scroll such that the indicated position is displayed.\n\t * @param position Scroll to this adapter position.\n\t */\n\tpublic void smoothScrollToPosition(int position) {\n\t\tmTouchHandler.smoothScrollToPosition(position);\n\t}\n\n\t/**\n\t * Smoothly scroll to the specified adapter position. The view will\n\t * scroll such that the indicated position is displayed, but it will\n\t * stop early if scrolling further would scroll boundPosition out of\n\t * view.\n\t * @param position Scroll to this adapter position.\n\t * @param boundPosition Do not scroll if it would move this adapter\n\t *          position out of view.\n\t */\n\tpublic void smoothScrollToPosition(int position, int boundPosition) {\n\t\tmTouchHandler.smoothScrollToPosition(position, boundPosition);\n\t}\n\n\t/**\n\t * Smoothly scroll by distance pixels over duration milliseconds.\n\t * @param distance Distance to scroll in pixels.\n\t * @param duration Duration of the scroll animation in milliseconds.\n\t */\n\tpublic void smoothScrollBy(int distance, int duration) {\n\t\tmTouchHandler.smoothScrollBy(distance, duration);\n\t}\n\n\n\t/**\n\t * Returns the number of header views in the list. Header views are special views\n\t * at the top of the list that should not be recycled during a layout.\n\t *\n\t * @return The number of header views, 0 in the default implementation.\n\t */\n\tint getHeaderViewsCount() {\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Returns the number of footer views in the list. Footer views are special views\n\t * at the bottom of the list that should not be recycled during a layout.\n\t *\n\t * @return The number of footer views, 0 in the default implementation.\n\t */\n\tint getFooterViewsCount() {\n\t\treturn 0;\n\t}\n\n\t/**\n\t * Fills the gap left open by a touch-scroll. During a touch scroll, children that\n\t * remain on screen are shifted and the other ones are discarded. The role of this\n\t * method is to fill the gap thus created by performing a partial layout in the\n\t * empty space.\n\t *\n\t * @param down true if the scroll is going down, false if it is going up\n\t */\n\tabstract void fillGap(boolean down);\n\n\tvoid hideSelector() {\n\t\tif (mSelectedPosition != INVALID_POSITION) {\n\t\t\tif (mLayoutMode != LAYOUT_SPECIFIC) {\n\t\t\t\tmResurrectToPosition = mSelectedPosition;\n\t\t\t}\n\t\t\tif (mNextSelectedPosition >= 0 && mNextSelectedPosition != mSelectedPosition) {\n\t\t\t\tmResurrectToPosition = mNextSelectedPosition;\n\t\t\t}\n\t\t\tsetSelectedPositionInt(INVALID_POSITION);\n\t\t\tsetNextSelectedPositionInt(INVALID_POSITION);\n\t\t\tmSelectedTop = 0;\n\t\t\tmSelectorRect.setEmpty();\n\t\t}\n\t}\n\n\t/**\n\t * @return A position to select. First we try mSelectedPosition. If that has been clobbered by\n\t * entering touch mode, we then try mResurrectToPosition. Values are pinned to the range\n\t * of items available in the adapter\n\t */\n\tint reconcileSelectedPosition() {\n\t\tint position = mSelectedPosition;\n\t\tif (position < 0) {\n\t\t\tposition = mResurrectToPosition;\n\t\t}\n\t\tposition = Math.max(0, position);\n\t\tposition = Math.min(position, mItemCount - 1);\n\t\treturn position;\n\t}\n\n\t/**\n\t * Find the row closest to y. This row will be used as the motion row when scrolling\n\t *\n\t * @param y Where the user touched\n\t * @return The position of the first (or only) item in the row containing y\n\t */\n\tabstract int findMotionRowY(int y);\n\n\t/**\n\t * Find the row closest to y. This row will be used as the motion row when scrolling.\n\t * \n\t * @param y Where the user touched\n\t * @return The position of the first (or only) item in the row closest to y\n\t */\n\tint findClosestMotionRowY(int y) {\n\t\tfinal int childCount = getChildCount();\n\t\tif (childCount == 0) {\n\t\t\treturn INVALID_POSITION;\n\t\t}\n\n\t\tfinal int motionRow = findMotionRowY(y);\n\t\treturn motionRow != INVALID_POSITION ? motionRow : mFirstPosition + childCount - 1;\n\t}\n\n\t/**\n\t * Find the row closest to x. This row will be used as the motion row when scrolling\n\t *\n\t * @param x Where the user touched\n\t * @return The position of the first (or only) item in the row containing y\n\t */\n\tabstract int findMotionRowX(int x);\n\n\t/**\n\t * Find the row closest to y. This row will be used as the motion row when scrolling.\n\t * \n\t * @param x Where the user touched\n\t * @return The position of the first (or only) item in the row closest to y\n\t */\n\tint findClosestMotionRow(int x) {\n\t\tfinal int childCount = getChildCount();\n\t\tif (childCount == 0) {\n\t\t\treturn INVALID_POSITION;\n\t\t}\n\n\t\tfinal int motionRow = findMotionRowX(x);\n\t\treturn motionRow != INVALID_POSITION ? motionRow : mFirstPosition + childCount - 1;\n\t}\n\n\t/**\n\t * Causes all the views to be rebuilt and redrawn.\n\t */\n\tpublic void invalidateViews() {\n\t\tmDataChanged = true;\n\t\trememberSyncState();\n\t\trequestLayout();\n\t\tinvalidate();\n\t}\n\n\t/**\n\t * Makes the item at the supplied position selected.\n\t *\n\t * @param position the position of the new selection\n\t */\n\tabstract void setSelectionInt(int position);\n\n\t/**\n\t * Attempt to bring the selection back if the user is switching from touch\n\t * to trackball mode\n\t * @return Whether selection was set to something.\n\t */\n\tboolean resurrectSelection() {\n\t\treturn mTouchHandler.resurrectSelection();\n\t}\n\n\t@Override\n\tprotected void handleDataChanged() {\n\t\tint count = mItemCount;\n\t\tif (count > 0) {\n\n\t\t\tint newPos;\n\n\t\t\tint selectablePos;\n\n\t\t\t// Find the row we are supposed to sync to\n\t\t\tif (mNeedSync) {\n\t\t\t\t// Update this first, since setNextSelectedPositionInt inspects it\n\t\t\t\tmNeedSync = false;\n\n\t\t\t\tif (mTranscriptMode == TRANSCRIPT_MODE_ALWAYS_SCROLL ||\n\t\t\t\t\t\t(mTranscriptMode == TRANSCRIPT_MODE_NORMAL &&\n\t\t\t\t\t\t\t\tmFirstPosition + getChildCount() >= mOldItemCount)) {\n\t\t\t\t\tmLayoutMode = LAYOUT_FORCE_BOTTOM;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tswitch (mSyncMode) {\n\t\t\t\tcase SYNC_SELECTED_POSITION:\n\t\t\t\t\tif (isInTouchMode()) {\n\t\t\t\t\t\t// We saved our state when not in touch mode. (We know this because\n\t\t\t\t\t\t// mSyncMode is SYNC_SELECTED_POSITION.) Now we are trying to\n\t\t\t\t\t\t// restore in touch mode. Just leave mSyncPosition as it is (possibly\n\t\t\t\t\t\t// adjusting if the available range changed) and return.\n\t\t\t\t\t\tmLayoutMode = LAYOUT_SYNC;\n\t\t\t\t\t\tmSyncPosition = Math.min(Math.max(0, mSyncPosition), count - 1);\n\n\t\t\t\t\t\treturn;\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// See if we can find a position in the new data with the same\n\t\t\t\t\t\t// id as the old selection. This will change mSyncPosition.\n\t\t\t\t\t\tnewPos = findSyncPosition();\n\t\t\t\t\t\tif (newPos >= 0) {\n\t\t\t\t\t\t\t// Found it. Now verify that new selection is still selectable\n\t\t\t\t\t\t\tselectablePos = lookForSelectablePosition(newPos, true);\n\t\t\t\t\t\t\tif (selectablePos == newPos) {\n\t\t\t\t\t\t\t\t// Same row id is selected\n\t\t\t\t\t\t\t\tmSyncPosition = newPos;\n\t\t\t\t\t\t\t\tint size = mIsVertical ? getHeight() : getWidth();\n\t\t\t\t\t\t\t\tif (mSyncSize == size) {\n\t\t\t\t\t\t\t\t\t// If we are at the same height as when we saved state, try\n\t\t\t\t\t\t\t\t\t// to restore the scroll position too.\n\t\t\t\t\t\t\t\t\tmLayoutMode = LAYOUT_SYNC;\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t// We are not the same height as when the selection was saved, so\n\t\t\t\t\t\t\t\t\t// don't try to restore the exact position\n\t\t\t\t\t\t\t\t\tmLayoutMode = LAYOUT_SET_SELECTION;\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t// Restore selection\n\t\t\t\t\t\t\t\tsetNextSelectedPositionInt(newPos);\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\tcase SYNC_FIRST_POSITION:\n\t\t\t\t\t// Leave mSyncPosition as it is -- just pin to available range\n\t\t\t\t\tmLayoutMode = LAYOUT_SYNC;\n\t\t\t\t\tmSyncPosition = Math.min(Math.max(0, mSyncPosition), count - 1);\n\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (!isInTouchMode()) {\n\t\t\t\t// We couldn't find matching data -- try to use the same position\n\t\t\t\tnewPos = getSelectedItemPosition();\n\n\t\t\t\t// Pin position to the available range\n\t\t\t\tif (newPos >= count) {\n\t\t\t\t\tnewPos = count - 1;\n\t\t\t\t}\n\t\t\t\tif (newPos < 0) {\n\t\t\t\t\tnewPos = 0;\n\t\t\t\t}\n\n\t\t\t\t// Make sure we select something selectable -- first look down\n\t\t\t\tselectablePos = lookForSelectablePosition(newPos, true);\n\n\t\t\t\tif (selectablePos >= 0) {\n\t\t\t\t\tsetNextSelectedPositionInt(selectablePos);\n\t\t\t\t\treturn;\n\t\t\t\t} else {\n\t\t\t\t\t// Looking down didn't work -- try looking up\n\t\t\t\t\tselectablePos = lookForSelectablePosition(newPos, false);\n\t\t\t\t\tif (selectablePos >= 0) {\n\t\t\t\t\t\tsetNextSelectedPositionInt(selectablePos);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\n\t\t\t\t// We already know where we want to resurrect the selection\n\t\t\t\tif (mResurrectToPosition >= 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\n\t\t}\n\n\t\t// Nothing is selected. Give up and reset everything.\n\t\tmLayoutMode = mStackFromBottom ? LAYOUT_FORCE_BOTTOM : LAYOUT_FORCE_TOP;\n\t\tmSelectedPosition = INVALID_POSITION;\n\t\tmSelectedRowId = INVALID_ROW_ID;\n\t\tmNextSelectedPosition = INVALID_POSITION;\n\t\tmNextSelectedRowId = INVALID_ROW_ID;\n\t\tmNeedSync = false;\n\t\tcheckSelectionChanged();\n\t}\n\n\n\t//    @Override\n\t//    protected void onDisplayHint(int hint) {\n\t//        super.onDisplayHint(hint);\n\t//        switch (hint) {\n\t//            case INVISIBLE:\n\t//                if (mPopup != null && mPopup.isShowing()) {\n\t//                    dismissPopup();\n\t//                }\n\t//                break;\n\t//            case VISIBLE:\n\t//                if (mFiltered && mPopup != null && !mPopup.isShowing()) {\n\t//                    showPopup();\n\t//                }\n\t//                break;\n\t//        }\n\t//        mPopupHidden = hint == INVISIBLE;\n\t//    }\n\t//\n\t//    /**\n\t//     * Removes the filter window\n\t//     */\n\t//    private void dismissPopup() {\n\t//        if (mPopup != null) {\n\t//            mPopup.dismiss();\n\t//        }\n\t//    }\n\t//\n\t//    /**\n\t//     * Shows the filter window\n\t//     */\n\t//    private void showPopup() {\n\t//        // Make sure we have a window before showing the popup\n\t//        if (getWindowVisibility() == View.VISIBLE) {\n\t//            createTextFilter(true);\n\t//            positionPopup();\n\t//            // Make sure we get focus if we are showing the popup\n\t//            checkFocus();\n\t//        }\n\t//    }\n\t//\n\t//    private void positionPopup() {\n\t//        int screenHeight = getResources().getDisplayMetrics().heightPixels;\n\t//        final int[] xy = new int[2];\n\t//        getLocationOnScreen(xy);\n\t//        // TODO: The 20 below should come from the theme\n\t//        // TODO: And the gravity should be defined in the theme as well\n\t//        final int bottomGap = screenHeight - xy[1] - getHeight() + (int) (mDensityScale * 20);\n\t//        if (!mPopup.isShowing()) {\n\t//            mPopup.showAtLocation(this, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL,\n\t//                    xy[0], bottomGap);\n\t//        } else {\n\t//            mPopup.update(xy[0], bottomGap, -1, -1);\n\t//        }\n\t//    }\n\n\t/**\n\t * What is the distance between the source and destination rectangles given the direction of\n\t * focus navigation between them? The direction basically helps figure out more quickly what is\n\t * self evident by the relationship between the rects...\n\t *\n\t * @param source the source rectangle\n\t * @param dest the destination rectangle\n\t * @param direction the direction\n\t * @return the distance between the rectangles\n\t */\n\tstatic int getDistance(Rect source, Rect dest, int direction) {\n\t\tint sX, sY; // source x, y\n\t\tint dX, dY; // dest x, y\n\t\tswitch (direction) {\n\t\tcase View.FOCUS_RIGHT:\n\t\t\tsX = source.right;\n\t\t\tsY = source.top + source.height() / 2;\n\t\t\tdX = dest.left;\n\t\t\tdY = dest.top + dest.height() / 2;\n\t\t\tbreak;\n\t\tcase View.FOCUS_DOWN:\n\t\t\tsX = source.left + source.width() / 2;\n\t\t\tsY = source.bottom;\n\t\t\tdX = dest.left + dest.width() / 2;\n\t\t\tdY = dest.top;\n\t\t\tbreak;\n\t\tcase View.FOCUS_LEFT:\n\t\t\tsX = source.left;\n\t\t\tsY = source.top + source.height() / 2;\n\t\t\tdX = dest.right;\n\t\t\tdY = dest.top + dest.height() / 2;\n\t\t\tbreak;\n\t\tcase View.FOCUS_UP:\n\t\t\tsX = source.left + source.width() / 2;\n\t\t\tsY = source.top;\n\t\t\tdX = dest.left + dest.width() / 2;\n\t\t\tdY = dest.bottom;\n\t\t\tbreak;\n\t\tcase View.FOCUS_FORWARD:\n\t\tcase View.FOCUS_BACKWARD:\n\t\t\tsX = source.right + source.width() / 2;\n\t\t\tsY = source.top + source.height() / 2;\n\t\t\tdX = dest.left + dest.width() / 2;\n\t\t\tdY = dest.top + dest.height() / 2;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tthrow new IllegalArgumentException(\"direction must be one of \"\n\t\t\t\t+ \"{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT, \"\n\t\t\t\t+ \"FOCUS_FORWARD, FOCUS_BACKWARD}.\");\n\t\t}\n\t\tint deltaX = dX - sX;\n\t\tint deltaY = dY - sY;\n\t\treturn deltaY * deltaY + deltaX * deltaX;\n\t}\n\n\t//    @Override\n\t//    protected boolean isInFilterMode() {\n\t//        return mFiltered;\n\t//    }\n\t//\n\t//    /**\n\t//     * Sends a key to the text filter window\n\t//     *\n\t//     * @param keyCode The keycode for the event\n\t//     * @param event The actual key event\n\t//     *\n\t//     * @return True if the text filter handled the event, false otherwise.\n\t//     */\n\t//    boolean sendToTextFilter(int keyCode, int count, KeyEvent event) {\n\t//        if (!acceptFilter()) {\n\t//            return false;\n\t//        }\n\t//\n\t//        boolean handled = false;\n\t//        boolean okToSend = true;\n\t//        switch (keyCode) {\n\t//        case KeyEvent.KEYCODE_DPAD_UP:\n\t//        case KeyEvent.KEYCODE_DPAD_DOWN:\n\t//        case KeyEvent.KEYCODE_DPAD_LEFT:\n\t//        case KeyEvent.KEYCODE_DPAD_RIGHT:\n\t//        case KeyEvent.KEYCODE_DPAD_CENTER:\n\t//        case KeyEvent.KEYCODE_ENTER:\n\t//            okToSend = false;\n\t//            break;\n\t//        case KeyEvent.KEYCODE_BACK:\n\t//            if (mFiltered && mPopup != null && mPopup.isShowing()) {\n\t//                if (event.getAction() == KeyEvent.ACTION_DOWN\n\t//                        && event.getRepeatCount() == 0) {\n\t//                    getKeyDispatcherState().startTracking(event, this);\n\t//                    handled = true;\n\t//                } else if (event.getAction() == KeyEvent.ACTION_UP\n\t//                        && event.isTracking() && !event.isCanceled()) {\n\t//                    handled = true;\n\t//                    mTextFilter.setText(\"\");\n\t//                }\n\t//            }\n\t//            okToSend = false;\n\t//            break;\n\t//        case KeyEvent.KEYCODE_SPACE:\n\t//            // Only send spaces once we are filtered\n\t//            okToSend = mFiltered;\n\t//            break;\n\t//        }\n\t//\n\t//        if (okToSend) {\n\t//            createTextFilter(true);\n\t//\n\t//            KeyEvent forwardEvent = event;\n\t//            if (forwardEvent.getRepeatCount() > 0) {\n\t//                forwardEvent = KeyEvent.changeTimeRepeat(event, event.getEventTime(), 0);\n\t//            }\n\t//\n\t//            int action = event.getAction();\n\t//            switch (action) {\n\t//                case KeyEvent.ACTION_DOWN:\n\t//                    handled = mTextFilter.onKeyDown(keyCode, forwardEvent);\n\t//                    break;\n\t//\n\t//                case KeyEvent.ACTION_UP:\n\t//                    handled = mTextFilter.onKeyUp(keyCode, forwardEvent);\n\t//                    break;\n\t//\n\t//                case KeyEvent.ACTION_MULTIPLE:\n\t//                    handled = mTextFilter.onKeyMultiple(keyCode, count, event);\n\t//                    break;\n\t//            }\n\t//        }\n\t//        return handled;\n\t//    }\n\t//\n\t//    /**\n\t//     * Return an InputConnection for editing of the filter text.\n\t//     */\n\t//    @Override\n\t//    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {\n\t//        if (isTextFilterEnabled()) {\n\t//            // XXX we need to have the text filter created, so we can get an\n\t//            // InputConnection to proxy to.  Unfortunately this means we pretty\n\t//            // much need to make it as soon as a list view gets focus.\n\t//            createTextFilter(false);\n\t//            if (mPublicInputConnection == null) {\n\t//                mDefInputConnection = new BaseInputConnection(this, false);\n\t//                mPublicInputConnection = new InputConnectionWrapper(\n\t//                        mTextFilter.onCreateInputConnection(outAttrs), true) {\n\t//                    @Override\n\t//                    public boolean reportFullscreenMode(boolean enabled) {\n\t//                        // Use our own input connection, since it is\n\t//                        // the \"real\" one the IME is talking with.\n\t//                        return mDefInputConnection.reportFullscreenMode(enabled);\n\t//                    }\n\t//\n\t//                    @Override\n\t//                    public boolean performEditorAction(int editorAction) {\n\t//                        // The editor is off in its own window; we need to be\n\t//                        // the one that does this.\n\t//                        if (editorAction == EditorInfo.IME_ACTION_DONE) {\n\t//                            InputMethodManager imm = (InputMethodManager)\n\t//                                    getContext().getSystemService(\n\t//                                            Context.INPUT_METHOD_SERVICE);\n\t//                            if (imm != null) {\n\t//                                imm.hideSoftInputFromWindow(getWindowToken(), 0);\n\t//                            }\n\t//                            return true;\n\t//                        }\n\t//                        return false;\n\t//                    }\n\t//\n\t//                    @Override\n\t//                    public boolean sendKeyEvent(KeyEvent event) {\n\t//                        // Use our own input connection, since the filter\n\t//                        // text view may not be shown in a window so has\n\t//                        // no ViewRoot to dispatch events with.\n\t//                        return mDefInputConnection.sendKeyEvent(event);\n\t//                    }\n\t//                };\n\t//            }\n\t//            outAttrs.inputType = EditorInfo.TYPE_CLASS_TEXT\n\t//                    | EditorInfo.TYPE_TEXT_VARIATION_FILTER;\n\t//            outAttrs.imeOptions = EditorInfo.IME_ACTION_DONE;\n\t//            return mPublicInputConnection;\n\t//        }\n\t//        return null;\n\t//    }\n\t//\n\t//    /**\n\t//     * For filtering we proxy an input connection to an internal text editor,\n\t//     * and this allows the proxying to happen.\n\t//     */\n\t//\n\t//    @Override\n\t//    public boolean checkInputConnectionProxy(View view) {\n\t//        return view == mTextFilter;\n\t//    }\n\t//\n\t//    /**\n\t//     * Creates the window for the text filter and populates it with an EditText field;\n\t//     *\n\t//     * @param animateEntrance true if the window should appear with an animation\n\t//     */\n\t//    /*\n\t//    private void createTextFilter(boolean animateEntrance) {\n\t//        if (mPopup == null) {\n\t//            Context c = getContext();\n\t//            PopupWindow p = new PopupWindow(c);\n\t//            LayoutInflater layoutInflater = (LayoutInflater)\n\t//                    c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);\n\t//            mTextFilter = (EditText) layoutInflater.inflate(\n\t//                    android.R.layout.typing_filter, null);\n\t//            // For some reason setting this as the \"real\" input type changes\n\t//            // the text view in some way that it doesn't work, and I don't\n\t//            // want to figure out why this is.\n\t//            mTextFilter.setRawInputType(EditorInfo.TYPE_CLASS_TEXT\n\t//                    | EditorInfo.TYPE_TEXT_VARIATION_FILTER);\n\t//            mTextFilter.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);\n\t//            mTextFilter.addTextChangedListener(this);\n\t//            p.setFocusable(false);\n\t//            p.setTouchable(false);\n\t//            p.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);\n\t//            p.setContentView(mTextFilter);\n\t//            p.setWidth(LayoutParams.WRAP_CONTENT);\n\t//            p.setHeight(LayoutParams.WRAP_CONTENT);\n\t//            p.setBackgroundDrawable(null);\n\t//            mPopup = p;\n\t//            getViewTreeObserver().addOnGlobalLayoutListener(this);\n\t//            mGlobalLayoutListenerAddedFilter = true;\n\t//        }\n\t//        if (animateEntrance) {\n\t//            mPopup.setAnimationStyle(R.style.Animation_TypingFilter);\n\t//        } else {\n\t//            mPopup.setAnimationStyle(R.style.Animation_TypingFilterRestore);\n\t//        }\n\t//    }*/\n\t//\n\t//    /**\n\t//     * Clear the text filter.\n\t//     */\n\t//    /*\n\t//    public void clearTextFilter() {\n\t//        if (mFiltered) {\n\t//            mTextFilter.setText(\"\");\n\t//            mFiltered = false;\n\t//            if (mPopup != null && mPopup.isShowing()) {\n\t//                dismissPopup();\n\t//            }\n\t//        }\n\t//    }*/\n\t//\n\t//    /**\n\t//     * Returns if the ListView currently has a text filter.\n\t//     */\n\t//    public boolean hasTextFilter() {\n\t//        return mFiltered;\n\t//    }\n\t//\n\t//    public void onGlobalLayout() {\n\t//        if (isShown()) {\n\t//            // Show the popup if we are filtered\n\t//            if (mFiltered && mPopup != null && !mPopup.isShowing() && !mPopupHidden) {\n\t//                showPopup();\n\t//            }\n\t//        } else {\n\t//            // Hide the popup when we are no longer visible\n\t//            if (mPopup != null && mPopup.isShowing()) {\n\t//                dismissPopup();\n\t//            }\n\t//        }\n\t//\n\t//    }\n\t//\n\t//    /**\n\t//     * For our text watcher that is associated with the text filter.  Does\n\t//     * nothing.\n\t//     */\n\t//    public void beforeTextChanged(CharSequence s, int start, int count, int after) {\n\t//    }\n\t//\n\t//    /**\n\t//     * For our text watcher that is associated with the text filter. Performs\n\t//     * the actual filtering as the text changes, and takes care of hiding and\n\t//     * showing the popup displaying the currently entered filter text.\n\t//     */\n\t//    public void onTextChanged(CharSequence s, int start, int before, int count) {\n\t//        if (mPopup != null && isTextFilterEnabled()) {\n\t//            int length = s.length();\n\t//            boolean showing = mPopup.isShowing();\n\t//            if (!showing && length > 0) {\n\t//                // Show the filter popup if necessary\n\t//                showPopup();\n\t//                mFiltered = true;\n\t//            } else if (showing && length == 0) {\n\t//                // Remove the filter popup if the user has cleared all text\n\t//                dismissPopup();\n\t//                mFiltered = false;\n\t//            }\n\t//            if (mAdapter instanceof Filterable) {\n\t//                Filter f = ((Filterable) mAdapter).getFilter();\n\t//                // Filter should not be null when we reach this part\n\t//                if (f != null) {\n\t//                    f.filter(s, this);\n\t//                } else {\n\t//                    throw new IllegalStateException(\"You cannot call onTextChanged with a non \"\n\t//                            + \"filterable adapter\");\n\t//                }\n\t//            }\n\t//        }\n\t//    }\n\t//\n\t//    /**\n\t//     * For our text watcher that is associated with the text filter.  Does\n\t//     * nothing.\n\t//     */\n\t//    public void afterTextChanged(Editable s) {\n\t//    }\n\t//\n\t//    public void onFilterComplete(int count) {\n\t//        if (mSelectedPosition < 0 && count > 0) {\n\t//            mResurrectToPosition = INVALID_POSITION;\n\t//            resurrectSelection();\n\t//        }\n\t//    }\n\n\t@Override\n\tprotected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {\n\t\treturn new LayoutParams(p);\n\t}\n\n\t@Override\n\tpublic LayoutParams generateLayoutParams(AttributeSet attrs) {\n\t\treturn new TwoWayAbsListView.LayoutParams(getContext(), attrs);\n\t}\n\n\t@Override\n\tprotected boolean checkLayoutParams(ViewGroup.LayoutParams p) {\n\t\treturn p instanceof TwoWayAbsListView.LayoutParams;\n\t}\n\n\t/**\n\t * Puts the list or grid into transcript mode. In this mode the list or grid will always scroll\n\t * to the bottom to show new items.\n\t *\n\t * @param mode the transcript mode to set\n\t *\n\t * @see #TRANSCRIPT_MODE_DISABLED\n\t * @see #TRANSCRIPT_MODE_NORMAL\n\t * @see #TRANSCRIPT_MODE_ALWAYS_SCROLL\n\t */\n\tpublic void setTranscriptMode(int mode) {\n\t\tmTranscriptMode = mode;\n\t}\n\n\t/**\n\t * Returns the current transcript mode.\n\t *\n\t * @return {@link #TRANSCRIPT_MODE_DISABLED}, {@link #TRANSCRIPT_MODE_NORMAL} or\n\t *         {@link #TRANSCRIPT_MODE_ALWAYS_SCROLL}\n\t */\n\tpublic int getTranscriptMode() {\n\t\treturn mTranscriptMode;\n\t}\n\n\t/**\n\t * Sets the direction that the view schould scroll when in portrait orientation\n\t *\n\t * @param direction the view should scroll\n\t *\n\t * @see #SCROLL_VERTICAL\n\t * @see #SCROLL_HORIZONTAL\n\t */\n\tpublic void setScrollDirectionPortrait(int direction) {\n\t\tboolean tempDirection = mScrollVerticallyPortrait;\n\t\tmScrollVerticallyPortrait = (direction == SCROLL_VERTICAL);\n\t\tif (tempDirection != mScrollVerticallyPortrait) {\n\t\t\tsetupScrollInfo();\n\t\t\t//TODO or requestLayoutIfNecessary()?\n\t\t\tresetList();\n\t\t\tmRecycler.clear();\n\t\t}\n\t}\n\n\t/**\n\t * Returns the current portrait scroll direction.\n\t *\n\t * @return {@link #SCROLL_VERTICAL} or {@link #SCROLL_HORIZONTAL}\n\t */\n\tpublic int getScrollDirectionPortrait() {\n\t\treturn mScrollVerticallyPortrait ? SCROLL_VERTICAL : SCROLL_HORIZONTAL;\n\t}\n\n\t/**\n\t * Sets the direction that the view schould scroll when in landscape orientation\n\t *\n\t * @param direction the view should scroll\n\t *\n\t * @see #SCROLL_VERTICAL\n\t * @see #SCROLL_HORIZONTAL\n\t */\n\tpublic void setScrollDirectionLandscape(int direction) {\n\t\tboolean tempDirection = mScrollVerticallyLandscape;\n\t\tmScrollVerticallyLandscape = (direction == SCROLL_VERTICAL);\n\t\tif (tempDirection != mScrollVerticallyLandscape) {\n\t\t\tsetupScrollInfo();\n\t\t\t//TODO or requestLayoutIfNecessary()?\n\t\t\tresetList();\n\t\t\tmRecycler.clear();\n\t\t}\n\t}\n\n\t/**\n\t * Returns the current landscape scroll direction.\n\t *\n\t * @return {@link #SCROLL_VERTICAL} or {@link #SCROLL_HORIZONTAL}\n\t */\n\tpublic int getScrollDirectionLandscape() {\n\t\treturn mScrollVerticallyLandscape ? SCROLL_VERTICAL : SCROLL_HORIZONTAL;\n\t}\n\n\t@Override\n\tpublic int getSolidColor() {\n\t\treturn mCacheColorHint;\n\t}\n\n\t/**\n\t * When set to a non-zero value, the cache color hint indicates that this list is always drawn\n\t * on top of a solid, single-color, opaque background\n\t *\n\t * @param color The background color\n\t */\n\tpublic void setCacheColorHint(int color) {\n\t\tif (color != mCacheColorHint) {\n\t\t\tmCacheColorHint = color;\n\t\t\tint count = getChildCount();\n\t\t\tfor (int i = 0; i < count; i++) {\n\t\t\t\tgetChildAt(i).setDrawingCacheBackgroundColor(color);\n\t\t\t}\n\t\t\tmRecycler.setCacheColorHint(color);\n\t\t}\n\t}\n\n\t/**\n\t * When set to a non-zero value, the cache color hint indicates that this list is always drawn\n\t * on top of a solid, single-color, opaque background\n\t *\n\t * @return The cache color hint\n\t */\n\tpublic int getCacheColorHint() {\n\t\treturn mCacheColorHint;\n\t}\n\n\t/**\n\t * Move all views (excluding headers and footers) held by this TwoWayAbsListView into the supplied\n\t * List. This includes views displayed on the screen as well as views stored in TwoWayAbsListView's\n\t * internal view recycler.\n\t *\n\t * @param views A list into which to put the reclaimed views\n\t */\n\tpublic void reclaimViews(List<View> views) {\n\t\tint childCount = getChildCount();\n\t\tRecyclerListener listener = mRecycler.mRecyclerListener;\n\n\t\t// Reclaim views on screen\n\t\tfor (int i = 0; i < childCount; i++) {\n\t\t\tView child = getChildAt(i);\n\t\t\tTwoWayAbsListView.LayoutParams lp = (TwoWayAbsListView.LayoutParams) child.getLayoutParams();\n\t\t\t// Don't reclaim header or footer views, or views that should be ignored\n\t\t\tif (lp != null && mRecycler.shouldRecycleViewType(lp.viewType)) {\n\t\t\t\tviews.add(child);\n\t\t\t\tif (listener != null) {\n\t\t\t\t\t// Pretend they went through the scrap heap\n\t\t\t\t\tlistener.onMovedToScrapHeap(child);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tmRecycler.reclaimScrapViews(views);\n\t\tremoveAllViewsInLayout();\n\t}\n\n\t/**\n\t * @hide\n\t */\n\tprotected boolean checkConsistency(int consistency) {\n\t\tboolean result = true;\n\n\t\tfinal boolean checkLayout = true;\n\n\t\tif (checkLayout) {\n\t\t\t// The active recycler must be empty\n\t\t\tfinal View[] activeViews = mRecycler.mActiveViews;\n\t\t\tint count = activeViews.length;\n\t\t\tfor (int i = 0; i < count; i++) {\n\t\t\t\tif (activeViews[i] != null) {\n\t\t\t\t\tresult = false;\n\t\t\t\t\tLog.d(\"Consistency\",\n\t\t\t\t\t\t\t\"AbsListView \" + this + \" has a view in its active recycler: \" +\n\t\t\t\t\t\t\tactiveViews[i]);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// All views in the recycler must NOT be on screen and must NOT have a parent\n\t\t\tfinal ArrayList<View> scrap = mRecycler.mCurrentScrap;\n\t\t\tif (!checkScrap(scrap)) result = false;\n\t\t\tfinal ArrayList<View>[] scraps = mRecycler.mScrapViews;\n\t\t\tcount = scraps.length;\n\t\t\tfor (int i = 0; i < count; i++) {\n\t\t\t\tif (!checkScrap(scraps[i])) result = false;\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\tprivate boolean checkScrap(ArrayList<View> scrap) {\n\t\tif (scrap == null) return true;\n\t\tboolean result = true;\n\n\t\tfinal int count = scrap.size();\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tfinal View view = scrap.get(i);\n\t\t\tif (view.getParent() != null) {\n\t\t\t\tresult = false;\n\t\t\t\tLog.d(\"Consistency\", \"TwoWayAbsListView \" + this +\n\t\t\t\t\t\t\" has a view in its scrap heap still attached to a parent: \" + view);\n\t\t\t}\n\t\t\tif (indexOfChild(view) >= 0) {\n\t\t\t\tresult = false;\n\t\t\t\tLog.d(\"Consistency\", \"TwoWayAbsListView \" + this +\n\t\t\t\t\t\t\" has a view in its scrap heap that is also a direct child: \" + view);\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Sets the recycler listener to be notified whenever a View is set aside in\n\t * the recycler for later reuse. This listener can be used to free resources\n\t * associated to the View.\n\t *\n\t * @param listener The recycler listener to be notified of views set aside\n\t *        in the recycler.\n\t *\n\t * @see com.jess.ui.TwoWayAbsListView.RecycleBin\n\t * @see com.jess.ui.TwoWayAbsListView.RecyclerListener\n\t */\n\tpublic void setRecyclerListener(RecyclerListener listener) {\n\t\tmRecycler.mRecyclerListener = listener;\n\t}\n\n\t/**\n\t * TwoWayAbsListView extends LayoutParams to provide a place to hold the view type.\n\t */\n\tpublic static class LayoutParams extends ViewGroup.LayoutParams {\n\t\t/**\n\t\t * View type for this view, as returned by\n\t\t * {@link android.widget.Adapter#getItemViewType(int) }\n\t\t */\n\t\t@ViewDebug.ExportedProperty(mapping = {\n\t\t\t\t@ViewDebug.IntToString(from = ITEM_VIEW_TYPE_IGNORE, to = \"ITEM_VIEW_TYPE_IGNORE\"),\n\t\t\t\t@ViewDebug.IntToString(from = ITEM_VIEW_TYPE_HEADER_OR_FOOTER, to = \"ITEM_VIEW_TYPE_HEADER_OR_FOOTER\")\n\t\t})\n\t\tint viewType;\n\n\t\t/**\n\t\t * When this boolean is set, the view has been added to the TwoWayAbsListView\n\t\t * at least once. It is used to know whether headers/footers have already\n\t\t * been added to the list view and whether they should be treated as\n\t\t * recycled views or not.\n\t\t */\n\t\t@ViewDebug.ExportedProperty\n\t\tboolean recycledHeaderFooter;\n\n\t\t/**\n\t\t * When an TwoWayAbsListView is measured with an AT_MOST measure spec, it needs\n\t\t * to obtain children views to measure itself. When doing so, the children\n\t\t * are not attached to the window, but put in the recycler which assumes\n\t\t * they've been attached before. Setting this flag will force the reused\n\t\t * view to be attached to the window rather than just attached to the\n\t\t * parent.\n\t\t */\n\t\t@ViewDebug.ExportedProperty\n\t\tboolean forceAdd;\n\n\t\tpublic LayoutParams(Context c, AttributeSet attrs) {\n\t\t\tsuper(c, attrs);\n\t\t}\n\n\t\tpublic LayoutParams(int w, int h) {\n\t\t\tsuper(w, h);\n\t\t}\n\n\t\tpublic LayoutParams(int w, int h, int viewType) {\n\t\t\tsuper(w, h);\n\t\t\tthis.viewType = viewType;\n\t\t}\n\n\t\tpublic LayoutParams(ViewGroup.LayoutParams source) {\n\t\t\tsuper(source);\n\t\t}\n\t}\n\n\t/**\n\t * A RecyclerListener is used to receive a notification whenever a View is placed\n\t * inside the RecycleBin's scrap heap. This listener is used to free resources\n\t * associated to Views placed in the RecycleBin.\n\t *\n\t * @see com.jess.ui.TwoWayAbsListView.RecycleBin\n\t * @see com.jess.ui.TwoWayAbsListView#setRecyclerListener(com.jess.ui.TwoWayAbsListView.RecyclerListener)\n\t */\n\tpublic static interface RecyclerListener {\n\t\t/**\n\t\t * Indicates that the specified View was moved into the recycler's scrap heap.\n\t\t * The view is not displayed on screen any more and any expensive resource\n\t\t * associated with the view should be discarded.\n\t\t *\n\t\t * @param view\n\t\t */\n\t\tvoid onMovedToScrapHeap(View view);\n\t}\n\n\t/**\n\t * The RecycleBin facilitates reuse of views across layouts. The RecycleBin has two levels of\n\t * storage: ActiveViews and ScrapViews. ActiveViews are those views which were onscreen at the\n\t * start of a layout. By construction, they are displaying current information. At the end of\n\t * layout, all views in ActiveViews are demoted to ScrapViews. ScrapViews are old views that\n\t * could potentially be used by the adapter to avoid allocating views unnecessarily.\n\t *\n\t * @see com.jess.ui.TwoWayAbsListView#setRecyclerListener(com.jess.ui.TwoWayAbsListView.RecyclerListener)\n\t * @see com.jess.ui.TwoWayAbsListView.RecyclerListener\n\t */\n\tclass RecycleBin {\n\t\tprivate RecyclerListener mRecyclerListener;\n\n\t\t/**\n\t\t * The position of the first view stored in mActiveViews.\n\t\t */\n\t\tprivate int mFirstActivePosition;\n\n\t\t/**\n\t\t * Views that were on screen at the start of layout. This array is populated at the start of\n\t\t * layout, and at the end of layout all view in mActiveViews are moved to mScrapViews.\n\t\t * Views in mActiveViews represent a contiguous range of Views, with position of the first\n\t\t * view store in mFirstActivePosition.\n\t\t */\n\t\tprivate View[] mActiveViews = new View[0];\n\n\t\t/**\n\t\t * Unsorted views that can be used by the adapter as a convert view.\n\t\t */\n\t\tprivate ArrayList<View>[] mScrapViews;\n\n\t\tprivate int mViewTypeCount;\n\n\t\tprivate ArrayList<View> mCurrentScrap;\n\n\t\tpublic void setViewTypeCount(int viewTypeCount) {\n\t\t\tif (viewTypeCount < 1) {\n\t\t\t\tthrow new IllegalArgumentException(\"Can't have a viewTypeCount < 1\");\n\t\t\t}\n\t\t\t//noinspection unchecked\n\t\t\tArrayList<View>[] scrapViews = new ArrayList[viewTypeCount];\n\t\t\tfor (int i = 0; i < viewTypeCount; i++) {\n\t\t\t\tscrapViews[i] = new ArrayList<View>();\n\t\t\t}\n\t\t\tmViewTypeCount = viewTypeCount;\n\t\t\tmCurrentScrap = scrapViews[0];\n\t\t\tmScrapViews = scrapViews;\n\t\t}\n\n\t\tpublic void markChildrenDirty() {\n\t\t\tif (mViewTypeCount == 1) {\n\t\t\t\tfinal ArrayList<View> scrap = mCurrentScrap;\n\t\t\t\tfinal int scrapCount = scrap.size();\n\t\t\t\tfor (int i = 0; i < scrapCount; i++) {\n\t\t\t\t\tscrap.get(i).forceLayout();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfinal int typeCount = mViewTypeCount;\n\t\t\t\tfor (int i = 0; i < typeCount; i++) {\n\t\t\t\t\tfinal ArrayList<View> scrap = mScrapViews[i];\n\t\t\t\t\tfinal int scrapCount = scrap.size();\n\t\t\t\t\tfor (int j = 0; j < scrapCount; j++) {\n\t\t\t\t\t\tscrap.get(j).forceLayout();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tpublic boolean shouldRecycleViewType(int viewType) {\n\t\t\treturn viewType >= 0;\n\t\t}\n\n\t\t/**\n\t\t * Clears the scrap heap.\n\t\t */\n\t\tvoid clear() {\n\t\t\tif (mViewTypeCount == 1) {\n\t\t\t\tfinal ArrayList<View> scrap = mCurrentScrap;\n\t\t\t\tfinal int scrapCount = scrap.size();\n\t\t\t\tfor (int i = 0; i < scrapCount; i++) {\n\t\t\t\t\tremoveDetachedView(scrap.remove(scrapCount - 1 - i), false);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfinal int typeCount = mViewTypeCount;\n\t\t\t\tfor (int i = 0; i < typeCount; i++) {\n\t\t\t\t\tfinal ArrayList<View> scrap = mScrapViews[i];\n\t\t\t\t\tfinal int scrapCount = scrap.size();\n\t\t\t\t\tfor (int j = 0; j < scrapCount; j++) {\n\t\t\t\t\t\tremoveDetachedView(scrap.remove(scrapCount - 1 - j), false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Fill ActiveViews with all of the children of the TwoWayAbsListView.\n\t\t *\n\t\t * @param childCount The minimum number of views mActiveViews should hold\n\t\t * @param firstActivePosition The position of the first view that will be stored in\n\t\t *        mActiveViews\n\t\t */\n\t\tvoid fillActiveViews(int childCount, int firstActivePosition) {\n\t\t\tif (mActiveViews.length < childCount) {\n\t\t\t\tmActiveViews = new View[childCount];\n\t\t\t}\n\t\t\tmFirstActivePosition = firstActivePosition;\n\n\t\t\tfinal View[] activeViews = mActiveViews;\n\t\t\tfor (int i = 0; i < childCount; i++) {\n\t\t\t\tView child = getChildAt(i);\n\t\t\t\tTwoWayAbsListView.LayoutParams lp = (TwoWayAbsListView.LayoutParams) child.getLayoutParams();\n\t\t\t\t// Don't put header or footer views into the scrap heap\n\t\t\t\tif (lp != null && lp.viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {\n\t\t\t\t\t// Note:  We do place TwoWayAdapterView.ITEM_VIEW_TYPE_IGNORE in active views.\n\t\t\t\t\t//        However, we will NOT place them into scrap views.\n\t\t\t\t\tactiveViews[i] = child;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Get the view corresponding to the specified position. The view will be removed from\n\t\t * mActiveViews if it is found.\n\t\t *\n\t\t * @param position The position to look up in mActiveViews\n\t\t * @return The view if it is found, null otherwise\n\t\t */\n\t\tView getActiveView(int position) {\n\t\t\tint index = position - mFirstActivePosition;\n\t\t\tfinal View[] activeViews = mActiveViews;\n\t\t\tif (index >=0 && index < activeViews.length) {\n\t\t\t\tfinal View match = activeViews[index];\n\t\t\t\tactiveViews[index] = null;\n\t\t\t\treturn match;\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t/**\n\t\t * @return A view from the ScrapViews collection. These are unordered.\n\t\t */\n\t\tView getScrapView(int position) {\n\t\t\tArrayList<View> scrapViews;\n\t\t\tif (mViewTypeCount == 1) {\n\t\t\t\tscrapViews = mCurrentScrap;\n\t\t\t\tint size = scrapViews.size();\n\t\t\t\tif (size > 0) {\n\t\t\t\t\treturn scrapViews.remove(size - 1);\n\t\t\t\t} else {\n\t\t\t\t\treturn null;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tint whichScrap = mAdapter.getItemViewType(position);\n\t\t\t\tif (whichScrap >= 0 && whichScrap < mScrapViews.length) {\n\t\t\t\t\tscrapViews = mScrapViews[whichScrap];\n\t\t\t\t\tint size = scrapViews.size();\n\t\t\t\t\tif (size > 0) {\n\t\t\t\t\t\treturn scrapViews.remove(size - 1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn null;\n\t\t}\n\n\t\t/**\n\t\t * Put a view into the ScapViews list. These views are unordered.\n\t\t *\n\t\t * @param scrap The view to add\n\t\t */\n\t\tvoid addScrapView(View scrap) {\n\t\t\tTwoWayAbsListView.LayoutParams lp = (TwoWayAbsListView.LayoutParams) scrap.getLayoutParams();\n\t\t\tif (lp == null) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Don't put header or footer views or views that should be ignored\n\t\t\t// into the scrap heap\n\t\t\tint viewType = lp.viewType;\n\t\t\tif (!shouldRecycleViewType(viewType)) {\n\t\t\t\tif (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {\n\t\t\t\t\tremoveDetachedView(scrap, false);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (mViewTypeCount == 1) {\n\t\t\t\tscrap.onStartTemporaryDetach();\n\t\t\t\tmCurrentScrap.add(scrap);\n\t\t\t} else {\n\t\t\t\tscrap.onStartTemporaryDetach();\n\t\t\t\tmScrapViews[viewType].add(scrap);\n\t\t\t}\n\n\t\t\tif (mRecyclerListener != null) {\n\t\t\t\tmRecyclerListener.onMovedToScrapHeap(scrap);\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Move all views remaining in mActiveViews to mScrapViews.\n\t\t */\n\t\tvoid scrapActiveViews() {\n\t\t\tfinal View[] activeViews = mActiveViews;\n\t\t\tfinal boolean hasListener = mRecyclerListener != null;\n\t\t\tfinal boolean multipleScraps = mViewTypeCount > 1;\n\n\t\t\tArrayList<View> scrapViews = mCurrentScrap;\n\t\t\tfinal int count = activeViews.length;\n\t\t\tfor (int i = count - 1; i >= 0; i--) {\n\t\t\t\tfinal View victim = activeViews[i];\n\t\t\t\tif (victim != null) {\n\t\t\t\t\tint whichScrap = ((TwoWayAbsListView.LayoutParams) victim.getLayoutParams()).viewType;\n\n\t\t\t\t\tactiveViews[i] = null;\n\n\t\t\t\t\tif (!shouldRecycleViewType(whichScrap)) {\n\t\t\t\t\t\t// Do not move views that should be ignored\n\t\t\t\t\t\tif (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) {\n\t\t\t\t\t\t\tremoveDetachedView(victim, false);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (multipleScraps) {\n\t\t\t\t\t\tscrapViews = mScrapViews[whichScrap];\n\t\t\t\t\t}\n\t\t\t\t\tvictim.onStartTemporaryDetach();\n\t\t\t\t\tscrapViews.add(victim);\n\n\t\t\t\t\tif (hasListener) {\n\t\t\t\t\t\tmRecyclerListener.onMovedToScrapHeap(victim);\n\t\t\t\t\t}\n\n\t\t\t\t\tif (ViewDebug.TRACE_RECYCLER) {\n\t\t\t\t\t\tViewDebug.trace(victim,\n\t\t\t\t\t\t\t\tViewDebug.RecyclerTraceType.MOVE_FROM_ACTIVE_TO_SCRAP_HEAP,\n\t\t\t\t\t\t\t\tmFirstActivePosition + i, -1);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpruneScrapViews();\n\t\t}\n\n\t\t/**\n\t\t * Makes sure that the size of mScrapViews does not exceed the size of mActiveViews.\n\t\t * (This can happen if an adapter does not recycle its views).\n\t\t */\n\t\tprivate void pruneScrapViews() {\n\t\t\tfinal int maxViews = mActiveViews.length;\n\t\t\tfinal int viewTypeCount = mViewTypeCount;\n\t\t\tfinal ArrayList<View>[] scrapViews = mScrapViews;\n\t\t\tfor (int i = 0; i < viewTypeCount; ++i) {\n\t\t\t\tfinal ArrayList<View> scrapPile = scrapViews[i];\n\t\t\t\tint size = scrapPile.size();\n\t\t\t\tfinal int extras = size - maxViews;\n\t\t\t\tsize--;\n\t\t\t\tfor (int j = 0; j < extras; j++) {\n\t\t\t\t\tremoveDetachedView(scrapPile.remove(size--), false);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Puts all views in the scrap heap into the supplied list.\n\t\t */\n\t\tvoid reclaimScrapViews(List<View> views) {\n\t\t\tif (mViewTypeCount == 1) {\n\t\t\t\tviews.addAll(mCurrentScrap);\n\t\t\t} else {\n\t\t\t\tfinal int viewTypeCount = mViewTypeCount;\n\t\t\t\tfinal ArrayList<View>[] scrapViews = mScrapViews;\n\t\t\t\tfor (int i = 0; i < viewTypeCount; ++i) {\n\t\t\t\t\tfinal ArrayList<View> scrapPile = scrapViews[i];\n\t\t\t\t\tviews.addAll(scrapPile);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Updates the cache color hint of all known views.\n\t\t *\n\t\t * @param color The new cache color hint.\n\t\t */\n\t\tvoid setCacheColorHint(int color) {\n\t\t\tif (mViewTypeCount == 1) {\n\t\t\t\tfinal ArrayList<View> scrap = mCurrentScrap;\n\t\t\t\tfinal int scrapCount = scrap.size();\n\t\t\t\tfor (int i = 0; i < scrapCount; i++) {\n\t\t\t\t\tscrap.get(i).setDrawingCacheBackgroundColor(color);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfinal int typeCount = mViewTypeCount;\n\t\t\t\tfor (int i = 0; i < typeCount; i++) {\n\t\t\t\t\tfinal ArrayList<View> scrap = mScrapViews[i];\n\t\t\t\t\tfinal int scrapCount = scrap.size();\n\t\t\t\t\tfor (int j = 0; j < scrapCount; j++) {\n\t\t\t\t\t\tscrap.get(i).setDrawingCacheBackgroundColor(color);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Just in case this is called during a layout pass\n\t\t\tfinal View[] activeViews = mActiveViews;\n\t\t\tfinal int count = activeViews.length;\n\t\t\tfor (int i = 0; i < count; ++i) {\n\t\t\t\tfinal View victim = activeViews[i];\n\t\t\t\tif (victim != null) {\n\t\t\t\t\tvictim.setDrawingCacheBackgroundColor(color);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\n\n\n\t////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\t//  Touch Handler\n\t////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n\tabstract class TouchHandler {\n\t\t/**\n\t\t * Handles scrolling between positions within the list.\n\t\t */\n\t\tprotected PositionScroller mPositionScroller;\n\n\t\t/**\n\t\t * Handles one frame of a fling\n\t\t */\n\t\tprotected FlingRunnable mFlingRunnable;\n\n\t\t/**\n\t\t * How far the finger moved before we started scrolling\n\t\t */\n\t\tint mMotionCorrection;\n\n\t\tpublic void onWindowFocusChanged(boolean hasWindowFocus) {\n\n\t\t\tfinal int touchMode = isInTouchMode() ? TOUCH_MODE_ON : TOUCH_MODE_OFF;\n\n\t\t\tif (!hasWindowFocus) {\n\t\t\t\tsetChildrenDrawingCacheEnabled(false);\n\t\t\t\tif (mFlingRunnable != null) {\n\t\t\t\t\tremoveCallbacks(mFlingRunnable);\n\t\t\t\t\t// let the fling runnable report it's new state which\n\t\t\t\t\t// should be idle\n\t\t\t\t\tmFlingRunnable.endFling();\n\t\t\t\t\t//TODO: this doesn't seem the right way to do this.\n\t\t\t\t\tif (getScrollY() != 0) {\n\t\t\t\t\t\tscrollTo(getScrollX(), 0);\n\t\t\t\t\t\t//mScrollY = 0;\n\t\t\t\t\t\tinvalidate();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Always hide the type filter\n\t\t\t\t//dismissPopup();\n\n\t\t\t\tif (touchMode == TOUCH_MODE_OFF) {\n\t\t\t\t\t// Remember the last selected element\n\t\t\t\t\tmResurrectToPosition = mSelectedPosition;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t//if (mFiltered && !mPopupHidden) {\n\t\t\t\t// Show the type filter only if a filter is in effect\n\t\t\t\t//    showPopup();\n\t\t\t\t//}\n\n\t\t\t\t// If we changed touch mode since the last time we had focus\n\t\t\t\tif (touchMode != mLastTouchMode && mLastTouchMode != TOUCH_MODE_UNKNOWN) {\n\t\t\t\t\t// If we come back in trackball mode, we bring the selection back\n\t\t\t\t\tif (touchMode == TOUCH_MODE_OFF) {\n\t\t\t\t\t\t// This will trigger a layout\n\t\t\t\t\t\tresurrectSelection();\n\n\t\t\t\t\t\t// If we come back in touch mode, then we want to hide the selector\n\t\t\t\t\t} else {\n\t\t\t\t\t\thideSelector();\n\t\t\t\t\t\tmLayoutMode = LAYOUT_NORMAL;\n\t\t\t\t\t\tlayoutChildren();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmLastTouchMode = touchMode;\n\t\t}\n\n\n\t\tpublic boolean startScrollIfNeeded(int delta) {\n\t\t\t// Check if we have moved far enough that it looks more like a\n\t\t\t// scroll than a tap\n\t\t\tfinal int distance = Math.abs(delta);\n\t\t\tif (distance > mTouchSlop) {\n\t\t\t\tcreateScrollingCache();\n\t\t\t\tmTouchMode = TOUCH_MODE_SCROLL;\n\t\t\t\tmMotionCorrection = delta;\n\t\t\t\tfinal Handler handler = getHandler();\n\t\t\t\t// Handler should not be null unless the TwoWayAbsListView is not attached to a\n\t\t\t\t// window, which would make it very hard to scroll it... but the monkeys\n\t\t\t\t// say it's possible.\n\t\t\t\tif (handler != null) {\n\t\t\t\t\thandler.removeCallbacks(mPendingCheckForLongPress);\n\t\t\t\t}\n\t\t\t\tsetPressed(false);\n\t\t\t\tView motionView = getChildAt(mMotionPosition - mFirstPosition);\n\t\t\t\tif (motionView != null) {\n\t\t\t\t\tmotionView.setPressed(false);\n\t\t\t\t}\n\t\t\t\treportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);\n\t\t\t\t// Time to start stealing events! Once we've stolen them, don't let anyone\n\t\t\t\t// steal from us\n\t\t\t\trequestDisallowInterceptTouchEvent(true);\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\n\t\tpublic void onTouchModeChanged(boolean isInTouchMode) {\n\t\t\tif (isInTouchMode) {\n\t\t\t\t// Get rid of the selection when we enter touch mode\n\t\t\t\thideSelector();\n\t\t\t\t// Layout, but only if we already have done so previously.\n\t\t\t\t// (Otherwise may clobber a LAYOUT_SYNC layout that was requested to restore\n\t\t\t\t// state.)\n\t\t\t\tif (getHeight() > 0 && getChildCount() > 0) {\n\t\t\t\t\t// We do not lose focus initiating a touch (since TwoWayAbsListView is focusable in\n\t\t\t\t\t// touch mode). Force an initial layout to get rid of the selection.\n\t\t\t\t\tlayoutChildren();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Fires an \"on scroll state changed\" event to the registered\n\t\t * {@link com.jess.ui.TwoWayAbsListView.OnScrollListener}, if any. The state change\n\t\t * is fired only if the specified state is different from the previously known state.\n\t\t *\n\t\t * @param newState The new scroll state.\n\t\t */\n\t\tvoid reportScrollStateChange(int newState) {\n\t\t\tif (newState != mLastScrollState) {\n\t\t\t\tif (mOnScrollListener != null) {\n\t\t\t\t\tmOnScrollListener.onScrollStateChanged(TwoWayAbsListView.this, newState);\n\t\t\t\t\tmLastScrollState = newState;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Smoothly scroll to the specified adapter position. The view will\n\t\t * scroll such that the indicated position is displayed.\n\t\t * @param position Scroll to this adapter position.\n\t\t */\n\t\tpublic void smoothScrollToPosition(int position) {\n\t\t\tif (mPositionScroller == null) {\n\t\t\t\tmPositionScroller = getPositionScroller();\n\t\t\t}\n\t\t\tmPositionScroller.start(position);\n\t\t}\n\n\t\t/**\n\t\t * Smoothly scroll to the specified adapter position. The view will\n\t\t * scroll such that the indicated position is displayed, but it will\n\t\t * stop early if scrolling further would scroll boundPosition out of\n\t\t * view.\n\t\t * @param position Scroll to this adapter position.\n\t\t * @param boundPosition Do not scroll if it would move this adapter\n\t\t *          position out of view.\n\t\t */\n\t\tpublic void smoothScrollToPosition(int position, int boundPosition) {\n\t\t\tif (mPositionScroller == null) {\n\t\t\t\tmPositionScroller = getPositionScroller();\n\t\t\t}\n\t\t\tmPositionScroller.start(position, boundPosition);\n\t\t}\n\n\t\t/**\n\t\t * Smoothly scroll by distance pixels over duration milliseconds.\n\t\t * @param distance Distance to scroll in pixels.\n\t\t * @param duration Duration of the scroll animation in milliseconds.\n\t\t */\n\t\tpublic void smoothScrollBy(int distance, int duration) {\n\t\t\tif (mFlingRunnable == null) {\n\t\t\t\tmFlingRunnable = getFlingRunnable();\n\t\t\t} else {\n\t\t\t\tmFlingRunnable.endFling();\n\t\t\t}\n\t\t\tmFlingRunnable.startScroll(distance, duration);\n\t\t}\n\n\t\tprotected void createScrollingCache() {\n\t\t\tif (mScrollingCacheEnabled && !mCachingStarted) {\n\t\t\t\tsetChildrenDrawnWithCacheEnabled(true);\n\t\t\t\tsetChildrenDrawingCacheEnabled(true);\n\t\t\t\tmCachingStarted = true;\n\t\t\t}\n\t\t}\n\n\t\tprotected void clearScrollingCache() {\n\t\t\tif (mClearScrollingCache == null) {\n\t\t\t\tmClearScrollingCache = new Runnable() {\n\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\tif (mCachingStarted) {\n\t\t\t\t\t\t\tmCachingStarted = false;\n\t\t\t\t\t\t\tsetChildrenDrawnWithCacheEnabled(false);\n\t\t\t\t\t\t\tif ((TwoWayAbsListView.this.getPersistentDrawingCache() & PERSISTENT_SCROLLING_CACHE) == 0) {\n\t\t\t\t\t\t\t\tsetChildrenDrawingCacheEnabled(false);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tif (!isAlwaysDrawnWithCacheEnabled()) {\n\t\t\t\t\t\t\t\tinvalidate();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t}\n\t\t\tpost(mClearScrollingCache);\n\t\t}\n\n\t\t/**\n\t\t * Track a motion scroll\n\t\t *\n\t\t * @param delta Amount to offset mMotionView. This is the accumulated delta since the motion\n\t\t *        began. Positive numbers mean the user's finger is moving down or right on the screen.\n\t\t * @param incrementalDelta Change in delta from the previous event.\n\t\t * @return true if we're already at the beginning/end of the list and have nothing to do.\n\t\t */\n\t\tabstract boolean trackMotionScroll(int delta, int incrementalDelta);\n\n\t\t/**\n\t\t * Attempt to bring the selection back if the user is switching from touch\n\t\t * to trackball mode\n\t\t * @return Whether selection was set to something.\n\t\t */\n\t\tabstract boolean resurrectSelection();\n\n\n\t\tpublic abstract boolean onTouchEvent(MotionEvent ev);\n\n\n\t\tpublic abstract boolean onInterceptTouchEvent(MotionEvent ev);\n\n\t\tprotected abstract PositionScroller getPositionScroller();\n\n\t\tprotected abstract FlingRunnable getFlingRunnable();\n\n\t\t/**\n\t\t * Responsible for fling behavior. Use {@link #start(int)} to\n\t\t * initiate a fling. Each frame of the fling is handled in {@link #run()}.\n\t\t * A FlingRunnable will keep re-posting itself until the fling is done.\n\t\t *\n\t\t */\n\t\tprotected abstract class FlingRunnable implements Runnable {\n\t\t\t/**\n\t\t\t * Tracks the decay of a fling scroll\n\t\t\t */\n\t\t\tprotected final Scroller mScroller;\n\n\t\t\tprotected Runnable mCheckFlywheel;\n\t\t\t\n\t\t\tpublic boolean isScrollingInDirection(float xvel, float yvel) {\n\t\t\t\tfinal int dx = mScroller.getFinalX() - mScroller.getStartX();\n\t\t\t\tfinal int dy = mScroller.getFinalY() - mScroller.getStartY();\n\t\t\t\treturn !mScroller.isFinished() && Math.signum(xvel) == Math.signum(dx) &&\n\t\t\t\t\t\tMath.signum(yvel) == Math.signum(dy);\n\t\t\t}\n\n\n\t\t\tFlingRunnable() {\n\t\t\t\tmScroller = new Scroller(getContext());\n\t\t\t}\n\n\t\t\tabstract void flywheelTouch();\n\n\t\t\tabstract void start(int initialVelocity);\n\n\t\t\tabstract void startScroll(int distance, int duration);\n\n\t\t\tprotected void endFling() {\n\t\t\t\tmTouchMode = TOUCH_MODE_REST;\n\n\t\t\t\treportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);\n\t\t\t\tclearScrollingCache();\n\n\t\t\t\tremoveCallbacks(this);\n\t\t\t\t\n\t\t\t\tif (mCheckFlywheel != null) {\n\t\t\t\t\tremoveCallbacks(mCheckFlywheel);\n\t\t\t\t}\n\t\t\t\tif (mPositionScroller != null) {\n\t\t\t\t\tremoveCallbacks(mPositionScroller);\n\t\t\t\t}\n\n\t\t\t\tmScroller.abortAnimation();\n\t\t\t}\n\n\t\t\tpublic abstract void run();\n\t\t}\n\n\t\tabstract class PositionScroller implements Runnable {\n\t\t\tprotected static final int SCROLL_DURATION = 400;\n\n\t\t\tprotected static final int MOVE_DOWN_POS = 1;\n\t\t\tprotected static final int MOVE_UP_POS = 2;\n\t\t\tprotected static final int MOVE_DOWN_BOUND = 3;\n\t\t\tprotected static final int MOVE_UP_BOUND = 4;\n\n\t\t\tprotected boolean mVertical;\n\t\t\tprotected int mMode;\n\t\t\tprotected int mTargetPos;\n\t\t\tprotected int mBoundPos;\n\t\t\tprotected int mLastSeenPos;\n\t\t\tprotected int mScrollDuration;\n\t\t\tprotected final int mExtraScroll;\n\n\t\t\tPositionScroller() {\n\t\t\t\tmExtraScroll = ViewConfiguration.get(mContext).getScaledFadingEdgeLength();\n\t\t\t}\n\n\t\t\tvoid start(int position) {\n\t\t\t\tfinal int firstPos = mFirstPosition;\n\t\t\t\tfinal int lastPos = firstPos + getChildCount() - 1;\n\n\t\t\t\tint viewTravelCount = 0;\n\t\t\t\tif (position <= firstPos) {\n\t\t\t\t\tviewTravelCount = firstPos - position + 1;\n\t\t\t\t\tmMode = MOVE_UP_POS;\n\t\t\t\t} else if (position >= lastPos) {\n\t\t\t\t\tviewTravelCount = position - lastPos + 1;\n\t\t\t\t\tmMode = MOVE_DOWN_POS;\n\t\t\t\t} else {\n\t\t\t\t\t// Already on screen, nothing to do\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (viewTravelCount > 0) {\n\t\t\t\t\tmScrollDuration = SCROLL_DURATION / viewTravelCount;\n\t\t\t\t} else {\n\t\t\t\t\tmScrollDuration = SCROLL_DURATION;\n\t\t\t\t}\n\t\t\t\tmTargetPos = position;\n\t\t\t\tmBoundPos = INVALID_POSITION;\n\t\t\t\tmLastSeenPos = INVALID_POSITION;\n\n\t\t\t\tpost(this);\n\t\t\t}\n\n\t\t\tvoid start(int position, int boundPosition) {\n\t\t\t\tif (boundPosition == INVALID_POSITION) {\n\t\t\t\t\tstart(position);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tfinal int firstPos = mFirstPosition;\n\t\t\t\tfinal int lastPos = firstPos + getChildCount() - 1;\n\n\t\t\t\tint viewTravelCount = 0;\n\t\t\t\tif (position <= firstPos) {\n\t\t\t\t\tfinal int boundPosFromLast = lastPos - boundPosition;\n\t\t\t\t\tif (boundPosFromLast < 1) {\n\t\t\t\t\t\t// Moving would shift our bound position off the screen. Abort.\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tfinal int posTravel = firstPos - position + 1;\n\t\t\t\t\tfinal int boundTravel = boundPosFromLast - 1;\n\t\t\t\t\tif (boundTravel < posTravel) {\n\t\t\t\t\t\tviewTravelCount = boundTravel;\n\t\t\t\t\t\tmMode = MOVE_UP_BOUND;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tviewTravelCount = posTravel;\n\t\t\t\t\t\tmMode = MOVE_UP_POS;\n\t\t\t\t\t}\n\t\t\t\t} else if (position >= lastPos) {\n\t\t\t\t\tfinal int boundPosFromFirst = boundPosition - firstPos;\n\t\t\t\t\tif (boundPosFromFirst < 1) {\n\t\t\t\t\t\t// Moving would shift our bound position off the screen. Abort.\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tfinal int posTravel = position - lastPos + 1;\n\t\t\t\t\tfinal int boundTravel = boundPosFromFirst - 1;\n\t\t\t\t\tif (boundTravel < posTravel) {\n\t\t\t\t\t\tviewTravelCount = boundTravel;\n\t\t\t\t\t\tmMode = MOVE_DOWN_BOUND;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tviewTravelCount = posTravel;\n\t\t\t\t\t\tmMode = MOVE_DOWN_POS;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Already on screen, nothing to do\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (viewTravelCount > 0) {\n\t\t\t\t\tmScrollDuration = SCROLL_DURATION / viewTravelCount;\n\t\t\t\t} else {\n\t\t\t\t\tmScrollDuration = SCROLL_DURATION;\n\t\t\t\t}\n\t\t\t\tmTargetPos = position;\n\t\t\t\tmBoundPos = boundPosition;\n\t\t\t\tmLastSeenPos = INVALID_POSITION;\n\n\t\t\t\tpost(this);\n\t\t\t}\n\n\t\t\tvoid stop() {\n\t\t\t\tremoveCallbacks(this);\n\t\t\t}\n\n\t\t\tpublic abstract void run();\n\t\t}\n\n\n\t}\n\n\n\n\n\n\n\t////////////////////////////////////////////////////////////////////////////////////////\n\t//  Vertical Touch Handler\n\t////////////////////////////////////////////////////////////////////////////////////////\n\n\tclass VerticalTouchHandler extends TouchHandler {\n\t\t/**\n\t\t * The offset to the top of the mMotionPosition view when the down motion event was received\n\t\t */\n\t\tint mMotionViewOriginalTop;\n\n\t\t/**\n\t\t * Y value from on the previous motion event (if any)\n\t\t */\n\t\tint mLastY;\n\n\t\t/**\n\t\t * The desired offset to the top of the mMotionPosition view after a scroll\n\t\t */\n\t\tint mMotionViewNewTop;\n\n\t\t@Override\n\t\tpublic boolean onTouchEvent(MotionEvent ev) {\n\t\t\tif (!isEnabled()) {\n\t\t\t\t// A disabled view that is clickable still consumes the touch\n\t\t\t\t// events, it just doesn't respond to them.\n\t\t\t\treturn isClickable() || isLongClickable();\n\t\t\t}\n\n\t\t\t/*\n            if (mFastScroller != null) {\n                boolean intercepted = mFastScroller.onTouchEvent(ev);\n                if (intercepted) {\n                    return true;\n                }\n            }*/\n\n\t\t\tfinal int action = ev.getAction();\n\n\t\t\tView v;\n\t\t\tint deltaY;\n\n\t\t\tif (mVelocityTracker == null) {\n\t\t\t\tmVelocityTracker = VelocityTracker.obtain();\n\t\t\t}\n\t\t\tmVelocityTracker.addMovement(ev);\n\n\t\t\tswitch (action) {\n\t\t\tcase MotionEvent.ACTION_DOWN: {\n\t\t\t\tfinal int x = (int) ev.getX();\n\t\t\t\tfinal int y = (int) ev.getY();\n\t\t\t\tint motionPosition = pointToPosition(x, y);\n\t\t\t\tif (!mDataChanged) {\n\t\t\t\t\tif ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0)\n\t\t\t\t\t\t\t&& (getAdapter().isEnabled(motionPosition))) {\n\t\t\t\t\t\t// User clicked on an actual view (and was not stopping a fling). It might be a\n\t\t\t\t\t\t// click or a scroll. Assume it is a click until proven otherwise\n\t\t\t\t\t\tmTouchMode = TOUCH_MODE_DOWN;\n\t\t\t\t\t\t// FIXME Debounce\n\t\t\t\t\t\tif (mPendingCheckForTap == null) {\n\t\t\t\t\t\t\tmPendingCheckForTap = new CheckForTap();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpostDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (ev.getEdgeFlags() != 0 && motionPosition < 0) {\n\t\t\t\t\t\t\t// If we couldn't find a view to click on, but the down event was touching\n\t\t\t\t\t\t\t// the edge, we will bail out and try again. This allows the edge correcting\n\t\t\t\t\t\t\t// code in ViewRoot to try to find a nearby view to select\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (mTouchMode == TOUCH_MODE_FLING) {\n\t\t\t\t\t\t\t// Stopped a fling. It is a scroll.\n\t\t\t\t\t\t\tcreateScrollingCache();\n\t\t\t\t\t\t\tmTouchMode = TOUCH_MODE_SCROLL;\n\t\t\t\t\t\t\tmMotionCorrection = 0;\n\t\t\t\t\t\t\tmotionPosition = findMotionRowY(y);\n\t\t\t\t\t\t\tmFlingRunnable.flywheelTouch();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (motionPosition >= 0) {\n\t\t\t\t\t// Remember where the motion event started\n\t\t\t\t\tv = getChildAt(motionPosition - mFirstPosition);\n\t\t\t\t\tmMotionViewOriginalTop = v.getTop();\n\t\t\t\t}\n\t\t\t\tmMotionX = x;\n\t\t\t\tmMotionY = y;\n\t\t\t\tmMotionPosition = motionPosition;\n\t\t\t\tmLastY = Integer.MIN_VALUE;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MotionEvent.ACTION_MOVE: {\n\t\t\t\tfinal int y = (int) ev.getY();\n\t\t\t\tdeltaY = y - mMotionY;\n\t\t\t\tswitch (mTouchMode) {\n\t\t\t\tcase TOUCH_MODE_DOWN:\n\t\t\t\tcase TOUCH_MODE_TAP:\n\t\t\t\tcase TOUCH_MODE_DONE_WAITING:\n\t\t\t\t\t// Check if we have moved far enough that it looks more like a\n\t\t\t\t\t// scroll than a tap\n\t\t\t\t\tstartScrollIfNeeded(deltaY);\n\t\t\t\t\tbreak;\n\t\t\t\tcase TOUCH_MODE_SCROLL:\n\t\t\t\t\tif (PROFILE_SCROLLING) {\n\t\t\t\t\t\tif (!mScrollProfilingStarted) {\n\t\t\t\t\t\t\tDebug.startMethodTracing(\"JessAbsListViewScroll\");\n\t\t\t\t\t\t\tmScrollProfilingStarted = true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (y != mLastY) {\n\t\t\t\t\t\tdeltaY -= mMotionCorrection;\n\t\t\t\t\t\tint incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;\n\n\t\t\t\t\t\t// No need to do all this work if we're not going to move anyway\n\t\t\t\t\t\tboolean atEdge = false;\n\t\t\t\t\t\tif (incrementalDeltaY != 0) {\n\t\t\t\t\t\t\tatEdge = trackMotionScroll(deltaY, incrementalDeltaY);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Check to see if we have bumped into the scroll limit\n\t\t\t\t\t\tif (atEdge && getChildCount() > 0) {\n\t\t\t\t\t\t\t// Treat this like we're starting a new scroll from the current\n\t\t\t\t\t\t\t// position. This will let the user start scrolling back into\n\t\t\t\t\t\t\t// content immediately rather than needing to scroll back to the\n\t\t\t\t\t\t\t// point where they hit the limit first.\n\t\t\t\t\t\t\tint motionPosition = findMotionRowY(y);\n\t\t\t\t\t\t\tif (motionPosition >= 0) {\n\t\t\t\t\t\t\t\tfinal View motionView = getChildAt(motionPosition - mFirstPosition);\n\t\t\t\t\t\t\t\tmMotionViewOriginalTop = motionView.getTop();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tmMotionY = y;\n\t\t\t\t\t\t\tmMotionPosition = motionPosition;\n\t\t\t\t\t\t\tinvalidate();\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmLastY = y;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MotionEvent.ACTION_UP: {\n\t\t\t\tswitch (mTouchMode) {\n\t\t\t\tcase TOUCH_MODE_DOWN:\n\t\t\t\tcase TOUCH_MODE_TAP:\n\t\t\t\tcase TOUCH_MODE_DONE_WAITING:\n\t\t\t\t\tfinal int motionPosition = mMotionPosition;\n\t\t\t\t\tfinal View child = getChildAt(motionPosition - mFirstPosition);\n\t\t\t\t\tif (child != null && !child.hasFocusable()) {\n\t\t\t\t\t\tif (mTouchMode != TOUCH_MODE_DOWN) {\n\t\t\t\t\t\t\tchild.setPressed(false);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (mPerformClick == null) {\n\t\t\t\t\t\t\tmPerformClick = new PerformClick();\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tfinal TwoWayAbsListView.PerformClick performClick = mPerformClick;\n\t\t\t\t\t\tperformClick.mChild = child;\n\t\t\t\t\t\tperformClick.mClickMotionPosition = motionPosition;\n\t\t\t\t\t\tperformClick.rememberWindowAttachCount();\n\n\t\t\t\t\t\tmResurrectToPosition = motionPosition;\n\n\t\t\t\t\t\tif (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) {\n\t\t\t\t\t\t\tfinal Handler handler = getHandler();\n\t\t\t\t\t\t\tif (handler != null) {\n\t\t\t\t\t\t\t\thandler.removeCallbacks(mTouchMode == TOUCH_MODE_DOWN ?\n\t\t\t\t\t\t\t\t\t\tmPendingCheckForTap : mPendingCheckForLongPress);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tmLayoutMode = LAYOUT_NORMAL;\n\t\t\t\t\t\t\tif (!mDataChanged && mAdapter.isEnabled(motionPosition)) {\n\t\t\t\t\t\t\t\tmTouchMode = TOUCH_MODE_TAP;\n\t\t\t\t\t\t\t\tsetSelectedPositionInt(mMotionPosition);\n\t\t\t\t\t\t\t\tlayoutChildren();\n\t\t\t\t\t\t\t\tchild.setPressed(true);\n\t\t\t\t\t\t\t\tpositionSelector(child);\n\t\t\t\t\t\t\t\tsetPressed(true);\n\t\t\t\t\t\t\t\tif (mSelector != null) {\n\t\t\t\t\t\t\t\t\tDrawable d = mSelector.getCurrent();\n\t\t\t\t\t\t\t\t\tif (d != null && d instanceof TransitionDrawable) {\n\t\t\t\t\t\t\t\t\t\t((TransitionDrawable) d).resetTransition();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tpostDelayed(new Runnable() {\n\t\t\t\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\t\t\t\tchild.setPressed(false);\n\t\t\t\t\t\t\t\t\t\tsetPressed(false);\n\t\t\t\t\t\t\t\t\t\tif (!mDataChanged) {\n\t\t\t\t\t\t\t\t\t\t\tpost(performClick);\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tmTouchMode = TOUCH_MODE_REST;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}, ViewConfiguration.getPressedStateDuration());\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tmTouchMode = TOUCH_MODE_REST;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t} else if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {\n\t\t\t\t\t\t\tpost(performClick);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tmTouchMode = TOUCH_MODE_REST;\n\t\t\t\t\tbreak;\n\t\t\t\tcase TOUCH_MODE_SCROLL:\n\t\t\t\t\tfinal int childCount = getChildCount();\n\t\t\t\t\tif (childCount > 0) {\n\t\t\t\t\t\tif (mFirstPosition == 0 && getChildAt(0).getTop() >= mListPadding.top &&\n\t\t\t\t\t\t\t\tmFirstPosition + childCount < mItemCount &&\n\t\t\t\t\t\t\t\tgetChildAt(childCount - 1).getBottom() <=\n\t\t\t\t\t\t\t\t\tgetHeight() - mListPadding.bottom) {\n\t\t\t\t\t\t\tmTouchMode = TOUCH_MODE_REST;\n\t\t\t\t\t\t\treportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tfinal VelocityTracker velocityTracker = mVelocityTracker;\n\t\t\t\t\t\t\tvelocityTracker.computeCurrentVelocity(1000);\n\t\t\t\t\t\t\tfinal int initialVelocity = (int) velocityTracker.getYVelocity();\n\n\t\t\t\t\t\t\tif (Math.abs(initialVelocity) > mMinimumVelocity) {\n\t\t\t\t\t\t\t\tif (mFlingRunnable == null) {\n\t\t\t\t\t\t\t\t\tmFlingRunnable = new VerticalFlingRunnable();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);\n\n\t\t\t\t\t\t\t\tmFlingRunnable.start(-initialVelocity);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tmTouchMode = TOUCH_MODE_REST;\n\t\t\t\t\t\t\t\treportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmTouchMode = TOUCH_MODE_REST;\n\t\t\t\t\t\treportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tsetPressed(false);\n\n\t\t\t\t// Need to redraw since we probably aren't drawing the selector anymore\n\t\t\t\tinvalidate();\n\n\t\t\t\tfinal Handler handler = getHandler();\n\t\t\t\tif (handler != null) {\n\t\t\t\t\thandler.removeCallbacks(mPendingCheckForLongPress);\n\t\t\t\t}\n\n\t\t\t\tif (mVelocityTracker != null) {\n\t\t\t\t\tmVelocityTracker.recycle();\n\t\t\t\t\tmVelocityTracker = null;\n\t\t\t\t}\n\n\t\t\t\tmActivePointerId = INVALID_POINTER;\n\n\t\t\t\tif (PROFILE_SCROLLING) {\n\t\t\t\t\tif (mScrollProfilingStarted) {\n\t\t\t\t\t\tDebug.stopMethodTracing();\n\t\t\t\t\t\tmScrollProfilingStarted = false;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase MotionEvent.ACTION_CANCEL: {\n\t\t\t\tmTouchMode = TOUCH_MODE_REST;\n\t\t\t\tsetPressed(false);\n\t\t\t\tView motionView = TwoWayAbsListView.this.getChildAt(mMotionPosition - mFirstPosition);\n\t\t\t\tif (motionView != null) {\n\t\t\t\t\tmotionView.setPressed(false);\n\t\t\t\t}\n\t\t\t\tclearScrollingCache();\n\n\t\t\t\tfinal Handler handler = getHandler();\n\t\t\t\tif (handler != null) {\n\t\t\t\t\thandler.removeCallbacks(mPendingCheckForLongPress);\n\t\t\t\t}\n\n\t\t\t\tif (mVelocityTracker != null) {\n\t\t\t\t\tmVelocityTracker.recycle();\n\t\t\t\t\tmVelocityTracker = null;\n\t\t\t\t}\n\n\t\t\t\tmActivePointerId = INVALID_POINTER;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\n\n\t\t@Override\n\t\tpublic boolean onInterceptTouchEvent(MotionEvent ev) {\n\t\t\tint action = ev.getAction();\n\t\t\tView v;\n\n\t\t\t/*\n            if (mFastScroller != null) {\n                boolean intercepted = mFastScroller.onInterceptTouchEvent(ev);\n                if (intercepted) {\n                    return true;\n                }\n            }*/\n\n\t\t\tswitch (action) {\n\t\t\t\tcase MotionEvent.ACTION_DOWN: {\n\t\t\t\t\tint touchMode = mTouchMode;\n\t\n\t\t\t\t\tfinal int x = (int) ev.getX();\n\t\t\t\t\tfinal int y = (int) ev.getY();\n\t\t\t\t\t\n\t\t\t\t\tint motionPosition = findMotionRowY(y);\n\t\t\t\t\tif (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) {\n\t\t\t\t\t\t// User clicked on an actual view (and was not stopping a fling).\n\t\t\t\t\t\t// Remember where the motion event started\n\t\t\t\t\t\tv = getChildAt(motionPosition - mFirstPosition);\n\t\t\t\t\t\tmMotionViewOriginalTop = v.getTop();\n\t\t\t\t\t\tmMotionX = x;\n\t\t\t\t\t\tmMotionY = y;\n\t\t\t\t\t\tmMotionPosition = motionPosition;\n\t\t\t\t\t\tmTouchMode = TOUCH_MODE_DOWN;\n\t\t\t\t\t\tclearScrollingCache();\n\t\t\t\t\t}\n\t\t\t\t\tmLastY = Integer.MIN_VALUE;\n\t\t\t\t\tinitOrResetVelocityTracker();\n\t\t\t\t\tmVelocityTracker.addMovement(ev);\n\t\t\t\t\tif (touchMode == TOUCH_MODE_FLING) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\n\t\t\t\tcase MotionEvent.ACTION_MOVE: {\n\t\t\t\t\tswitch (mTouchMode) {\n\t\t\t\t\tcase TOUCH_MODE_DOWN:\n\t\t\t\t\t\tfinal int y = (int) ev.getY();\n\t\t\t\t\t\tif (startScrollIfNeeded(y - mMotionY)) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\n\t\t\t\tcase MotionEvent.ACTION_UP: {\n\t\t\t\t\tmTouchMode = TOUCH_MODE_REST;\n\t\t\t\t\tmActivePointerId = INVALID_POINTER;\n\t\t\t\t\treportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\t/**\n\t\t * Track a motion scroll\n\t\t *\n\t\t * @param deltaY Amount to offset mMotionView. This is the accumulated delta since the motion\n\t\t *        began. Positive numbers mean the user's finger is moving down the screen.\n\t\t * @param incrementalDeltaY Change in deltaY from the previous event.\n\t\t * @return true if we're already at the beginning/end of the list and have nothing to do.\n\t\t */\n\t\t@Override\n\t\tboolean trackMotionScroll(int deltaY, int incrementalDeltaY) {\n\t\t\tif (DEBUG) Log.i(TAG, \"trackMotionScroll() - deltaY: \" + deltaY + \" incrDeltaY: \" + incrementalDeltaY);\n\t\t\tfinal int childCount = getChildCount();\n\t\t\tif (childCount == 0) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tfinal int firstTop = getChildAt(0).getTop();\n\t\t\tfinal int lastBottom = getChildAt(childCount - 1).getBottom();\n\n\t\t\tfinal Rect listPadding = mListPadding;\n\n\t\t\t// FIXME account for grid vertical spacing too?\n\t\t\tfinal int spaceAbove = listPadding.top - firstTop;\n\t\t\tfinal int end = getHeight() - listPadding.bottom;\n\t\t\tfinal int spaceBelow = lastBottom - end;\n\n\t\t\tfinal int height = getHeight() - getPaddingBottom() - getPaddingTop();\n\t\t\tif (deltaY < 0) {\n\t\t\t\tdeltaY = Math.max(-(height - 1), deltaY);\n\t\t\t} else {\n\t\t\t\tdeltaY = Math.min(height - 1, deltaY);\n\t\t\t}\n\n\t\t\tif (incrementalDeltaY < 0) {\n\t\t\t\tincrementalDeltaY = Math.max(-(height - 1), incrementalDeltaY);\n\t\t\t} else {\n\t\t\t\tincrementalDeltaY = Math.min(height - 1, incrementalDeltaY);\n\t\t\t}\n\n\t\t\tfinal int firstPosition = mFirstPosition;\n\n\t\t\tif (firstPosition == 0 && firstTop >= listPadding.top && deltaY >= 0) {\n\t\t\t\t// Don't need to move views down if the top of the first position\n\t\t\t\t// is already visible\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (firstPosition + childCount == mItemCount && lastBottom <= end && deltaY <= 0) {\n\t\t\t\t// Don't need to move views up if the bottom of the last position\n\t\t\t\t// is already visible\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tfinal boolean down = incrementalDeltaY < 0;\n\n\t\t\tfinal boolean inTouchMode = isInTouchMode();\n\t\t\tif (inTouchMode) {\n\t\t\t\thideSelector();\n\t\t\t}\n\n\t\t\tfinal int headerViewsCount = getHeaderViewsCount();\n\t\t\tfinal int footerViewsStart = mItemCount - getFooterViewsCount();\n\n\t\t\tint start = 0;\n\t\t\tint count = 0;\n\n\t\t\tif (down) {\n\t\t\t\tfinal int top = listPadding.top - incrementalDeltaY;\n\t\t\t\tfor (int i = 0; i < childCount; i++) {\n\t\t\t\t\tfinal View child = getChildAt(i);\n\t\t\t\t\tif (child.getBottom() >= top) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcount++;\n\t\t\t\t\t\tint position = firstPosition + i;\n\t\t\t\t\t\tif (position >= headerViewsCount && position < footerViewsStart) {\n\t\t\t\t\t\t\tmRecycler.addScrapView(child);\n\n\t\t\t\t\t\t\tif (ViewDebug.TRACE_RECYCLER) {\n\t\t\t\t\t\t\t\tViewDebug.trace(child,\n\t\t\t\t\t\t\t\t\t\tViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,\n\t\t\t\t\t\t\t\t\t\tfirstPosition + i, -1);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfinal int bottom = getHeight() - listPadding.bottom - incrementalDeltaY;\n\t\t\t\tfor (int i = childCount - 1; i >= 0; i--) {\n\t\t\t\t\tfinal View child = getChildAt(i);\n\t\t\t\t\tif (child.getTop() <= bottom) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstart = i;\n\t\t\t\t\t\tcount++;\n\t\t\t\t\t\tint position = firstPosition + i;\n\t\t\t\t\t\tif (position >= headerViewsCount && position < footerViewsStart) {\n\t\t\t\t\t\t\tmRecycler.addScrapView(child);\n\n\t\t\t\t\t\t\tif (ViewDebug.TRACE_RECYCLER) {\n\t\t\t\t\t\t\t\tViewDebug.trace(child,\n\t\t\t\t\t\t\t\t\t\tViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,\n\t\t\t\t\t\t\t\t\t\tfirstPosition + i, -1);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmMotionViewNewTop = mMotionViewOriginalTop + deltaY;\n\n\t\t\tmBlockLayoutRequests = true;\n\n\t\t\tif (count > 0) {\n\t\t\t\tdetachViewsFromParent(start, count);\n\t\t\t}\n\t\t\toffsetChildrenTopAndBottom(incrementalDeltaY);\n\n\t\t\tif (down) {\n\t\t\t\tmFirstPosition += count;\n\t\t\t}\n\n\t\t\tinvalidate();\n\n\t\t\tfinal int absIncrementalDeltaY = Math.abs(incrementalDeltaY);\n\t\t\tif (spaceAbove < absIncrementalDeltaY || spaceBelow < absIncrementalDeltaY) {\n\t\t\t\tfillGap(down);\n\t\t\t}\n\n\t\t\tif (!inTouchMode && mSelectedPosition != INVALID_POSITION) {\n\t\t\t\tfinal int childIndex = mSelectedPosition - mFirstPosition;\n\t\t\t\tif (childIndex >= 0 && childIndex < getChildCount()) {\n\t\t\t\t\tpositionSelector(getChildAt(childIndex));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmBlockLayoutRequests = false;\n\n\t\t\tinvokeOnItemScrollListener();\n\t\t\t//awakenScrollBars();\n\n\t\t\treturn false;\n\t\t}\n\n\t\t/**\n\t\t * Attempt to bring the selection back if the user is switching from touch\n\t\t * to trackball mode\n\t\t * @return Whether selection was set to something.\n\t\t */\n\t\t@Override\n\t\tboolean resurrectSelection() {\n\t\t\tfinal int childCount = getChildCount();\n\n\t\t\tif (childCount <= 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tint selectedTop = 0;\n\t\t\tint selectedPos;\n\t\t\tint childrenTop = mListPadding.top;\n\t\t\tint childrenBottom = getBottom() - getTop() - mListPadding.bottom;\n\t\t\tfinal int firstPosition = mFirstPosition;\n\t\t\tfinal int toPosition = mResurrectToPosition;\n\t\t\tboolean down = true;\n\n\t\t\tif (toPosition >= firstPosition && toPosition < firstPosition + childCount) {\n\t\t\t\tselectedPos = toPosition;\n\n\t\t\t\tfinal View selected = getChildAt(selectedPos - mFirstPosition);\n\t\t\t\tselectedTop = selected.getTop();\n\t\t\t\tint selectedBottom = selected.getBottom();\n\n\t\t\t\t// We are scrolled, don't get in the fade\n\t\t\t\tif (selectedTop < childrenTop) {\n\t\t\t\t\tselectedTop = childrenTop + getVerticalFadingEdgeLength();\n\t\t\t\t} else if (selectedBottom > childrenBottom) {\n\t\t\t\t\tselectedTop = childrenBottom - selected.getMeasuredHeight()\n\t\t\t\t\t- getVerticalFadingEdgeLength();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (toPosition < firstPosition) {\n\t\t\t\t\t// Default to selecting whatever is first\n\t\t\t\t\tselectedPos = firstPosition;\n\t\t\t\t\tfor (int i = 0; i < childCount; i++) {\n\t\t\t\t\t\tfinal View v = getChildAt(i);\n\t\t\t\t\t\tfinal int top = v.getTop();\n\n\t\t\t\t\t\tif (i == 0) {\n\t\t\t\t\t\t\t// Remember the position of the first item\n\t\t\t\t\t\t\tselectedTop = top;\n\t\t\t\t\t\t\t// See if we are scrolled at all\n\t\t\t\t\t\t\tif (firstPosition > 0 || top < childrenTop) {\n\t\t\t\t\t\t\t\t// If we are scrolled, don't select anything that is\n\t\t\t\t\t\t\t\t// in the fade region\n\t\t\t\t\t\t\t\tchildrenTop += getVerticalFadingEdgeLength();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (top >= childrenTop) {\n\t\t\t\t\t\t\t// Found a view whose top is fully visisble\n\t\t\t\t\t\t\tselectedPos = firstPosition + i;\n\t\t\t\t\t\t\tselectedTop = top;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfinal int itemCount = mItemCount;\n\t\t\t\t\tdown = false;\n\t\t\t\t\tselectedPos = firstPosition + childCount - 1;\n\n\t\t\t\t\tfor (int i = childCount - 1; i >= 0; i--) {\n\t\t\t\t\t\tfinal View v = getChildAt(i);\n\t\t\t\t\t\tfinal int top = v.getTop();\n\t\t\t\t\t\tfinal int bottom = v.getBottom();\n\n\t\t\t\t\t\tif (i == childCount - 1) {\n\t\t\t\t\t\t\tselectedTop = top;\n\t\t\t\t\t\t\tif (firstPosition + childCount < itemCount || bottom > childrenBottom) {\n\t\t\t\t\t\t\t\tchildrenBottom -= getVerticalFadingEdgeLength();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (bottom <= childrenBottom) {\n\t\t\t\t\t\t\tselectedPos = firstPosition + i;\n\t\t\t\t\t\t\tselectedTop = top;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmResurrectToPosition = INVALID_POSITION;\n\t\t\tremoveCallbacks(mFlingRunnable);\n\t\t\tmTouchMode = TOUCH_MODE_REST;\n\t\t\tclearScrollingCache();\n\t\t\tmSpecificTop = selectedTop;\n\t\t\tselectedPos = lookForSelectablePosition(selectedPos, down);\n\t\t\tif (selectedPos >= firstPosition && selectedPos <= getLastVisiblePosition()) {\n\t\t\t\tmLayoutMode = LAYOUT_SPECIFIC;\n\t\t\t\tsetSelectionInt(selectedPos);\n\t\t\t\tinvokeOnItemScrollListener();\n\t\t\t} else {\n\t\t\t\tselectedPos = INVALID_POSITION;\n\t\t\t}\n\t\t\treportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);\n\n\t\t\treturn selectedPos >= 0;\n\t\t}\n\n\n\n\t\t@Override\n\t\tprotected PositionScroller getPositionScroller() {\n\t\t\treturn new VerticalPositionScroller();\n\t\t}\n\n\t\t@Override\n\t\tprotected FlingRunnable getFlingRunnable() {\n\t\t\treturn new VerticalFlingRunnable();\n\t\t}\n\n\n\t\t/**\n\t\t * Responsible for fling behavior. Use {@link #start(int)} to\n\t\t * initiate a fling. Each frame of the fling is handled in {@link #run()}.\n\t\t * A FlingRunnable will keep re-posting itself until the fling is done.\n\t\t *\n\t\t */\n\t\tprivate class VerticalFlingRunnable extends FlingRunnable {\n\t\t\t/**\n\t\t\t * Y value reported by mScroller on the previous fling\n\t\t\t */\n\t\t\tprotected int mLastFlingY;\n\n\t\t\t@Override\n\t\t\tvoid start(int initialVelocity) {\n\t\t\t\tint initialY = initialVelocity < 0 ? Integer.MAX_VALUE : 0;\n\t\t\t\tmLastFlingY = initialY;\n\t\t\t\tmScroller.fling(0, initialY, 0, initialVelocity,\n\t\t\t\t\t\t0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE);\n\t\t\t\tmTouchMode = TOUCH_MODE_FLING;\n\t\t\t\tpost(this);\n\n\t\t\t\tif (PROFILE_FLINGING) {\n\t\t\t\t\tif (!mFlingProfilingStarted) {\n\t\t\t\t\t\tDebug.startMethodTracing(\"AbsListViewFling\");\n\t\t\t\t\t\tmFlingProfilingStarted = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tvoid startScroll(int distance, int duration) {\n\t\t\t\tint initialY = distance < 0 ? Integer.MAX_VALUE : 0;\n\t\t\t\tmLastFlingY = initialY;\n\t\t\t\tmScroller.startScroll(0, initialY, 0, distance, duration);\n\t\t\t\tmTouchMode = TOUCH_MODE_FLING;\n\t\t\t\tpost(this);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tswitch (mTouchMode) {\n\t\t\t\tdefault:\n\t\t\t\t\treturn;\n\n\t\t\t\tcase TOUCH_MODE_FLING: {\n\t\t\t\t\tif (mItemCount == 0 || getChildCount() == 0) {\n\t\t\t\t\t\tendFling();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tfinal Scroller scroller = mScroller;\n\t\t\t\t\tboolean more = scroller.computeScrollOffset();\n\t\t\t\t\tfinal int y = scroller.getCurrY();\n\n\t\t\t\t\t// Flip sign to convert finger direction to list items direction\n\t\t\t\t\t// (e.g. finger moving down means list is moving towards the top)\n\t\t\t\t\tint delta = mLastFlingY - y;\n\n\t\t\t\t\t// Pretend that each frame of a fling scroll is a touch scroll\n\t\t\t\t\tif (delta > 0) {\n\t\t\t\t\t\t// List is moving towards the top. Use first view as mMotionPosition\n\t\t\t\t\t\tmMotionPosition = mFirstPosition;\n\t\t\t\t\t\tfinal View firstView = getChildAt(0);\n\t\t\t\t\t\tmMotionViewOriginalTop = firstView.getTop();\n\n\t\t\t\t\t\t// Don't fling more than 1 screen\n\t\t\t\t\t\tdelta = Math.min(getHeight() - getPaddingBottom() - getPaddingTop() - 1, delta);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// List is moving towards the bottom. Use last view as mMotionPosition\n\t\t\t\t\t\tint offsetToLast = getChildCount() - 1;\n\t\t\t\t\t\tmMotionPosition = mFirstPosition + offsetToLast;\n\n\t\t\t\t\t\tfinal View lastView = getChildAt(offsetToLast);\n\t\t\t\t\t\tmMotionViewOriginalTop = lastView.getTop();\n\n\t\t\t\t\t\t// Don't fling more than 1 screen\n\t\t\t\t\t\tdelta = Math.max(-(getHeight() - getPaddingBottom() - getPaddingTop() - 1), delta);\n\t\t\t\t\t}\n\n\t\t\t\t\tfinal boolean atEnd = trackMotionScroll(delta, delta);\n\n\t\t\t\t\tif (more && !atEnd) {\n\t\t\t\t\t\tinvalidate();\n\t\t\t\t\t\tmLastFlingY = y;\n\t\t\t\t\t\tpost(this);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tendFling();\n\n\t\t\t\t\t\tif (PROFILE_FLINGING) {\n\t\t\t\t\t\t\tif (mFlingProfilingStarted) {\n\t\t\t\t\t\t\t\tDebug.stopMethodTracing();\n\t\t\t\t\t\t\t\tmFlingProfilingStarted = false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\t\t\t\n\t\t\tprivate static final int FLYWHEEL_TIMEOUT = 40; // milliseconds\n\t\t\t\n\t\t\tpublic void flywheelTouch() {\n\t\t\t\tif(mCheckFlywheel == null) {\n\t\t\t\t\tmCheckFlywheel = new Runnable() {\n\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\tfinal VelocityTracker vt = mVelocityTracker;\n\t\t\t\t\t\t\tif (vt == null) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tvt.computeCurrentVelocity(1000, mMaximumVelocity);\n\t\t\t\t\t\t\tfinal float yvel = -vt.getYVelocity();\n\n\t\t\t\t\t\t\tif (Math.abs(yvel) >= mMinimumVelocity\n\t\t\t\t\t\t\t\t\t&& isScrollingInDirection(0, yvel)) {\n\t\t\t\t\t\t\t\t// Keep the fling alive a little longer\n\t\t\t\t\t\t\t\tpostDelayed(this, FLYWHEEL_TIMEOUT);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tendFling();\n\t\t\t\t\t\t\t\tmTouchMode = TOUCH_MODE_SCROLL;\n\t\t\t\t\t\t\t\treportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tpostDelayed(mCheckFlywheel, FLYWHEEL_TIMEOUT);\n\t\t\t}\n\t\t}\n\n\n\t\tclass VerticalPositionScroller extends PositionScroller {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tfinal int listHeight = getHeight();\n\t\t\t\tfinal int firstPos = mFirstPosition;\n\n\t\t\t\tswitch (mMode) {\n\t\t\t\tcase MOVE_DOWN_POS: {\n\t\t\t\t\tfinal int lastViewIndex = getChildCount() - 1;\n\t\t\t\t\tfinal int lastPos = firstPos + lastViewIndex;\n\n\t\t\t\t\tif (lastViewIndex < 0) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (lastPos == mLastSeenPos) {\n\t\t\t\t\t\t// No new views, let things keep going.\n\t\t\t\t\t\tpost(this);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tfinal View lastView = getChildAt(lastViewIndex);\n\t\t\t\t\tfinal int lastViewHeight = lastView.getHeight();\n\t\t\t\t\tfinal int lastViewTop = lastView.getTop();\n\t\t\t\t\tfinal int lastViewPixelsShowing = listHeight - lastViewTop;\n\t\t\t\t\tfinal int extraScroll = lastPos < mItemCount - 1 ? mExtraScroll : mListPadding.bottom;\n\n\t\t\t\t\tsmoothScrollBy(lastViewHeight - lastViewPixelsShowing + extraScroll,\n\t\t\t\t\t\t\tmScrollDuration);\n\n\t\t\t\t\tmLastSeenPos = lastPos;\n\t\t\t\t\tif (lastPos < mTargetPos) {\n\t\t\t\t\t\tpost(this);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase MOVE_DOWN_BOUND: {\n\t\t\t\t\tfinal int nextViewIndex = 1;\n\t\t\t\t\tfinal int childCount = getChildCount();\n\n\t\t\t\t\tif (firstPos == mBoundPos || childCount <= nextViewIndex\n\t\t\t\t\t\t\t|| firstPos + childCount >= mItemCount) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tfinal int nextPos = firstPos + nextViewIndex;\n\n\t\t\t\t\tif (nextPos == mLastSeenPos) {\n\t\t\t\t\t\t// No new views, let things keep going.\n\t\t\t\t\t\tpost(this);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tfinal View nextView = getChildAt(nextViewIndex);\n\t\t\t\t\tfinal int nextViewHeight = nextView.getHeight();\n\t\t\t\t\tfinal int nextViewTop = nextView.getTop();\n\t\t\t\t\tfinal int extraScroll = mExtraScroll;\n\t\t\t\t\tif (nextPos < mBoundPos) {\n\t\t\t\t\t\tsmoothScrollBy(Math.max(0, nextViewHeight + nextViewTop - extraScroll),\n\t\t\t\t\t\t\t\tmScrollDuration);\n\n\t\t\t\t\t\tmLastSeenPos = nextPos;\n\n\t\t\t\t\t\tpost(this);\n\t\t\t\t\t} else  {\n\t\t\t\t\t\tif (nextViewTop > extraScroll) {\n\t\t\t\t\t\t\tsmoothScrollBy(nextViewTop - extraScroll, mScrollDuration);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase MOVE_UP_POS: {\n\t\t\t\t\tif (firstPos == mLastSeenPos) {\n\t\t\t\t\t\t// No new views, let things keep going.\n\t\t\t\t\t\tpost(this);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tfinal View firstView = getChildAt(0);\n\t\t\t\t\tif (firstView == null) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tfinal int firstViewTop = firstView.getTop();\n\t\t\t\t\tfinal int extraScroll = firstPos > 0 ? mExtraScroll : mListPadding.top;\n\n\t\t\t\t\tsmoothScrollBy(firstViewTop - extraScroll, mScrollDuration);\n\n\t\t\t\t\tmLastSeenPos = firstPos;\n\n\t\t\t\t\tif (firstPos > mTargetPos) {\n\t\t\t\t\t\tpost(this);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase MOVE_UP_BOUND: {\n\t\t\t\t\tfinal int lastViewIndex = getChildCount() - 2;\n\t\t\t\t\tif (lastViewIndex < 0) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tfinal int lastPos = firstPos + lastViewIndex;\n\n\t\t\t\t\tif (lastPos == mLastSeenPos) {\n\t\t\t\t\t\t// No new views, let things keep going.\n\t\t\t\t\t\tpost(this);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tfinal View lastView = getChildAt(lastViewIndex);\n\t\t\t\t\tfinal int lastViewHeight = lastView.getHeight();\n\t\t\t\t\tfinal int lastViewTop = lastView.getTop();\n\t\t\t\t\tfinal int lastViewPixelsShowing = listHeight - lastViewTop;\n\t\t\t\t\tmLastSeenPos = lastPos;\n\t\t\t\t\tif (lastPos > mBoundPos) {\n\t\t\t\t\t\tsmoothScrollBy(-(lastViewPixelsShowing - mExtraScroll), mScrollDuration);\n\t\t\t\t\t\tpost(this);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfinal int bottom = listHeight - mExtraScroll;\n\t\t\t\t\t\tfinal int lastViewBottom = lastViewTop + lastViewHeight;\n\t\t\t\t\t\tif (bottom > lastViewBottom) {\n\t\t\t\t\t\t\tsmoothScrollBy(-(bottom - lastViewBottom), mScrollDuration);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\n\n\n\n\n\t///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\t//  Horizontal Touch Handler\n\t///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////\n\n\tclass HorizontalTouchHandler extends TouchHandler {\n\t\t/**\n\t\t * The offset to the top of the mMotionPosition view when the down motion event was received\n\t\t */\n\t\tint mMotionViewOriginalLeft;\n\n\t\t/**\n\t\t * X value from on the previous motion event (if any)\n\t\t */\n\t\tint mLastX;\n\n\t\t/**\n\t\t * The desired offset to the top of the mMotionPosition view after a scroll\n\t\t */\n\t\tint mMotionViewNewLeft;\n\n\n\n\t\t@Override\n\t\tprotected FlingRunnable getFlingRunnable() {\n\t\t\treturn new HorizontalFlingRunnable();\n\t\t}\n\n\n\t\t@Override\n\t\tprotected PositionScroller getPositionScroller() {\n\t\t\treturn new HorizontalPositionScroller();\n\t\t}\n\n\n\t\t@Override\n\t\tpublic boolean onInterceptTouchEvent(MotionEvent ev) {\n\t\t\tint action = ev.getAction();\n\t\t\tView v;\n\n\t\t\t/*\n            if (mFastScroller != null) {\n                boolean intercepted = mFastScroller.onInterceptTouchEvent(ev);\n                if (intercepted) {\n                    return true;\n                }\n            }*/\n\n\t\t\tswitch (action) {\n\t\t\t\tcase MotionEvent.ACTION_DOWN: {\n\t\t\t\t\tint touchMode = mTouchMode;\n\t\n\t\t\t\t\tfinal int x = (int) ev.getX();\n\t\t\t\t\tfinal int y = (int) ev.getY();\n\t\t\t\t\t\n\t\t\t\t\tint motionPosition = findMotionRowX(x);\n\t\t\t\t\tif (touchMode != TOUCH_MODE_FLING && motionPosition >= 0) {\n\t\t\t\t\t\t// User clicked on an actual view (and was not stopping a fling).\n\t\t\t\t\t\t// Remember where the motion event started\n\t\t\t\t\t\tv = getChildAt(motionPosition - mFirstPosition);\n\t\t\t\t\t\tmMotionViewOriginalLeft = v.getLeft();\n\t\t\t\t\t\tmMotionX = x;\n\t\t\t\t\t\tmMotionY = y;\n\t\t\t\t\t\tmMotionPosition = motionPosition;\n\t\t\t\t\t\tmTouchMode = TOUCH_MODE_DOWN;\n\t\t\t\t\t\tclearScrollingCache();\n\t\t\t\t\t}\n\t\t\t\t\tmLastX = Integer.MIN_VALUE;\n\t\t\t\t\tinitOrResetVelocityTracker();\n\t\t\t\t\tmVelocityTracker.addMovement(ev);\n\t\t\t\t\tif (touchMode == TOUCH_MODE_FLING) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\n\t\t\t\tcase MotionEvent.ACTION_MOVE: {\n\t\t\t\t\tswitch (mTouchMode) {\n\t\t\t\t\tcase TOUCH_MODE_DOWN:\n\t\t\t\t\t\tfinal int x = (int) ev.getX();\n\t\t\t\t\t\tif (startScrollIfNeeded(x - mMotionX)) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\n\t\t\t\tcase MotionEvent.ACTION_UP: {\n\t\t\t\t\tmTouchMode = TOUCH_MODE_REST;\n\t\t\t\t\tmActivePointerId = INVALID_POINTER;\n\t\t\t\t\treportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\n\t\t@Override\n\t\tpublic boolean onTouchEvent(MotionEvent ev) {\n\t\t\tif (!isEnabled()) {\n\t\t\t\t// A disabled view that is clickable still consumes the touch\n\t\t\t\t// events, it just doesn't respond to them.\n\t\t\t\treturn isClickable() || isLongClickable();\n\t\t\t}\n\n\t\t\t/*\n            if (mFastScroller != null) {\n                boolean intercepted = mFastScroller.onTouchEvent(ev);\n                if (intercepted) {\n                    return true;\n                }\n            }*/\n\n\t\t\tfinal int action = ev.getAction();\n\n\t\t\tView v;\n\t\t\tint deltaX;\n\n\t\t\tif (mVelocityTracker == null) {\n\t\t\t\tmVelocityTracker = VelocityTracker.obtain();\n\t\t\t}\n\t\t\tmVelocityTracker.addMovement(ev);\n\n\t\t\tswitch (action) {\n\t\t\t\tcase MotionEvent.ACTION_DOWN: {\n\t\t\t\t\tfinal int x = (int) ev.getX();\n\t\t\t\t\tfinal int y = (int) ev.getY();\n\t\t\t\t\tint motionPosition = pointToPosition(x, y);\n\t\t\t\t\tif (!mDataChanged) {\n\t\t\t\t\t\tif ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0)\n\t\t\t\t\t\t\t\t&& (getAdapter().isEnabled(motionPosition))) {\n\t\t\t\t\t\t\t// User clicked on an actual view (and was not stopping a fling). It might be a\n\t\t\t\t\t\t\t// click or a scroll. Assume it is a click until proven otherwise\n\t\t\t\t\t\t\tmTouchMode = TOUCH_MODE_DOWN;\n\t\t\t\t\t\t\t// FIXME Debounce\n\t\t\t\t\t\t\tif (mPendingCheckForTap == null) {\n\t\t\t\t\t\t\t\tmPendingCheckForTap = new CheckForTap();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tpostDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tif (ev.getEdgeFlags() != 0 && motionPosition < 0) {\n\t\t\t\t\t\t\t\t// If we couldn't find a view to click on, but the down event was touching\n\t\t\t\t\t\t\t\t// the edge, we will bail out and try again. This allows the edge correcting\n\t\t\t\t\t\t\t\t// code in ViewRoot to try to find a nearby view to select\n\t\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\t\tif (mTouchMode == TOUCH_MODE_FLING) {\n\t\t\t\t\t\t\t\t// Stopped a fling. It is a scroll.\n\t\t\t\t\t\t\t\tcreateScrollingCache();\n\t\t\t\t\t\t\t\tmTouchMode = TOUCH_MODE_SCROLL;\n\t\t\t\t\t\t\t\tmMotionCorrection = 0;\n\t\t\t\t\t\t\t\tmotionPosition = findMotionRowX(x);\n\t\t\t\t\t\t\t\tmFlingRunnable.flywheelTouch();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\n\t\t\t\t\tif (motionPosition >= 0) {\n\t\t\t\t\t\t// Remember where the motion event started\n\t\t\t\t\t\tv = getChildAt(motionPosition - mFirstPosition);\n\t\t\t\t\t\tmMotionViewOriginalLeft = v.getLeft();\n\t\t\t\t\t}\n\t\t\t\t\tmMotionX = x;\n\t\t\t\t\tmMotionY = y;\n\t\t\t\t\tmMotionPosition = motionPosition;\n\t\t\t\t\tmLastX = Integer.MIN_VALUE;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\n\t\t\t\tcase MotionEvent.ACTION_MOVE: {\n\t\t\t\t\tfinal int x = (int) ev.getX();\n\t\t\t\t\tdeltaX = x - mMotionX;\n\t\t\t\t\tswitch (mTouchMode) {\n\t\t\t\t\tcase TOUCH_MODE_DOWN:\n\t\t\t\t\tcase TOUCH_MODE_TAP:\n\t\t\t\t\tcase TOUCH_MODE_DONE_WAITING:\n\t\t\t\t\t\t// Check if we have moved far enough that it looks more like a\n\t\t\t\t\t\t// scroll than a tap\n\t\t\t\t\t\tstartScrollIfNeeded(deltaX);\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase TOUCH_MODE_SCROLL:\n\t\t\t\t\t\tif (PROFILE_SCROLLING) {\n\t\t\t\t\t\t\tif (!mScrollProfilingStarted) {\n\t\t\t\t\t\t\t\tDebug.startMethodTracing(\"JessAbsListViewScroll\");\n\t\t\t\t\t\t\t\tmScrollProfilingStarted = true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\tif (x != mLastX) {\n\t\t\t\t\t\t\tdeltaX -= mMotionCorrection;\n\t\t\t\t\t\t\tint incrementalDeltaX = mLastX != Integer.MIN_VALUE ? x - mLastX : deltaX;\n\t\n\t\t\t\t\t\t\t// No need to do all this work if we're not going to move anyway\n\t\t\t\t\t\t\tboolean atEdge = false;\n\t\t\t\t\t\t\tif (incrementalDeltaX != 0) {\n\t\t\t\t\t\t\t\tatEdge = trackMotionScroll(deltaX, incrementalDeltaX);\n\t\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\t\t// Check to see if we have bumped into the scroll limit\n\t\t\t\t\t\t\tif (atEdge && getChildCount() > 0) {\n\t\t\t\t\t\t\t\t// Treat this like we're starting a new scroll from the current\n\t\t\t\t\t\t\t\t// position. This will let the user start scrolling back into\n\t\t\t\t\t\t\t\t// content immediately rather than needing to scroll back to the\n\t\t\t\t\t\t\t\t// point where they hit the limit first.\n\t\t\t\t\t\t\t\tint motionPosition = findMotionRowX(x);\n\t\t\t\t\t\t\t\tif (motionPosition >= 0) {\n\t\t\t\t\t\t\t\t\tfinal View motionView = getChildAt(motionPosition - mFirstPosition);\n\t\t\t\t\t\t\t\t\tmMotionViewOriginalLeft = motionView.getLeft();\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tmMotionX = x;\n\t\t\t\t\t\t\t\tmMotionPosition = motionPosition;\n\t\t\t\t\t\t\t\tinvalidate();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tmLastX = x;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\n\t\t\t\tcase MotionEvent.ACTION_UP: {\n\t\t\t\t\tswitch (mTouchMode) {\n\t\t\t\t\tcase TOUCH_MODE_DOWN:\n\t\t\t\t\tcase TOUCH_MODE_TAP:\n\t\t\t\t\tcase TOUCH_MODE_DONE_WAITING:\n\t\t\t\t\t\tfinal int motionPosition = mMotionPosition;\n\t\t\t\t\t\tfinal View child = getChildAt(motionPosition - mFirstPosition);\n\t\t\t\t\t\tif (child != null && !child.hasFocusable()) {\n\t\t\t\t\t\t\tif (mTouchMode != TOUCH_MODE_DOWN) {\n\t\t\t\t\t\t\t\tchild.setPressed(false);\n\t\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\t\tif (mPerformClick == null) {\n\t\t\t\t\t\t\t\tmPerformClick = new PerformClick();\n\t\t\t\t\t\t\t}\n\t\n\t\t\t\t\t\t\tfinal TwoWayAbsListView.PerformClick performClick = mPerformClick;\n\t\t\t\t\t\t\tperformClick.mChild = child;\n\t\t\t\t\t\t\tperformClick.mClickMotionPosition = motionPosition;\n\t\t\t\t\t\t\tperformClick.rememberWindowAttachCount();\n\t\n\t\t\t\t\t\t\tmResurrectToPosition = motionPosition;\n\t\n\t\t\t\t\t\t\tif (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) {\n\t\t\t\t\t\t\t\tfinal Handler handler = getHandler();\n\t\t\t\t\t\t\t\tif (handler != null) {\n\t\t\t\t\t\t\t\t\thandler.removeCallbacks(mTouchMode == TOUCH_MODE_DOWN ?\n\t\t\t\t\t\t\t\t\t\t\tmPendingCheckForTap : mPendingCheckForLongPress);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tmLayoutMode = LAYOUT_NORMAL;\n\t\t\t\t\t\t\t\tif (!mDataChanged && mAdapter.isEnabled(motionPosition)) {\n\t\t\t\t\t\t\t\t\tmTouchMode = TOUCH_MODE_TAP;\n\t\t\t\t\t\t\t\t\tsetSelectedPositionInt(mMotionPosition);\n\t\t\t\t\t\t\t\t\tlayoutChildren();\n\t\t\t\t\t\t\t\t\tchild.setPressed(true);\n\t\t\t\t\t\t\t\t\tpositionSelector(child);\n\t\t\t\t\t\t\t\t\tsetPressed(true);\n\t\t\t\t\t\t\t\t\tif (mSelector != null) {\n\t\t\t\t\t\t\t\t\t\tDrawable d = mSelector.getCurrent();\n\t\t\t\t\t\t\t\t\t\tif (d != null && d instanceof TransitionDrawable) {\n\t\t\t\t\t\t\t\t\t\t\t((TransitionDrawable) d).resetTransition();\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\tpostDelayed(new Runnable() {\n\t\t\t\t\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\t\t\t\t\tchild.setPressed(false);\n\t\t\t\t\t\t\t\t\t\t\tsetPressed(false);\n\t\t\t\t\t\t\t\t\t\t\tif (!mDataChanged) {\n\t\t\t\t\t\t\t\t\t\t\t\tpost(performClick);\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\tmTouchMode = TOUCH_MODE_REST;\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t}, ViewConfiguration.getPressedStateDuration());\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tmTouchMode = TOUCH_MODE_REST;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t} else if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {\n\t\t\t\t\t\t\t\tpost(performClick);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tmTouchMode = TOUCH_MODE_REST;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase TOUCH_MODE_SCROLL:\n\t\t\t\t\t\tfinal int childCount = getChildCount();\n\t\t\t\t\t\tif (childCount > 0) {\n\t\t\t\t\t\t\tif (mFirstPosition == 0 && getChildAt(0).getLeft() >= mListPadding.left &&\n\t\t\t\t\t\t\t\t\tmFirstPosition + childCount < mItemCount &&\n\t\t\t\t\t\t\t\t\tgetChildAt(childCount - 1).getRight() <=\n\t\t\t\t\t\t\t\t\t\tgetWidth() - mListPadding.right) {\n\t\t\t\t\t\t\t\tmTouchMode = TOUCH_MODE_REST;\n\t\t\t\t\t\t\t\treportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tfinal VelocityTracker velocityTracker = mVelocityTracker;\n\t\t\t\t\t\t\t\tvelocityTracker.computeCurrentVelocity(1000);\n\t\t\t\t\t\t\t\tfinal int initialVelocity = (int) velocityTracker.getXVelocity();\n\t\n\t\t\t\t\t\t\t\tif (Math.abs(initialVelocity) > mMinimumVelocity) {\n\t\t\t\t\t\t\t\t\tif (mFlingRunnable == null) {\n\t\t\t\t\t\t\t\t\t\tmFlingRunnable = new HorizontalFlingRunnable();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\treportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);\n\t\n\t\t\t\t\t\t\t\t\tmFlingRunnable.start(-initialVelocity);\n\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\tmTouchMode = TOUCH_MODE_REST;\n\t\t\t\t\t\t\t\t\treportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tmTouchMode = TOUCH_MODE_REST;\n\t\t\t\t\t\t\treportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\n\t\t\t\t\tsetPressed(false);\n\t\n\t\t\t\t\t// Need to redraw since we probably aren't drawing the selector anymore\n\t\t\t\t\tinvalidate();\n\t\n\t\t\t\t\tfinal Handler handler = getHandler();\n\t\t\t\t\tif (handler != null) {\n\t\t\t\t\t\thandler.removeCallbacks(mPendingCheckForLongPress);\n\t\t\t\t\t}\n\t\n\t\t\t\t\tif (mVelocityTracker != null) {\n\t\t\t\t\t\tmVelocityTracker.recycle();\n\t\t\t\t\t\tmVelocityTracker = null;\n\t\t\t\t\t}\n\t\n\t\t\t\t\tmActivePointerId = INVALID_POINTER;\n\t\n\t\t\t\t\tif (PROFILE_SCROLLING) {\n\t\t\t\t\t\tif (mScrollProfilingStarted) {\n\t\t\t\t\t\t\tDebug.stopMethodTracing();\n\t\t\t\t\t\t\tmScrollProfilingStarted = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\n\t\t\t\tcase MotionEvent.ACTION_CANCEL: {\n\t\t\t\t\tmTouchMode = TOUCH_MODE_REST;\n\t\t\t\t\tsetPressed(false);\n\t\t\t\t\tView motionView = TwoWayAbsListView.this.getChildAt(mMotionPosition - mFirstPosition);\n\t\t\t\t\tif (motionView != null) {\n\t\t\t\t\t\tmotionView.setPressed(false);\n\t\t\t\t\t}\n\t\t\t\t\tclearScrollingCache();\n\t\n\t\t\t\t\tfinal Handler handler = getHandler();\n\t\t\t\t\tif (handler != null) {\n\t\t\t\t\t\thandler.removeCallbacks(mPendingCheckForLongPress);\n\t\t\t\t\t}\n\t\n\t\t\t\t\tif (mVelocityTracker != null) {\n\t\t\t\t\t\tmVelocityTracker.recycle();\n\t\t\t\t\t\tmVelocityTracker = null;\n\t\t\t\t\t}\n\t\n\t\t\t\t\tmActivePointerId = INVALID_POINTER;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn true;\n\t\t}\n\n\n\t\t@Override\n\t\tboolean resurrectSelection() {\n\t\t\tfinal int childCount = getChildCount();\n\n\t\t\tif (childCount <= 0) {\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tint selectedLeft = 0;\n\t\t\tint selectedPos;\n\t\t\tint childrenLeft = mListPadding.top;\n\t\t\tint childrenRight = getRight() - getLeft() - mListPadding.right;\n\t\t\tfinal int firstPosition = mFirstPosition;\n\t\t\tfinal int toPosition = mResurrectToPosition;\n\t\t\tboolean down = true;\n\n\t\t\tif (toPosition >= firstPosition && toPosition < firstPosition + childCount) {\n\t\t\t\tselectedPos = toPosition;\n\n\t\t\t\tfinal View selected = getChildAt(selectedPos - mFirstPosition);\n\t\t\t\tselectedLeft = selected.getLeft();\n\t\t\t\tint selectedRight = selected.getRight();\n\n\t\t\t\t// We are scrolled, don't get in the fade\n\t\t\t\tif (selectedLeft < childrenLeft) {\n\t\t\t\t\tselectedLeft = childrenLeft + getHorizontalFadingEdgeLength();\n\t\t\t\t} else if (selectedRight > childrenRight) {\n\t\t\t\t\tselectedLeft = childrenRight - selected.getMeasuredWidth()\n\t\t\t\t\t- getHorizontalFadingEdgeLength();\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif (toPosition < firstPosition) {\n\t\t\t\t\t// Default to selecting whatever is first\n\t\t\t\t\tselectedPos = firstPosition;\n\t\t\t\t\tfor (int i = 0; i < childCount; i++) {\n\t\t\t\t\t\tfinal View v = getChildAt(i);\n\t\t\t\t\t\tfinal int left = v.getLeft();\n\n\t\t\t\t\t\tif (i == 0) {\n\t\t\t\t\t\t\t// Remember the position of the first item\n\t\t\t\t\t\t\tselectedLeft = left;\n\t\t\t\t\t\t\t// See if we are scrolled at all\n\t\t\t\t\t\t\tif (firstPosition > 0 || left < childrenLeft) {\n\t\t\t\t\t\t\t\t// If we are scrolled, don't select anything that is\n\t\t\t\t\t\t\t\t// in the fade region\n\t\t\t\t\t\t\t\tchildrenLeft += getHorizontalFadingEdgeLength();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (left >= childrenLeft) {\n\t\t\t\t\t\t\t// Found a view whose top is fully visisble\n\t\t\t\t\t\t\tselectedPos = firstPosition + i;\n\t\t\t\t\t\t\tselectedLeft = left;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tfinal int itemCount = mItemCount;\n\t\t\t\t\tdown = false;\n\t\t\t\t\tselectedPos = firstPosition + childCount - 1;\n\n\t\t\t\t\tfor (int i = childCount - 1; i >= 0; i--) {\n\t\t\t\t\t\tfinal View v = getChildAt(i);\n\t\t\t\t\t\tfinal int left = v.getLeft();\n\t\t\t\t\t\tfinal int right = v.getRight();\n\n\t\t\t\t\t\tif (i == childCount - 1) {\n\t\t\t\t\t\t\tselectedLeft = left;\n\t\t\t\t\t\t\tif (firstPosition + childCount < itemCount || right > childrenRight) {\n\t\t\t\t\t\t\t\tchildrenRight -= getHorizontalFadingEdgeLength();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tif (right <= childrenRight) {\n\t\t\t\t\t\t\tselectedPos = firstPosition + i;\n\t\t\t\t\t\t\tselectedLeft = left;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmResurrectToPosition = INVALID_POSITION;\n\t\t\tremoveCallbacks(mFlingRunnable);\n\t\t\tmTouchMode = TOUCH_MODE_REST;\n\t\t\tclearScrollingCache();\n\t\t\tmSpecificTop = selectedLeft;\n\t\t\tselectedPos = lookForSelectablePosition(selectedPos, down);\n\t\t\tif (selectedPos >= firstPosition && selectedPos <= getLastVisiblePosition()) {\n\t\t\t\tmLayoutMode = LAYOUT_SPECIFIC;\n\t\t\t\tsetSelectionInt(selectedPos);\n\t\t\t\tinvokeOnItemScrollListener();\n\t\t\t} else {\n\t\t\t\tselectedPos = INVALID_POSITION;\n\t\t\t}\n\t\t\treportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);\n\n\t\t\treturn selectedPos >= 0;\n\t\t}\n\n\n\t\t@Override\n\t\tboolean trackMotionScroll(int delta, int incrementalDelta) {\n\t\t\tif (DEBUG) Log.i(TAG, \"trackMotionScroll() - deltaX: \" + delta + \" incrDeltaX: \" + incrementalDelta);\n\t\t\tfinal int childCount = getChildCount();\n\t\t\tif (childCount == 0) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tfinal int firstLeft = getChildAt(0).getLeft();\n\t\t\tfinal int lastRight = getChildAt(childCount - 1).getRight();\n\n\t\t\tfinal Rect listPadding = mListPadding;\n\n\t\t\t// FIXME account for grid horizontal spacing too?\n\t\t\tfinal int spaceAbove = listPadding.left - firstLeft;\n\t\t\tfinal int end = getWidth() - listPadding.right;\n\t\t\tfinal int spaceBelow = lastRight - end;\n\n\t\t\tfinal int width = getWidth() - getPaddingRight() - getPaddingLeft();\n\t\t\tif (delta < 0) {\n\t\t\t\tdelta = Math.max(-(width - 1), delta);\n\t\t\t} else {\n\t\t\t\tdelta = Math.min(width - 1, delta);\n\t\t\t}\n\n\t\t\tif (incrementalDelta < 0) {\n\t\t\t\tincrementalDelta = Math.max(-(width - 1), incrementalDelta);\n\t\t\t} else {\n\t\t\t\tincrementalDelta = Math.min(width - 1, incrementalDelta);\n\t\t\t}\n\n\t\t\tfinal int firstPosition = mFirstPosition;\n\n\t\t\tif (firstPosition == 0 && firstLeft >= listPadding.left && delta >= 0) {\n\t\t\t\t// Don't need to move views right if the top of the first position\n\t\t\t\t// is already visible\n\t\t\t\tif (DEBUG) Log.i(TAG, \"trackScrollMotion returning true\");\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (firstPosition + childCount == mItemCount && lastRight <= end && delta <= 0) {\n\t\t\t\t// Don't need to move views left if the bottom of the last position\n\t\t\t\t// is already visible\n\t\t\t\tif (DEBUG) Log.i(TAG, \"trackScrollMotion returning true\");\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tfinal boolean down = incrementalDelta < 0;\n\n\t\t\tfinal boolean inTouchMode = isInTouchMode();\n\t\t\tif (inTouchMode) {\n\t\t\t\thideSelector();\n\t\t\t}\n\n\t\t\tfinal int headerViewsCount = getHeaderViewsCount();\n\t\t\tfinal int footerViewsStart = mItemCount - getFooterViewsCount();\n\n\t\t\tint start = 0;\n\t\t\tint count = 0;\n\n\t\t\tif (down) {\n\t\t\t\tfinal int left = listPadding.left - incrementalDelta;\n\t\t\t\tfor (int i = 0; i < childCount; i++) {\n\t\t\t\t\tfinal View child = getChildAt(i);\n\t\t\t\t\tif (child.getRight() >= left) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcount++;\n\t\t\t\t\t\tint position = firstPosition + i;\n\t\t\t\t\t\tif (position >= headerViewsCount && position < footerViewsStart) {\n\t\t\t\t\t\t\tmRecycler.addScrapView(child);\n\n\t\t\t\t\t\t\tif (ViewDebug.TRACE_RECYCLER) {\n\t\t\t\t\t\t\t\tViewDebug.trace(child,\n\t\t\t\t\t\t\t\t\t\tViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,\n\t\t\t\t\t\t\t\t\t\tfirstPosition + i, -1);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfinal int right = getWidth() - listPadding.right - incrementalDelta;\n\t\t\t\tfor (int i = childCount - 1; i >= 0; i--) {\n\t\t\t\t\tfinal View child = getChildAt(i);\n\t\t\t\t\tif (child.getLeft() <= right) {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tstart = i;\n\t\t\t\t\t\tcount++;\n\t\t\t\t\t\tint position = firstPosition + i;\n\t\t\t\t\t\tif (position >= headerViewsCount && position < footerViewsStart) {\n\t\t\t\t\t\t\tmRecycler.addScrapView(child);\n\n\t\t\t\t\t\t\tif (ViewDebug.TRACE_RECYCLER) {\n\t\t\t\t\t\t\t\tViewDebug.trace(child,\n\t\t\t\t\t\t\t\t\t\tViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP,\n\t\t\t\t\t\t\t\t\t\tfirstPosition + i, -1);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmMotionViewNewLeft = mMotionViewOriginalLeft + delta;\n\n\t\t\tmBlockLayoutRequests = true;\n\n\t\t\tif (count > 0) {\n\t\t\t\tdetachViewsFromParent(start, count);\n\t\t\t}\n\t\t\toffsetChildrenLeftAndRight(incrementalDelta);\n\n\t\t\tif (down) {\n\t\t\t\tmFirstPosition += count;\n\t\t\t}\n\n\t\t\tinvalidate();\n\n\t\t\tfinal int absIncrementalDelta = Math.abs(incrementalDelta);\n\t\t\tif (spaceAbove < absIncrementalDelta|| spaceBelow < absIncrementalDelta) {\n\t\t\t\tfillGap(down);\n\t\t\t}\n\n\t\t\tif (!inTouchMode && mSelectedPosition != INVALID_POSITION) {\n\t\t\t\tfinal int childIndex = mSelectedPosition - mFirstPosition;\n\t\t\t\tif (childIndex >= 0 && childIndex < getChildCount()) {\n\t\t\t\t\tpositionSelector(getChildAt(childIndex));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmBlockLayoutRequests = false;\n\n\t\t\tinvokeOnItemScrollListener();\n\t\t\t//awakenScrollBars();\n\t\t\tif (DEBUG) Log.i(TAG, \"trackScrollMotion returning false - mFirstPosition: \" + mFirstPosition);\n\t\t\treturn false;\n\t\t}\n\n\n\t\t/**\n\t\t * Responsible for fling behavior. Use {@link #start(int)} to\n\t\t * initiate a fling. Each frame of the fling is handled in {@link #run()}.\n\t\t * A FlingRunnable will keep re-posting itself until the fling is done.\n\t\t *\n\t\t */\n\t\tprivate class HorizontalFlingRunnable extends FlingRunnable {\n\t\t\t/**\n\t\t\t * X value reported by mScroller on the previous fling\n\t\t\t */\n\t\t\tprotected int mLastFlingX;\n\t\t\t\n\t\t\t@Override\n\t\t\tvoid start(int initialVelocity) {\n\t\t\t\tint initialX = initialVelocity < 0 ? Integer.MAX_VALUE : 0;\n\t\t\t\tmLastFlingX = initialX;\n\t\t\t\tmScroller.fling(initialX, 0, initialVelocity, 0,\n\t\t\t\t\t\t0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE);\n\t\t\t\tmTouchMode = TOUCH_MODE_FLING;\n\t\t\t\tpost(this);\n\n\t\t\t\tif (PROFILE_FLINGING) {\n\t\t\t\t\tif (!mFlingProfilingStarted) {\n\t\t\t\t\t\tDebug.startMethodTracing(\"AbsListViewFling\");\n\t\t\t\t\t\tmFlingProfilingStarted = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tvoid startScroll(int distance, int duration) {\n\t\t\t\tint initialX = distance < 0 ? Integer.MAX_VALUE : 0;\n\t\t\t\tmLastFlingX = initialX;\n\t\t\t\tmScroller.startScroll(initialX, 0, distance, 0, duration);\n\t\t\t\tmTouchMode = TOUCH_MODE_FLING;\n\t\t\t\tpost(this);\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tswitch (mTouchMode) {\n\t\t\t\tdefault:\n\t\t\t\t\treturn;\n\n\t\t\t\tcase TOUCH_MODE_FLING: {\n\t\t\t\t\tif (mItemCount == 0 || getChildCount() == 0) {\n\t\t\t\t\t\tendFling();\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tfinal Scroller scroller = mScroller;\n\t\t\t\t\tboolean more = scroller.computeScrollOffset();\n\t\t\t\t\tfinal int x = scroller.getCurrX();\n\n\t\t\t\t\t// Flip sign to convert finger direction to list items direction\n\t\t\t\t\t// (e.g. finger moving down means list is moving towards the top)\n\t\t\t\t\tint delta = mLastFlingX - x;\n\n\t\t\t\t\t// Pretend that each frame of a fling scroll is a touch scroll\n\t\t\t\t\tif (delta > 0) {\n\t\t\t\t\t\t// List is moving towards the top. Use first view as mMotionPosition\n\t\t\t\t\t\tmMotionPosition = mFirstPosition;\n\t\t\t\t\t\tfinal View firstView = getChildAt(0);\n\t\t\t\t\t\tmMotionViewOriginalLeft = firstView.getLeft();\n\n\t\t\t\t\t\t// Don't fling more than 1 screen\n\t\t\t\t\t\tdelta = Math.min(getWidth() - getPaddingRight() - getPaddingLeft() - 1, delta);\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// List is moving towards the bottom. Use last view as mMotionPosition\n\t\t\t\t\t\tint offsetToLast = getChildCount() - 1;\n\t\t\t\t\t\tmMotionPosition = mFirstPosition + offsetToLast;\n\n\t\t\t\t\t\tfinal View lastView = getChildAt(offsetToLast);\n\t\t\t\t\t\tmMotionViewOriginalLeft = lastView.getLeft();\n\n\t\t\t\t\t\t// Don't fling more than 1 screen\n\t\t\t\t\t\tdelta = Math.max(-(getWidth() - getPaddingRight() - getPaddingLeft() - 1), delta);\n\t\t\t\t\t}\n\n\t\t\t\t\tfinal boolean atEnd = trackMotionScroll(delta, delta);\n\n\t\t\t\t\tif (more && !atEnd) {\n\t\t\t\t\t\tinvalidate();\n\t\t\t\t\t\tmLastFlingX = x;\n\t\t\t\t\t\tpost(this);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tendFling();\n\n\t\t\t\t\t\tif (PROFILE_FLINGING) {\n\t\t\t\t\t\t\tif (mFlingProfilingStarted) {\n\t\t\t\t\t\t\t\tDebug.stopMethodTracing();\n\t\t\t\t\t\t\t\tmFlingProfilingStarted = false;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t}\n\t\t\t\n\t\t\t\n\t\t\tprivate static final int FLYWHEEL_TIMEOUT = 40; // milliseconds\n\t\n\t\t\tpublic void flywheelTouch() {\n\t\t\t\tif(mCheckFlywheel == null) {\n\t\t\t\t\tmCheckFlywheel = new Runnable() {\n\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\tfinal VelocityTracker vt = mVelocityTracker;\n\t\t\t\t\t\t\tif (vt == null) {\n\t\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tvt.computeCurrentVelocity(1000, mMaximumVelocity);\n\t\t\t\t\t\t\tfinal float xvel = -vt.getXVelocity();\n\n\t\t\t\t\t\t\tif (Math.abs(xvel) >= mMinimumVelocity\n\t\t\t\t\t\t\t\t\t&& isScrollingInDirection(0, xvel)) {\n\t\t\t\t\t\t\t\t// Keep the fling alive a little longer\n\t\t\t\t\t\t\t\tpostDelayed(this, FLYWHEEL_TIMEOUT);\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\tendFling();\n\t\t\t\t\t\t\t\tmTouchMode = TOUCH_MODE_SCROLL;\n\t\t\t\t\t\t\t\treportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t\tpostDelayed(mCheckFlywheel, FLYWHEEL_TIMEOUT);\n\t\t\t}\n\t\t}\n\n\n\t\tclass HorizontalPositionScroller extends PositionScroller {\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tfinal int listWidth = getWidth();\n\t\t\t\tfinal int firstPos = mFirstPosition;\n\n\t\t\t\tswitch (mMode) {\n\t\t\t\tcase MOVE_DOWN_POS: {\n\t\t\t\t\tfinal int lastViewIndex = getChildCount() - 1;\n\t\t\t\t\tfinal int lastPos = firstPos + lastViewIndex;\n\n\t\t\t\t\tif (lastViewIndex < 0) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (lastPos == mLastSeenPos) {\n\t\t\t\t\t\t// No new views, let things keep going.\n\t\t\t\t\t\tpost(this);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tfinal View lastView = getChildAt(lastViewIndex);\n\t\t\t\t\tfinal int lastViewWidth = lastView.getWidth();\n\t\t\t\t\tfinal int lastViewLeft = lastView.getLeft();\n\t\t\t\t\tfinal int lastViewPixelsShowing = listWidth - lastViewLeft;\n\t\t\t\t\tfinal int extraScroll = lastPos < mItemCount - 1 ? mExtraScroll : mListPadding.right;\n\n\t\t\t\t\tsmoothScrollBy(lastViewWidth - lastViewPixelsShowing + extraScroll,\n\t\t\t\t\t\t\tmScrollDuration);\n\n\t\t\t\t\tmLastSeenPos = lastPos;\n\t\t\t\t\tif (lastPos < mTargetPos) {\n\t\t\t\t\t\tpost(this);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase MOVE_DOWN_BOUND: {\n\t\t\t\t\tfinal int nextViewIndex = 1;\n\t\t\t\t\tfinal int childCount = getChildCount();\n\n\t\t\t\t\tif (firstPos == mBoundPos || childCount <= nextViewIndex\n\t\t\t\t\t\t\t|| firstPos + childCount >= mItemCount) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tfinal int nextPos = firstPos + nextViewIndex;\n\n\t\t\t\t\tif (nextPos == mLastSeenPos) {\n\t\t\t\t\t\t// No new views, let things keep going.\n\t\t\t\t\t\tpost(this);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tfinal View nextView = getChildAt(nextViewIndex);\n\t\t\t\t\tfinal int nextViewWidth = nextView.getWidth();\n\t\t\t\t\tfinal int nextViewLeft = nextView.getLeft();\n\t\t\t\t\tfinal int extraScroll = mExtraScroll;\n\t\t\t\t\tif (nextPos < mBoundPos) {\n\t\t\t\t\t\tsmoothScrollBy(Math.max(0, nextViewWidth + nextViewLeft - extraScroll),\n\t\t\t\t\t\t\t\tmScrollDuration);\n\n\t\t\t\t\t\tmLastSeenPos = nextPos;\n\n\t\t\t\t\t\tpost(this);\n\t\t\t\t\t} else  {\n\t\t\t\t\t\tif (nextViewLeft > extraScroll) {\n\t\t\t\t\t\t\tsmoothScrollBy(nextViewLeft - extraScroll, mScrollDuration);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase MOVE_UP_POS: {\n\t\t\t\t\tif (firstPos == mLastSeenPos) {\n\t\t\t\t\t\t// No new views, let things keep going.\n\t\t\t\t\t\tpost(this);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tfinal View firstView = getChildAt(0);\n\t\t\t\t\tif (firstView == null) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tfinal int firstViewLeft = firstView.getLeft();\n\t\t\t\t\tfinal int extraScroll = firstPos > 0 ? mExtraScroll : mListPadding.left;\n\n\t\t\t\t\tsmoothScrollBy(firstViewLeft - extraScroll, mScrollDuration);\n\n\t\t\t\t\tmLastSeenPos = firstPos;\n\n\t\t\t\t\tif (firstPos > mTargetPos) {\n\t\t\t\t\t\tpost(this);\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tcase MOVE_UP_BOUND: {\n\t\t\t\t\tfinal int lastViewIndex = getChildCount() - 2;\n\t\t\t\t\tif (lastViewIndex < 0) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tfinal int lastPos = firstPos + lastViewIndex;\n\n\t\t\t\t\tif (lastPos == mLastSeenPos) {\n\t\t\t\t\t\t// No new views, let things keep going.\n\t\t\t\t\t\tpost(this);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\n\t\t\t\t\tfinal View lastView = getChildAt(lastViewIndex);\n\t\t\t\t\tfinal int lastViewWidth = lastView.getWidth();\n\t\t\t\t\tfinal int lastViewLeft = lastView.getLeft();\n\t\t\t\t\tfinal int lastViewPixelsShowing = listWidth - lastViewLeft;\n\t\t\t\t\tmLastSeenPos = lastPos;\n\t\t\t\t\tif (lastPos > mBoundPos) {\n\t\t\t\t\t\tsmoothScrollBy(-(lastViewPixelsShowing - mExtraScroll), mScrollDuration);\n\t\t\t\t\t\tpost(this);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfinal int right = listWidth - mExtraScroll;\n\t\t\t\t\t\tfinal int lastViewRight = lastViewLeft + lastViewWidth;\n\t\t\t\t\t\tif (right > lastViewRight) {\n\t\t\t\t\t\t\tsmoothScrollBy(-(right - lastViewRight), mScrollDuration);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tdefault:\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t}\n\n\n\tprivate void initOrResetVelocityTracker() {\n\t\tif (mVelocityTracker == null) {\n\t\t\tmVelocityTracker = VelocityTracker.obtain();\n\t\t} else {\n\t\t\tmVelocityTracker.clear();\n\t\t}\n\t}\n\n}\n\n\n\n"
  },
  {
    "path": "lib/src/com/jess/ui/TwoWayAdapterView.java",
    "content": "/*\n * A modified version of the Android AdapterView\n *\n * Copyright 2012 Jess Anders\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.jess.ui;\n\n\nimport android.content.Context;\nimport android.database.DataSetObserver;\nimport android.os.Handler;\nimport android.os.Parcelable;\nimport android.os.SystemClock;\nimport android.util.AttributeSet;\nimport android.util.SparseArray;\nimport android.view.ContextMenu;\nimport android.view.SoundEffectConstants;\nimport android.view.View;\nimport android.view.ViewDebug;\nimport android.view.ViewGroup;\nimport android.view.ContextMenu.ContextMenuInfo;\nimport android.widget.Adapter;\n\n\n/**\n * A TwoWayAdapterView is a view whose children are determined by an {@link Adapter}.\n *\n * <p>\n * See {@link TwoWayAbsListView} and {@link TwoWayGridView} for commonly used subclasses of TwoWayAdapterView.\n */\npublic abstract class TwoWayAdapterView<T extends Adapter> extends ViewGroup {\n\n\t/**\n\t * The item view type returned by {@link Adapter#getItemViewType(int)} when\n\t * the adapter does not want the item's view recycled.\n\t */\n\tpublic static final int ITEM_VIEW_TYPE_IGNORE = -1;\n\n\t/**\n\t * The item view type returned by {@link Adapter#getItemViewType(int)} when\n\t * the item is a header or footer.\n\t */\n\tpublic static final int ITEM_VIEW_TYPE_HEADER_OR_FOOTER = -2;\n\n\tprotected Context mContext = null;\n\t\n\t/**\n\t * Whether the view displays its items vertically or horizontally\n\t */\n\tprotected boolean mIsVertical = true;\n\n\t/**\n\t * The position of the first child displayed\n\t */\n\t@ViewDebug.ExportedProperty\n\tint mFirstPosition = 0;\n\n\t/**\n\t * The offset in pixels from the top of the AdapterView to the top or left\n\t * of the view to select during the next layout.\n\t */\n\tint mSpecificTop;\n\n\t/**\n\t * Position from which to start looking for mSyncRowId\n\t */\n\tint mSyncPosition;\n\n\t/**\n\t * Row id to look for when data has changed\n\t */\n\tlong mSyncRowId = INVALID_ROW_ID;\n\n\t/**\n\t * Size of the view when mSyncPosition and mSyncRowId where set\n\t */\n\tlong mSyncSize;\n\n\t/**\n\t * True if we need to sync to mSyncRowId\n\t */\n\tboolean mNeedSync = false;\n\n\t/**\n\t * Indicates whether to sync based on the selection or position. Possible\n\t * values are {@link #SYNC_SELECTED_POSITION} or\n\t * {@link #SYNC_FIRST_POSITION}.\n\t */\n\tint mSyncMode;\n\n\t/**\n\t * Our height after the last layout\n\t */\n\tprivate int mLayoutHeight;\n\n\t/**\n\t * Our width after the last layout\n\t */\n\tprivate int mLayoutWidth;\n\n\t/**\n\t * Sync based on the selected child\n\t */\n\tstatic final int SYNC_SELECTED_POSITION = 0;\n\n\t/**\n\t * Sync based on the first child displayed\n\t */\n\tstatic final int SYNC_FIRST_POSITION = 1;\n\n\t/**\n\t * Maximum amount of time to spend in {@link #findSyncPosition()}\n\t */\n\tstatic final int SYNC_MAX_DURATION_MILLIS = 100;\n\n\t/**\n\t * Indicates that this view is currently being laid out.\n\t */\n\tboolean mInLayout = false;\n\n\t/**\n\t * The listener that receives notifications when an item is selected.\n\t */\n\tOnItemSelectedListener mOnItemSelectedListener;\n\n\t/**\n\t * The listener that receives notifications when an item is clicked.\n\t */\n\tOnItemClickListener mOnItemClickListener;\n\n\t/**\n\t * The listener that receives notifications when an item is long clicked.\n\t */\n\tOnItemLongClickListener mOnItemLongClickListener;\n\n\t/**\n\t * True if the data has changed since the last layout\n\t */\n\tboolean mDataChanged;\n\n\t/**\n\t * The position within the adapter's data set of the item to select\n\t * during the next layout.\n\t */\n\t@ViewDebug.ExportedProperty\n\tint mNextSelectedPosition = INVALID_POSITION;\n\n\t/**\n\t * The item id of the item to select during the next layout.\n\t */\n\tlong mNextSelectedRowId = INVALID_ROW_ID;\n\n\t/**\n\t * The position within the adapter's data set of the currently selected item.\n\t */\n\t@ViewDebug.ExportedProperty\n\tint mSelectedPosition = INVALID_POSITION;\n\n\t/**\n\t * The item id of the currently selected item.\n\t */\n\tlong mSelectedRowId = INVALID_ROW_ID;\n\n\t/**\n\t * View to show if there are no items to show.\n\t */\n\tprivate View mEmptyView;\n\n\t/**\n\t * The number of items in the current adapter.\n\t */\n\t@ViewDebug.ExportedProperty\n\tint mItemCount;\n\n\t/**\n\t * The number of items in the adapter before a data changed event occured.\n\t */\n\tint mOldItemCount;\n\n\t/**\n\t * Represents an invalid position. All valid positions are in the range 0 to 1 less than the\n\t * number of items in the current adapter.\n\t */\n\tpublic static final int INVALID_POSITION = -1;\n\n\t/**\n\t * Represents an empty or invalid row id\n\t */\n\tpublic static final long INVALID_ROW_ID = Long.MIN_VALUE;\n\n\t/**\n\t * The last selected position we used when notifying\n\t */\n\tint mOldSelectedPosition = INVALID_POSITION;\n\n\t/**\n\t * The id of the last selected position we used when notifying\n\t */\n\tlong mOldSelectedRowId = INVALID_ROW_ID;\n\n\t/**\n\t * Indicates what focusable state is requested when calling setFocusable().\n\t * In addition to this, this view has other criteria for actually\n\t * determining the focusable state (such as whether its empty or the text\n\t * filter is shown).\n\t *\n\t * @see #setFocusable(boolean)\n\t * @see #checkFocus()\n\t */\n\tprivate boolean mDesiredFocusableState;\n\tprivate boolean mDesiredFocusableInTouchModeState;\n\n\tprivate SelectionNotifier mSelectionNotifier;\n\t/**\n\t * When set to true, calls to requestLayout() will not propagate up the parent hierarchy.\n\t * This is used to layout the children during a layout pass.\n\t */\n\tboolean mBlockLayoutRequests = false;\n\n\tpublic TwoWayAdapterView(Context context) {\n\t\tsuper(context);\n\t\tmContext = context;\n\t}\n\n\tpublic TwoWayAdapterView(Context context, AttributeSet attrs) {\n\t\tsuper(context, attrs);\n\t\tmContext = context;\n\t}\n\n\tpublic TwoWayAdapterView(Context context, AttributeSet attrs, int defStyle) {\n\t\tsuper(context, attrs, defStyle);\n\t\tmContext = context;\n\t}\n\n\n\t/**\n\t * Interface definition for a callback to be invoked when an item in this\n\t * AdapterView has been clicked.\n\t */\n\tpublic interface OnItemClickListener {\n\n\t\t/**\n\t\t * Callback method to be invoked when an item in this AdapterView has\n\t\t * been clicked.\n\t\t * <p>\n\t\t * Implementers can call getItemAtPosition(position) if they need\n\t\t * to access the data associated with the selected item.\n\t\t *\n\t\t * @param parent The AdapterView where the click happened.\n\t\t * @param view The view within the AdapterView that was clicked (this\n\t\t *            will be a view provided by the adapter)\n\t\t * @param position The position of the view in the adapter.\n\t\t * @param id The row id of the item that was clicked.\n\t\t */\n\t\tvoid onItemClick(TwoWayAdapterView<?> parent, View view, int position, long id);\n\t}\n\n\t/**\n\t * Register a callback to be invoked when an item in this AdapterView has\n\t * been clicked.\n\t *\n\t * @param listener The callback that will be invoked.\n\t */\n\tpublic void setOnItemClickListener(OnItemClickListener listener) {\n\t\tmOnItemClickListener = listener;\n\t}\n\n\t/**\n\t * @return The callback to be invoked with an item in this AdapterView has\n\t *         been clicked, or null id no callback has been set.\n\t */\n\tpublic final OnItemClickListener getOnItemClickListener() {\n\t\treturn mOnItemClickListener;\n\t}\n\n\t/**\n\t * Call the OnItemClickListener, if it is defined.\n\t *\n\t * @param view The view within the AdapterView that was clicked.\n\t * @param position The position of the view in the adapter.\n\t * @param id The row id of the item that was clicked.\n\t * @return True if there was an assigned OnItemClickListener that was\n\t *         called, false otherwise is returned.\n\t */\n\tpublic boolean performItemClick(View view, int position, long id) {\n\t\tif (mOnItemClickListener != null) {\n\t\t\tplaySoundEffect(SoundEffectConstants.CLICK);\n\t\t\tmOnItemClickListener.onItemClick(this, view, position, id);\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Interface definition for a callback to be invoked when an item in this\n\t * view has been clicked and held.\n\t */\n\tpublic interface OnItemLongClickListener {\n\t\t/**\n\t\t * Callback method to be invoked when an item in this view has been\n\t\t * clicked and held.\n\t\t *\n\t\t * Implementers can call getItemAtPosition(position) if they need to access\n\t\t * the data associated with the selected item.\n\t\t *\n\t\t * @param parent The AbsListView where the click happened\n\t\t * @param view The view within the AbsListView that was clicked\n\t\t * @param position The position of the view in the list\n\t\t * @param id The row id of the item that was clicked\n\t\t *\n\t\t * @return true if the callback consumed the long click, false otherwise\n\t\t */\n\t\tboolean onItemLongClick(TwoWayAdapterView<?> parent, View view, int position, long id);\n\t}\n\n\n\t/**\n\t * Register a callback to be invoked when an item in this AdapterView has\n\t * been clicked and held\n\t *\n\t * @param listener The callback that will run\n\t */\n\tpublic void setOnItemLongClickListener(OnItemLongClickListener listener) {\n\t\tif (!isLongClickable()) {\n\t\t\tsetLongClickable(true);\n\t\t}\n\t\tmOnItemLongClickListener = listener;\n\t}\n\n\t/**\n\t * @return The callback to be invoked with an item in this AdapterView has\n\t *         been clicked and held, or null id no callback as been set.\n\t */\n\tpublic final OnItemLongClickListener getOnItemLongClickListener() {\n\t\treturn mOnItemLongClickListener;\n\t}\n\n\t/**\n\t * Interface definition for a callback to be invoked when\n\t * an item in this view has been selected.\n\t */\n\tpublic interface OnItemSelectedListener {\n\t\t/**\n\t\t * Callback method to be invoked when an item in this view has been\n\t\t * selected.\n\t\t *\n\t\t * Impelmenters can call getItemAtPosition(position) if they need to access the\n\t\t * data associated with the selected item.\n\t\t *\n\t\t * @param parent The AdapterView where the selection happened\n\t\t * @param view The view within the AdapterView that was clicked\n\t\t * @param position The position of the view in the adapter\n\t\t * @param id The row id of the item that is selected\n\t\t */\n\t\tvoid onItemSelected(TwoWayAdapterView<?> parent, View view, int position, long id);\n\n\t\t/**\n\t\t * Callback method to be invoked when the selection disappears from this\n\t\t * view. The selection can disappear for instance when touch is activated\n\t\t * or when the adapter becomes empty.\n\t\t *\n\t\t * @param parent The AdapterView that now contains no selected item.\n\t\t */\n\t\tvoid onNothingSelected(TwoWayAdapterView<?> parent);\n\t}\n\n\n\t/**\n\t * Register a callback to be invoked when an item in this AdapterView has\n\t * been selected.\n\t *\n\t * @param listener The callback that will run\n\t */\n\tpublic void setOnItemSelectedListener(OnItemSelectedListener listener) {\n\t\tmOnItemSelectedListener = listener;\n\t}\n\n\tpublic final OnItemSelectedListener getOnItemSelectedListener() {\n\t\treturn mOnItemSelectedListener;\n\t}\n\n\t/**\n\t * Extra menu information provided to the\n\t * {@link android.view.View.OnCreateContextMenuListener#onCreateContextMenu(ContextMenu, View, ContextMenuInfo) }\n\t * callback when a context menu is brought up for this AdapterView.\n\t *\n\t */\n\tpublic static class AdapterContextMenuInfo implements ContextMenu.ContextMenuInfo {\n\n\t\tpublic AdapterContextMenuInfo(View targetView, int position, long id) {\n\t\t\tthis.targetView = targetView;\n\t\t\tthis.position = position;\n\t\t\tthis.id = id;\n\t\t}\n\n\t\t/**\n\t\t * The child view for which the context menu is being displayed. This\n\t\t * will be one of the children of this AdapterView.\n\t\t */\n\t\tpublic View targetView;\n\n\t\t/**\n\t\t * The position in the adapter for which the context menu is being\n\t\t * displayed.\n\t\t */\n\t\tpublic int position;\n\n\t\t/**\n\t\t * The row id of the item for which the context menu is being displayed.\n\t\t */\n\t\tpublic long id;\n\t}\n\n\t/**\n\t * Returns the adapter currently associated with this widget.\n\t *\n\t * @return The adapter used to provide this view's content.\n\t */\n\tpublic abstract T getAdapter();\n\n\t/**\n\t * Sets the adapter that provides the data and the views to represent the data\n\t * in this widget.\n\t *\n\t * @param adapter The adapter to use to create this view's content.\n\t */\n\tpublic abstract void setAdapter(T adapter);\n\n\t/**\n\t * This method is not supported and throws an UnsupportedOperationException when called.\n\t *\n\t * @param child Ignored.\n\t *\n\t * @throws UnsupportedOperationException Every time this method is invoked.\n\t */\n\t@Override\n\tpublic void addView(View child) {\n\t\tthrow new UnsupportedOperationException(\"addView(View) is not supported in AdapterView\");\n\t}\n\n\t/**\n\t * This method is not supported and throws an UnsupportedOperationException when called.\n\t *\n\t * @param child Ignored.\n\t * @param index Ignored.\n\t *\n\t * @throws UnsupportedOperationException Every time this method is invoked.\n\t */\n\t@Override\n\tpublic void addView(View child, int index) {\n\t\tthrow new UnsupportedOperationException(\"addView(View, int) is not supported in AdapterView\");\n\t}\n\n\t/**\n\t * This method is not supported and throws an UnsupportedOperationException when called.\n\t *\n\t * @param child Ignored.\n\t * @param params Ignored.\n\t *\n\t * @throws UnsupportedOperationException Every time this method is invoked.\n\t */\n\t@Override\n\tpublic void addView(View child, LayoutParams params) {\n\t\tthrow new UnsupportedOperationException(\"addView(View, LayoutParams) \"\n\t\t\t\t+ \"is not supported in AdapterView\");\n\t}\n\n\t/**\n\t * This method is not supported and throws an UnsupportedOperationException when called.\n\t *\n\t * @param child Ignored.\n\t * @param index Ignored.\n\t * @param params Ignored.\n\t *\n\t * @throws UnsupportedOperationException Every time this method is invoked.\n\t */\n\t@Override\n\tpublic void addView(View child, int index, LayoutParams params) {\n\t\tthrow new UnsupportedOperationException(\"addView(View, int, LayoutParams) \"\n\t\t\t\t+ \"is not supported in AdapterView\");\n\t}\n\n\t/**\n\t * This method is not supported and throws an UnsupportedOperationException when called.\n\t *\n\t * @param child Ignored.\n\t *\n\t * @throws UnsupportedOperationException Every time this method is invoked.\n\t */\n\t@Override\n\tpublic void removeView(View child) {\n\t\tthrow new UnsupportedOperationException(\"removeView(View) is not supported in AdapterView\");\n\t}\n\n\t/**\n\t * This method is not supported and throws an UnsupportedOperationException when called.\n\t *\n\t * @param index Ignored.\n\t *\n\t * @throws UnsupportedOperationException Every time this method is invoked.\n\t */\n\t@Override\n\tpublic void removeViewAt(int index) {\n\t\tthrow new UnsupportedOperationException(\"removeViewAt(int) is not supported in AdapterView\");\n\t}\n\n\t/**\n\t * This method is not supported and throws an UnsupportedOperationException when called.\n\t *\n\t * @throws UnsupportedOperationException Every time this method is invoked.\n\t */\n\t@Override\n\tpublic void removeAllViews() {\n\t\tthrow new UnsupportedOperationException(\"removeAllViews() is not supported in AdapterView\");\n\t}\n\n\t@Override\n\tprotected void onLayout(boolean changed, int left, int top, int right, int bottom) {\n\t\tmLayoutHeight = getHeight();\n\t\tmLayoutWidth = getWidth();\n\t}\n\n\t/**\n\t * Return the position of the currently selected item within the adapter's data set\n\t *\n\t * @return int Position (starting at 0), or {@link #INVALID_POSITION} if there is nothing selected.\n\t */\n\t@ViewDebug.CapturedViewProperty\n\tpublic int getSelectedItemPosition() {\n\t\treturn mNextSelectedPosition;\n\t}\n\n\t/**\n\t * @return The id corresponding to the currently selected item, or {@link #INVALID_ROW_ID}\n\t * if nothing is selected.\n\t */\n\t@ViewDebug.CapturedViewProperty\n\tpublic long getSelectedItemId() {\n\t\treturn mNextSelectedRowId;\n\t}\n\n\t/**\n\t * @return The view corresponding to the currently selected item, or null\n\t * if nothing is selected\n\t */\n\tpublic abstract View getSelectedView();\n\n\t/**\n\t * @return The data corresponding to the currently selected item, or\n\t * null if there is nothing selected.\n\t */\n\tpublic Object getSelectedItem() {\n\t\tT adapter = getAdapter();\n\t\tint selection = getSelectedItemPosition();\n\t\tif (adapter != null && adapter.getCount() > 0 && selection >= 0) {\n\t\t\treturn adapter.getItem(selection);\n\t\t} else {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\t/**\n\t * @return The number of items owned by the Adapter associated with this\n\t *         AdapterView. (This is the number of data items, which may be\n\t *         larger than the number of visible view.)\n\t */\n\t@ViewDebug.CapturedViewProperty\n\tpublic int getCount() {\n\t\treturn mItemCount;\n\t}\n\n\t/**\n\t * Get the position within the adapter's data set for the view, where view is a an adapter item\n\t * or a descendant of an adapter item.\n\t *\n\t * @param view an adapter item, or a descendant of an adapter item. This must be visible in this\n\t *        AdapterView at the time of the call.\n\t * @return the position within the adapter's data set of the view, or {@link #INVALID_POSITION}\n\t *         if the view does not correspond to a list item (or it is not currently visible).\n\t */\n\tpublic int getPositionForView(View view) {\n\t\tView listItem = view;\n\t\ttry {\n\t\t\tView v;\n\t\t\twhile (!(v = (View) listItem.getParent()).equals(this)) {\n\t\t\t\tlistItem = v;\n\t\t\t}\n\t\t} catch (ClassCastException e) {\n\t\t\t// We made it up to the window without find this list view\n\t\t\treturn INVALID_POSITION;\n\t\t}\n\n\t\t// Search the children for the list item\n\t\tfinal int childCount = getChildCount();\n\t\tfor (int i = 0; i < childCount; i++) {\n\t\t\tif (getChildAt(i).equals(listItem)) {\n\t\t\t\treturn mFirstPosition + i;\n\t\t\t}\n\t\t}\n\n\t\t// Child not found!\n\t\treturn INVALID_POSITION;\n\t}\n\n\t/**\n\t * Returns the position within the adapter's data set for the first item\n\t * displayed on screen.\n\t *\n\t * @return The position within the adapter's data set\n\t */\n\tpublic int getFirstVisiblePosition() {\n\t\treturn mFirstPosition;\n\t}\n\n\t/**\n\t * Returns the position within the adapter's data set for the last item\n\t * displayed on screen.\n\t *\n\t * @return The position within the adapter's data set\n\t */\n\tpublic int getLastVisiblePosition() {\n\t\treturn mFirstPosition + getChildCount() - 1;\n\t}\n\n\t/**\n\t * Sets the currently selected item. To support accessibility subclasses that\n\t * override this method must invoke the overriden super method first.\n\t *\n\t * @param position Index (starting at 0) of the data item to be selected.\n\t */\n\tpublic abstract void setSelection(int position);\n\n\t/**\n\t * Sets the view to show if the adapter is empty\n\t */\n\tpublic void setEmptyView(View emptyView) {\n\t\tmEmptyView = emptyView;\n\n\t\tfinal T adapter = getAdapter();\n\t\tfinal boolean empty = ((adapter == null) || adapter.isEmpty());\n\t\tupdateEmptyStatus(empty);\n\t}\n\n\t/**\n\t * When the current adapter is empty, the AdapterView can display a special view\n\t * call the empty view. The empty view is used to provide feedback to the user\n\t * that no data is available in this AdapterView.\n\t *\n\t * @return The view to show if the adapter is empty.\n\t */\n\tpublic View getEmptyView() {\n\t\treturn mEmptyView;\n\t}\n\n\t/**\n\t * Indicates whether this view is in filter mode. Filter mode can for instance\n\t * be enabled by a user when typing on the keyboard.\n\t *\n\t * @return True if the view is in filter mode, false otherwise.\n\t */\n\tboolean isInFilterMode() {\n\t\treturn false;\n\t}\n\n\t@Override\n\tpublic void setFocusable(boolean focusable) {\n\t\tfinal T adapter = getAdapter();\n\t\tfinal boolean empty = adapter == null || adapter.getCount() == 0;\n\n\t\tmDesiredFocusableState = focusable;\n\t\tif (!focusable) {\n\t\t\tmDesiredFocusableInTouchModeState = false;\n\t\t}\n\n\t\tsuper.setFocusable(focusable && (!empty || isInFilterMode()));\n\t}\n\n\t@Override\n\tpublic void setFocusableInTouchMode(boolean focusable) {\n\t\tfinal T adapter = getAdapter();\n\t\tfinal boolean empty = adapter == null || adapter.getCount() == 0;\n\n\t\tmDesiredFocusableInTouchModeState = focusable;\n\t\tif (focusable) {\n\t\t\tmDesiredFocusableState = true;\n\t\t}\n\n\t\tsuper.setFocusableInTouchMode(focusable && (!empty || isInFilterMode()));\n\t}\n\n\tvoid checkFocus() {\n\t\tfinal T adapter = getAdapter();\n\t\tfinal boolean empty = adapter == null || adapter.getCount() == 0;\n\t\tfinal boolean focusable = !empty || isInFilterMode();\n\t\t// The order in which we set focusable in touch mode/focusable may matter\n\t\t// for the client, see View.setFocusableInTouchMode() comments for more\n\t\t// details\n\t\tsuper.setFocusableInTouchMode(focusable && mDesiredFocusableInTouchModeState);\n\t\tsuper.setFocusable(focusable && mDesiredFocusableState);\n\t\tif (mEmptyView != null) {\n\t\t\tupdateEmptyStatus((adapter == null) || adapter.isEmpty());\n\t\t}\n\t}\n\n\t/**\n\t * Update the status of the list based on the empty parameter.  If empty is true and\n\t * we have an empty view, display it.  In all the other cases, make sure that the listview\n\t * is VISIBLE and that the empty view is GONE (if it's not null).\n\t */\n\tprivate void updateEmptyStatus(boolean empty) {\n\t\tif (isInFilterMode()) {\n\t\t\tempty = false;\n\t\t}\n\n\t\tif (empty) {\n\t\t\tif (mEmptyView != null) {\n\t\t\t\tmEmptyView.setVisibility(View.VISIBLE);\n\t\t\t\tsetVisibility(View.GONE);\n\t\t\t} else {\n\t\t\t\t// If the caller just removed our empty view, make sure the list view is visible\n\t\t\t\tsetVisibility(View.VISIBLE);\n\t\t\t}\n\n\t\t\t// We are now GONE, so pending layouts will not be dispatched.\n\t\t\t// Force one here to make sure that the state of the list matches\n\t\t\t// the state of the adapter.\n\t\t\tif (mDataChanged) {\n\t\t\t\tthis.onLayout(false, getLeft(), getTop(), getRight(), getBottom());\n\t\t\t}\n\t\t} else {\n\t\t\tif (mEmptyView != null) mEmptyView.setVisibility(View.GONE);\n\t\t\tsetVisibility(View.VISIBLE);\n\t\t}\n\t}\n\n\t/**\n\t * Gets the data associated with the specified position in the list.\n\t *\n\t * @param position Which data to get\n\t * @return The data associated with the specified position in the list\n\t */\n\tpublic Object getItemAtPosition(int position) {\n\t\tT adapter = getAdapter();\n\t\treturn (adapter == null || position < 0) ? null : adapter.getItem(position);\n\t}\n\n\tpublic long getItemIdAtPosition(int position) {\n\t\tT adapter = getAdapter();\n\t\treturn (adapter == null || position < 0) ? INVALID_ROW_ID : adapter.getItemId(position);\n\t}\n\n\t@Override\n\tpublic void setOnClickListener(OnClickListener l) {\n\t\tthrow new RuntimeException(\"Don't call setOnClickListener for an AdapterView. \"\n\t\t\t\t+ \"You probably want setOnItemClickListener instead\");\n\t}\n\n\t/**\n\t * Override to prevent freezing of any views created by the adapter.\n\t */\n\t@Override\n\tprotected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {\n\t\tdispatchFreezeSelfOnly(container);\n\t}\n\n\t/**\n\t * Override to prevent thawing of any views created by the adapter.\n\t */\n\t@Override\n\tprotected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {\n\t\tdispatchThawSelfOnly(container);\n\t}\n\n\tclass AdapterDataSetObserver extends DataSetObserver {\n\n\t\tprivate Parcelable mInstanceState = null;\n\n\t\t@Override\n\t\tpublic void onChanged() {\n\t\t\tmDataChanged = true;\n\t\t\tmOldItemCount = mItemCount;\n\t\t\tmItemCount = getAdapter().getCount();\n\n\t\t\t// Detect the case where a cursor that was previously invalidated has\n\t\t\t// been repopulated with new data.\n\t\t\tif (TwoWayAdapterView.this.getAdapter().hasStableIds() && mInstanceState != null\n\t\t\t\t\t&& mOldItemCount == 0 && mItemCount > 0) {\n\t\t\t\tTwoWayAdapterView.this.onRestoreInstanceState(mInstanceState);\n\t\t\t\tmInstanceState = null;\n\t\t\t} else {\n\t\t\t\trememberSyncState();\n\t\t\t}\n\t\t\tcheckFocus();\n\t\t\trequestLayout();\n\t\t}\n\n\t\t@Override\n\t\tpublic void onInvalidated() {\n\t\t\tmDataChanged = true;\n\n\t\t\tif (TwoWayAdapterView.this.getAdapter().hasStableIds()) {\n\t\t\t\t// Remember the current state for the case where our hosting activity is being\n\t\t\t\t// stopped and later restarted\n\t\t\t\tmInstanceState = TwoWayAdapterView.this.onSaveInstanceState();\n\t\t\t}\n\n\t\t\t// Data is invalid so we should reset our state\n\t\t\tmOldItemCount = mItemCount;\n\t\t\tmItemCount = 0;\n\t\t\tmSelectedPosition = INVALID_POSITION;\n\t\t\tmSelectedRowId = INVALID_ROW_ID;\n\t\t\tmNextSelectedPosition = INVALID_POSITION;\n\t\t\tmNextSelectedRowId = INVALID_ROW_ID;\n\t\t\tmNeedSync = false;\n\t\t\tcheckSelectionChanged();\n\n\t\t\tcheckFocus();\n\t\t\trequestLayout();\n\t\t}\n\n\t\tpublic void clearSavedState() {\n\t\t\tmInstanceState = null;\n\t\t}\n\t}\n\n\tprivate class SelectionNotifier extends Handler implements Runnable {\n\t\tpublic void run() {\n\t\t\tif (mDataChanged) {\n\t\t\t\t// Data has changed between when this SelectionNotifier\n\t\t\t\t// was posted and now. We need to wait until the AdapterView\n\t\t\t\t// has been synched to the new data.\n\t\t\t\tpost(this);\n\t\t\t} else {\n\t\t\t\tfireOnSelected();\n\t\t\t}\n\t\t}\n\t}\n\n\tvoid selectionChanged() {\n\t\tif (mOnItemSelectedListener != null) {\n\t\t\tif (mInLayout || mBlockLayoutRequests) {\n\t\t\t\t// If we are in a layout traversal, defer notification\n\t\t\t\t// by posting. This ensures that the view tree is\n\t\t\t\t// in a consistent state and is able to accomodate\n\t\t\t\t// new layout or invalidate requests.\n\t\t\t\tif (mSelectionNotifier == null) {\n\t\t\t\t\tmSelectionNotifier = new SelectionNotifier();\n\t\t\t\t}\n\t\t\t\tmSelectionNotifier.post(mSelectionNotifier);\n\t\t\t} else {\n\t\t\t\tfireOnSelected();\n\t\t\t}\n\t\t}\n\n\t\t// we fire selection events here not in View\n\t\t/* taken out for backward compatibility\n\t\tif (mSelectedPosition != ListView.INVALID_POSITION && isShown() && !isInTouchMode()) {\n\t\t\tsendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);\n\t\t}*/\n\t}\n\n\tprivate void fireOnSelected() {\n\t\tif (mOnItemSelectedListener == null)\n\t\t\treturn;\n\n\t\tint selection = this.getSelectedItemPosition();\n\t\tif (selection >= 0) {\n\t\t\tView v = getSelectedView();\n\t\t\tmOnItemSelectedListener.onItemSelected(this, v, selection,\n\t\t\t\t\tgetAdapter().getItemId(selection));\n\t\t} else {\n\t\t\tmOnItemSelectedListener.onNothingSelected(this);\n\t\t}\n\t}\n\n\t/* taken out for backward compatibility\n\t@Override\n\tpublic boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {\n\t\tboolean populated = false;\n\t\t// This is an exceptional case which occurs when a window gets the\n\t\t// focus and sends a focus event via its focused child to announce\n\t\t// current focus/selection. AdapterView fires selection but not focus\n\t\t// events so we change the event type here.\n\t\tif (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED) {\n\t\t\tevent.setEventType(AccessibilityEvent.TYPE_VIEW_SELECTED);\n\t\t}\n\n\t\t// we send selection events only from AdapterView to avoid\n\t\t// generation of such event for each child\n\t\tView selectedView = getSelectedView();\n\t\tif (selectedView != null) {\n\t\t\tpopulated = selectedView.dispatchPopulateAccessibilityEvent(event);\n\t\t}\n\n\t\tif (!populated) {\n\t\t\tif (selectedView != null) {\n\t\t\t\tevent.setEnabled(selectedView.isEnabled());\n\t\t\t}\n\t\t\tevent.setItemCount(getCount());\n\t\t\tevent.setCurrentItemIndex(getSelectedItemPosition());\n\t\t}\n\n\t\treturn populated;\n\t}*/\n\n\t@Override\n\tprotected boolean canAnimate() {\n\t\treturn super.canAnimate() && mItemCount > 0;\n\t}\n\n\tvoid handleDataChanged() {\n\t\tfinal int count = mItemCount;\n\t\tboolean found = false;\n\n\t\tif (count > 0) {\n\n\t\t\tint newPos;\n\n\t\t\t// Find the row we are supposed to sync to\n\t\t\tif (mNeedSync) {\n\t\t\t\t// Update this first, since setNextSelectedPositionInt inspects\n\t\t\t\t// it\n\t\t\t\tmNeedSync = false;\n\n\t\t\t\t// See if we can find a position in the new data with the same\n\t\t\t\t// id as the old selection\n\t\t\t\tnewPos = findSyncPosition();\n\t\t\t\tif (newPos >= 0) {\n\t\t\t\t\t// Verify that new selection is selectable\n\t\t\t\t\tint selectablePos = lookForSelectablePosition(newPos, true);\n\t\t\t\t\tif (selectablePos == newPos) {\n\t\t\t\t\t\t// Same row id is selected\n\t\t\t\t\t\tsetNextSelectedPositionInt(newPos);\n\t\t\t\t\t\tfound = true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (!found) {\n\t\t\t\t// Try to use the same position if we can't find matching data\n\t\t\t\tnewPos = getSelectedItemPosition();\n\n\t\t\t\t// Pin position to the available range\n\t\t\t\tif (newPos >= count) {\n\t\t\t\t\tnewPos = count - 1;\n\t\t\t\t}\n\t\t\t\tif (newPos < 0) {\n\t\t\t\t\tnewPos = 0;\n\t\t\t\t}\n\n\t\t\t\t// Make sure we select something selectable -- first look down\n\t\t\t\tint selectablePos = lookForSelectablePosition(newPos, true);\n\t\t\t\tif (selectablePos < 0) {\n\t\t\t\t\t// Looking down didn't work -- try looking up\n\t\t\t\t\tselectablePos = lookForSelectablePosition(newPos, false);\n\t\t\t\t}\n\t\t\t\tif (selectablePos >= 0) {\n\t\t\t\t\tsetNextSelectedPositionInt(selectablePos);\n\t\t\t\t\tcheckSelectionChanged();\n\t\t\t\t\tfound = true;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (!found) {\n\t\t\t// Nothing is selected\n\t\t\tmSelectedPosition = INVALID_POSITION;\n\t\t\tmSelectedRowId = INVALID_ROW_ID;\n\t\t\tmNextSelectedPosition = INVALID_POSITION;\n\t\t\tmNextSelectedRowId = INVALID_ROW_ID;\n\t\t\tmNeedSync = false;\n\t\t\tcheckSelectionChanged();\n\t\t}\n\t}\n\n\tvoid checkSelectionChanged() {\n\t\tif ((mSelectedPosition != mOldSelectedPosition) || (mSelectedRowId != mOldSelectedRowId)) {\n\t\t\tselectionChanged();\n\t\t\tmOldSelectedPosition = mSelectedPosition;\n\t\t\tmOldSelectedRowId = mSelectedRowId;\n\t\t}\n\t}\n\n\t/**\n\t * Searches the adapter for a position matching mSyncRowId. The search starts at mSyncPosition\n\t * and then alternates between moving up and moving down until 1) we find the right position, or\n\t * 2) we run out of time, or 3) we have looked at every position\n\t *\n\t * @return Position of the row that matches mSyncRowId, or {@link #INVALID_POSITION} if it can't\n\t *         be found\n\t */\n\tint findSyncPosition() {\n\t\tint count = mItemCount;\n\n\t\tif (count == 0) {\n\t\t\treturn INVALID_POSITION;\n\t\t}\n\n\t\tlong idToMatch = mSyncRowId;\n\t\tint seed = mSyncPosition;\n\n\t\t// If there isn't a selection don't hunt for it\n\t\tif (idToMatch == INVALID_ROW_ID) {\n\t\t\treturn INVALID_POSITION;\n\t\t}\n\n\t\t// Pin seed to reasonable values\n\t\tseed = Math.max(0, seed);\n\t\tseed = Math.min(count - 1, seed);\n\n\t\tlong endTime = SystemClock.uptimeMillis() + SYNC_MAX_DURATION_MILLIS;\n\n\t\tlong rowId;\n\n\t\t// first position scanned so far\n\t\tint first = seed;\n\n\t\t// last position scanned so far\n\t\tint last = seed;\n\n\t\t// True if we should move down on the next iteration\n\t\tboolean next = false;\n\n\t\t// True when we have looked at the first item in the data\n\t\tboolean hitFirst;\n\n\t\t// True when we have looked at the last item in the data\n\t\tboolean hitLast;\n\n\t\t// Get the item ID locally (instead of getItemIdAtPosition), so\n\t\t// we need the adapter\n\t\tT adapter = getAdapter();\n\t\tif (adapter == null) {\n\t\t\treturn INVALID_POSITION;\n\t\t}\n\n\t\twhile (SystemClock.uptimeMillis() <= endTime) {\n\t\t\trowId = adapter.getItemId(seed);\n\t\t\tif (rowId == idToMatch) {\n\t\t\t\t// Found it!\n\t\t\t\treturn seed;\n\t\t\t}\n\n\t\t\thitLast = last == count - 1;\n\t\t\thitFirst = first == 0;\n\n\t\t\tif (hitLast && hitFirst) {\n\t\t\t\t// Looked at everything\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (hitFirst || (next && !hitLast)) {\n\t\t\t\t// Either we hit the top, or we are trying to move down\n\t\t\t\tlast++;\n\t\t\t\tseed = last;\n\t\t\t\t// Try going up next time\n\t\t\t\tnext = false;\n\t\t\t} else if (hitLast || (!next && !hitFirst)) {\n\t\t\t\t// Either we hit the bottom, or we are trying to move up\n\t\t\t\tfirst--;\n\t\t\t\tseed = first;\n\t\t\t\t// Try going down next time\n\t\t\t\tnext = true;\n\t\t\t}\n\n\t\t}\n\n\t\treturn INVALID_POSITION;\n\t}\n\n\t/**\n\t * Find a position that can be selected (i.e., is not a separator).\n\t *\n\t * @param position The starting position to look at.\n\t * @param lookDown Whether to look down for other positions.\n\t * @return The next selectable position starting at position and then searching either up or\n\t *         down. Returns {@link #INVALID_POSITION} if nothing can be found.\n\t */\n\tint lookForSelectablePosition(int position, boolean lookDown) {\n\t\treturn position;\n\t}\n\n\t/**\n\t * Utility to keep mSelectedPosition and mSelectedRowId in sync\n\t * @param position Our current position\n\t */\n\tvoid setSelectedPositionInt(int position) {\n\t\tmSelectedPosition = position;\n\t\tmSelectedRowId = getItemIdAtPosition(position);\n\t}\n\n\t/**\n\t * Utility to keep mNextSelectedPosition and mNextSelectedRowId in sync\n\t * @param position Intended value for mSelectedPosition the next time we go\n\t * through layout\n\t */\n\tvoid setNextSelectedPositionInt(int position) {\n\t\tmNextSelectedPosition = position;\n\t\tmNextSelectedRowId = getItemIdAtPosition(position);\n\t\t// If we are trying to sync to the selection, update that too\n\t\tif (mNeedSync && mSyncMode == SYNC_SELECTED_POSITION && position >= 0) {\n\t\t\tmSyncPosition = position;\n\t\t\tmSyncRowId = mNextSelectedRowId;\n\t\t}\n\t}\n\n\t/**\n\t * Remember enough information to restore the screen state when the data has\n\t * changed.\n\t *\n\t */\n\tvoid rememberSyncState() {\n\t\tif (getChildCount() > 0) {\n\t\t\tmNeedSync = true;\n\t\t\tif (mIsVertical) {\n\t\t\t\tmSyncSize = mLayoutHeight;\n\t\t\t} else {\n\t\t\t\tmSyncSize = mLayoutWidth;\n\t\t\t}\n\t\t\tif (mSelectedPosition >= 0) {\n\t\t\t\t// Sync the selection state\n\t\t\t\tView v = getChildAt(mSelectedPosition - mFirstPosition);\n\t\t\t\tmSyncRowId = mNextSelectedRowId;\n\t\t\t\tmSyncPosition = mNextSelectedPosition;\n\t\t\t\tif (v != null) {\n\t\t\t\t\tif (mIsVertical) {\n\t\t\t\t\t\tmSpecificTop = v.getTop();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmSpecificTop = v.getLeft();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmSyncMode = SYNC_SELECTED_POSITION;\n\t\t\t} else {\n\t\t\t\t// Sync the based on the offset of the first view\n\t\t\t\tView v = getChildAt(0);\n\t\t\t\tT adapter = getAdapter();\n\t\t\t\tif (mFirstPosition >= 0 && mFirstPosition < adapter.getCount()) {\n\t\t\t\t\tmSyncRowId = adapter.getItemId(mFirstPosition);\n\t\t\t\t} else {\n\t\t\t\t\tmSyncRowId = NO_ID;\n\t\t\t\t}\n\t\t\t\tmSyncPosition = mFirstPosition;\n\t\t\t\tif (v != null) {\n\t\t\t\t\tif (mIsVertical) {\n\t\t\t\t\t\tmSpecificTop = v.getTop();\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmSpecificTop = v.getLeft();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmSyncMode = SYNC_FIRST_POSITION;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Offset the vertical location of all children of this view by the specified number of pixels.\n\t *\n\t * @param offset the number of pixels to offset\n\t *\n\t */\n\tpublic void offsetChildrenTopAndBottom(int offset) {\n\t\tfinal int count = getChildCount();\n\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tfinal View v = getChildAt(i);\n\t\t\tv.offsetTopAndBottom(offset);\n\t\t}\n\t}\n\n\t/**\n\t * Offset the horizontal location of all children of this view by the specified number of pixels.\n\t *\n\t * @param offset the number of pixels to offset\n\t *\n\t */\n\tpublic void offsetChildrenLeftAndRight(int offset) {\n\t\tfinal int count = getChildCount();\n\n\t\tfor (int i = 0; i < count; i++) {\n\t\t\tfinal View v = getChildAt(i);\n\t\t\tv.offsetLeftAndRight(offset);\n\t\t}\n\t}\n\n\tprotected void setIsVertical(boolean vertical) {\n\t\tmIsVertical = vertical;\n\t}\n\n\tprotected boolean isVertical() {\n\t\treturn mIsVertical;\n\t}\n\t\n\n}\n"
  },
  {
    "path": "lib/src/com/jess/ui/TwoWayGridView.java",
    "content": "/*\n * A modified version of the Android GridView that can be configured to\n * scroll vertically or horizontally\n *\n * Copyright 2012 Jess Anders\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/*\n * Copyright (C) 2006 The Android Open Source Project\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.jess.ui;\n\n\nimport android.content.Context;\nimport android.content.res.TypedArray;\nimport android.graphics.Rect;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.view.Gravity;\nimport android.view.KeyEvent;\nimport android.view.SoundEffectConstants;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.view.animation.GridLayoutAnimationController;\nimport android.widget.ListAdapter;\n\n\n/**\n * A view that shows items in two-dimensional scrolling grid. The items in the\n * grid come from the {@link ListAdapter} associated with this view.\n *\n */\npublic class TwoWayGridView extends TwoWayAbsListView {\n\tpublic static final int NO_STRETCH = 0;\n\tpublic static final int STRETCH_SPACING = 1;\n\tpublic static final int STRETCH_COLUMN_WIDTH = 2;\n\tpublic static final int STRETCH_SPACING_UNIFORM = 3;\n\n\tpublic static final int AUTO_FIT = -1;\n\n\tpublic static final String TAG = \"TwoWayGridView\";\n\tpublic static final boolean DEBUG = false;\n\n\tprivate int mNumColumns = AUTO_FIT;\n\tprivate int mNumRows = AUTO_FIT;\n\n\tprivate int mHorizontalSpacing = 0;\n\tprivate int mRequestedHorizontalSpacing;\n\tprivate int mVerticalSpacing = 0;\n\tprivate int mRequestedVerticalSpacing;\n\tprivate int mStretchMode = STRETCH_COLUMN_WIDTH;\n\tprivate int mColumnWidth;\n\tprivate int mRequestedColumnWidth;\n\tprivate int mRequestedNumColumns;\n\tprivate int mRowHeight;\n\tprivate int mRequestedRowHeight;\n\tprivate int mRequestedNumRows;\n\n\tprivate View mReferenceView = null;\n\tprivate View mReferenceViewInSelectedRow = null;\n\n\tprivate int mGravity = Gravity.LEFT;\n\n\tprivate final Rect mTempRect = new Rect();\n\n\tprotected GridBuilder mGridBuilder = null;\n\n\tpublic TwoWayGridView(Context context) {\n\t\tsuper(context);\n\t\tsetupGridType();\n\t}\n\n\tpublic TwoWayGridView(Context context, AttributeSet attrs) {\n\t\tthis(context, attrs, android.R.attr.gridViewStyle);\n\t}\n\n\tpublic TwoWayGridView(Context context, AttributeSet attrs, int defStyle) {\n\t\tsuper(context, attrs, defStyle);\n\n\t\tTypedArray a = context.obtainStyledAttributes(attrs,\n\t\t\t\tR.styleable.TwoWayGridView, defStyle, 0);\n\n\t\tint hSpacing = a.getDimensionPixelOffset(\n\t\t\t\tR.styleable.TwoWayGridView_horizontalSpacing, 0);\n\t\tsetHorizontalSpacing(hSpacing);\n\n\t\tint vSpacing = a.getDimensionPixelOffset(\n\t\t\t\tR.styleable.TwoWayGridView_verticalSpacing, 0);\n\t\tsetVerticalSpacing(vSpacing);\n\n\t\tint index = a.getInt(R.styleable.TwoWayGridView_stretchMode, STRETCH_COLUMN_WIDTH);\n\t\tif (index >= 0) {\n\t\t\tsetStretchMode(index);\n\t\t}\n\n\t\tint columnWidth = a.getDimensionPixelOffset(R.styleable.TwoWayGridView_columnWidth, -1);\n\t\tif (columnWidth > 0) {\n\t\t\tsetColumnWidth(columnWidth);\n\t\t}\n\n\t\tint rowHeight = a.getDimensionPixelOffset(R.styleable.TwoWayGridView_rowHeight, -1);\n\t\tif (rowHeight > 0) {\n\t\t\tsetRowHeight(rowHeight);\n\t\t}\n\n\t\tint numColumns = a.getInt(R.styleable.TwoWayGridView_numColumns, 1);\n\t\tsetNumColumns(numColumns);\n\n\t\tint numRows = a.getInt(R.styleable.TwoWayGridView_numRows, 1);\n\t\tsetNumRows(numRows);\n\n\t\tindex = a.getInt(R.styleable.TwoWayGridView_gravity, -1);\n\t\tif (index >= 0) {\n\t\t\tsetGravity(index);\n\t\t}\n\n\t\ta.recycle();\n\t\tsetupGridType();\n\t}\n\n\tprivate void setupGridType() {\n\t\tif (mScrollVertically) {\n\t\t\tmGridBuilder = new VerticalGridBuilder();\n\t\t} else {\n\t\t\tmGridBuilder = new HorizontalGridBuilder();\n\t\t}\n\t}\n\n\t@Override\n\tpublic ListAdapter getAdapter() {\n\t\treturn mAdapter;\n\t}\n\n\t/**\n\t * Sets the data behind this TwoWayGridView.\n\t *\n\t * @param adapter the adapter providing the grid's data\n\t */\n\t@Override\n\tpublic void setAdapter(ListAdapter adapter) {\n\t\tif (null != mAdapter) {\n\t\t\tmAdapter.unregisterDataSetObserver(mDataSetObserver);\n\t\t}\n\n\t\tresetList();\n\t\tmRecycler.clear();\n\t\tmAdapter = adapter;\n\n\t\tmOldSelectedPosition = INVALID_POSITION;\n\t\tmOldSelectedRowId = INVALID_ROW_ID;\n\n\t\tif (mAdapter != null) {\n\t\t\tmOldItemCount = mItemCount;\n\t\t\tmItemCount = mAdapter.getCount();\n\t\t\tmDataChanged = true;\n\t\t\tcheckFocus();\n\n\t\t\tmDataSetObserver = new AdapterDataSetObserver();\n\t\t\tmAdapter.registerDataSetObserver(mDataSetObserver);\n\n\t\t\tmRecycler.setViewTypeCount(mAdapter.getViewTypeCount());\n\n\t\t\tint position;\n\t\t\tif (mStackFromBottom) {\n\t\t\t\tposition = lookForSelectablePosition(mItemCount - 1, false);\n\t\t\t} else {\n\t\t\t\tposition = lookForSelectablePosition(0, true);\n\t\t\t}\n\t\t\tsetSelectedPositionInt(position);\n\t\t\tsetNextSelectedPositionInt(position);\n\t\t\tcheckSelectionChanged();\n\t\t} else {\n\t\t\tcheckFocus();\n\t\t\t// Nothing selected\n\t\t\tcheckSelectionChanged();\n\t\t}\n\n\t\trequestLayout();\n\t}\n\n\t@Override\n\tint lookForSelectablePosition(int position, boolean lookDown) {\n\t\tfinal ListAdapter adapter = mAdapter;\n\t\tif (adapter == null || isInTouchMode()) {\n\t\t\treturn INVALID_POSITION;\n\t\t}\n\n\t\tif (position < 0 || position >= mItemCount) {\n\t\t\treturn INVALID_POSITION;\n\t\t}\n\t\treturn position;\n\t}\n\n\t/**\n\t * {@inheritDoc}\n\t */\n\t@Override\n\tvoid fillGap(boolean down) {\n\t\tif (DEBUG) Log.i(TAG, \"fillGap() down: \" + down);\n\t\tmGridBuilder.fillGap(down);\n\t}\n\n\t@Override\n\tint findMotionRowY(int y) {\n\t\tfinal int childCount = getChildCount();\n\t\tif (childCount > 0) {\n\n\t\t\tfinal int numColumns = mNumColumns;\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\tfor (int i = 0; i < childCount; i += numColumns) {\n\t\t\t\t\tif (y <= getChildAt(i).getBottom()) {\n\t\t\t\t\t\treturn mFirstPosition + i;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (int i = childCount - 1; i >= 0; i -= numColumns) {\n\t\t\t\t\tif (y >= getChildAt(i).getTop()) {\n\t\t\t\t\t\treturn mFirstPosition + i;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn INVALID_POSITION;\n\t}\n\n\t@Override\n\tint findMotionRowX(int x) {\n\t\tfinal int childCount = getChildCount();\n\t\tif (childCount > 0) {\n\n\t\t\tfinal int numRows = mNumRows;\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\tfor (int i = 0; i < childCount; i += numRows) {\n\t\t\t\t\tif (x <= getChildAt(i).getRight()) {\n\t\t\t\t\t\treturn mFirstPosition + i;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfor (int i = childCount - 1; i >= 0; i -= numRows) {\n\t\t\t\t\tif (x >= getChildAt(i).getLeft()) {\n\t\t\t\t\t\treturn mFirstPosition + i;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn INVALID_POSITION;\n\t}\n\n\n\n\t@Override\n\tprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n\t\tsuper.onMeasure(widthMeasureSpec, heightMeasureSpec);\n\t\tif ((mScrollVertically && !(mGridBuilder instanceof VerticalGridBuilder))\n\t\t\t|| (!mScrollVertically && !(mGridBuilder instanceof HorizontalGridBuilder)) ) {\n\t\t\tsetupGridType();\n\t\t}\n\t\t// Sets up mListPadding\n\t\tmGridBuilder.onMeasure(widthMeasureSpec, heightMeasureSpec);\n\t}\n\n\t//TODO implement horizontal support\n\t@Override\n\tprotected void attachLayoutAnimationParameters(View child,\n\t\t\tViewGroup.LayoutParams params, int index, int count) {\n\n\t\tGridLayoutAnimationController.AnimationParameters animationParams =\n\t\t\t(GridLayoutAnimationController.AnimationParameters) params.layoutAnimationParameters;\n\n\t\tif (animationParams == null) {\n\t\t\tanimationParams = new GridLayoutAnimationController.AnimationParameters();\n\t\t\tparams.layoutAnimationParameters = animationParams;\n\t\t}\n\n\t\tanimationParams.count = count;\n\t\tanimationParams.index = index;\n\t\tanimationParams.columnsCount = mNumColumns;\n\t\tanimationParams.rowsCount = count / mNumColumns;\n\n\t\tif (!mStackFromBottom) {\n\t\t\tanimationParams.column = index % mNumColumns;\n\t\t\tanimationParams.row = index / mNumColumns;\n\t\t} else {\n\t\t\tfinal int invertedIndex = count - 1 - index;\n\n\t\t\tanimationParams.column = mNumColumns - 1 - (invertedIndex % mNumColumns);\n\t\t\tanimationParams.row = animationParams.rowsCount - 1 - invertedIndex / mNumColumns;\n\t\t}\n\t}\n\n\t@Override\n\tprotected void layoutChildren() {\n\t\tfinal boolean blockLayoutRequests = mBlockLayoutRequests;\n\t\tif (!blockLayoutRequests) {\n\t\t\tmBlockLayoutRequests = true;\n\t\t}\n\n\t\ttry {\n\t\t\tsuper.layoutChildren();\n\n\t\t\tinvalidate();\n\n\t\t\tif (mAdapter == null) {\n\t\t\t\tresetList();\n\t\t\t\tinvokeOnItemScrollListener();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tmGridBuilder.layoutChildren();\n\n\t\t} finally {\n\t\t\tif (!blockLayoutRequests) {\n\t\t\t\tmBlockLayoutRequests = false;\n\t\t\t}\n\t\t}\n\t}\n\n\n\n\t/**\n\t * Sets the currently selected item\n\t * \n\t * @param position Index (starting at 0) of the data item to be selected.\n\t * \n\t * If in touch mode, the item will not be selected but it will still be positioned\n\t * appropriately.\n\t */\n\t@Override\n\tpublic void setSelection(int position) {\n\t\tif (!isInTouchMode()) {\n\t\t\tsetNextSelectedPositionInt(position);\n\t\t} else {\n\t\t\tmResurrectToPosition = position;\n\t\t}\n\t\tmLayoutMode = LAYOUT_SET_SELECTION;\n\t\trequestLayout();\n\t}\n\n\t/**\n\t * Makes the item at the supplied position selected.\n\t *\n\t * @param position the position of the new selection\n\t */\n\t@Override\n\tvoid setSelectionInt(int position) {\n\t\tmGridBuilder.setSelectionInt(position);\n\t}\n\n\t@Override\n\tpublic boolean onKeyDown(int keyCode, KeyEvent event) {\n\t\treturn commonKey(keyCode, 1, event);\n\t}\n\n\t@Override\n\tpublic boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {\n\t\treturn commonKey(keyCode, repeatCount, event);\n\t}\n\n\t@Override\n\tpublic boolean onKeyUp(int keyCode, KeyEvent event) {\n\t\treturn commonKey(keyCode, 1, event);\n\t}\n\n\tprivate boolean commonKey(int keyCode, int count, KeyEvent event) {\n\t\tif (mAdapter == null) {\n\t\t\treturn false;\n\t\t}\n\n\t\tif (mDataChanged) {\n\t\t\tlayoutChildren();\n\t\t}\n\n\t\tboolean handled = false;\n\t\tint action = event.getAction();\n\n\t\tif (action != KeyEvent.ACTION_UP) {\n\t\t\tif (mSelectedPosition < 0) {\n\t\t\t\tswitch (keyCode) {\n\t\t\t\tcase KeyEvent.KEYCODE_DPAD_UP:\n\t\t\t\tcase KeyEvent.KEYCODE_DPAD_DOWN:\n\t\t\t\tcase KeyEvent.KEYCODE_DPAD_LEFT:\n\t\t\t\tcase KeyEvent.KEYCODE_DPAD_RIGHT:\n\t\t\t\tcase KeyEvent.KEYCODE_DPAD_CENTER:\n\t\t\t\tcase KeyEvent.KEYCODE_SPACE:\n\t\t\t\tcase KeyEvent.KEYCODE_ENTER:\n\t\t\t\t\tresurrectSelection();\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tswitch (keyCode) {\n\t\t\tcase KeyEvent.KEYCODE_DPAD_LEFT:\n\t\t\t\tif (!event.isAltPressed()) {\n\t\t\t\t\thandled = mGridBuilder.arrowScroll(FOCUS_LEFT);\n\t\t\t\t} else {\n\t\t\t\t\thandled = fullScroll(FOCUS_UP);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase KeyEvent.KEYCODE_DPAD_RIGHT:\n\t\t\t\tif (!event.isAltPressed()) {\n\t\t\t\t\thandled = mGridBuilder.arrowScroll(FOCUS_RIGHT);\n\t\t\t\t} else {\n\t\t\t\t\thandled = fullScroll(FOCUS_DOWN);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase KeyEvent.KEYCODE_DPAD_UP:\n\t\t\t\tif (!event.isAltPressed()) {\n\t\t\t\t\thandled = mGridBuilder.arrowScroll(FOCUS_UP);\n\n\t\t\t\t} else {\n\t\t\t\t\thandled = fullScroll(FOCUS_UP);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase KeyEvent.KEYCODE_DPAD_DOWN:\n\t\t\t\tif (!event.isAltPressed()) {\n\t\t\t\t\thandled = mGridBuilder.arrowScroll(FOCUS_DOWN);\n\t\t\t\t} else {\n\t\t\t\t\thandled = fullScroll(FOCUS_DOWN);\n\t\t\t\t}\n\t\t\t\tbreak;\n\n\t\t\tcase KeyEvent.KEYCODE_DPAD_CENTER:\n\t\t\tcase KeyEvent.KEYCODE_ENTER: {\n\t\t\t\tif (getChildCount() > 0 && event.getRepeatCount() == 0) {\n\t\t\t\t\tkeyPressed();\n\t\t\t\t}\n\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tcase KeyEvent.KEYCODE_SPACE:\n\t\t\t\t//if (mPopup == null || !mPopup.isShowing()) {\n\t\t\t\tif (!event.isShiftPressed()) {\n\t\t\t\t\thandled = pageScroll(FOCUS_DOWN);\n\t\t\t\t} else {\n\t\t\t\t\thandled = pageScroll(FOCUS_UP);\n\t\t\t\t}\n\t\t\t\t//}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t//if (!handled) {\n\t\t//    handled = sendToTextFilter(keyCode, count, event);\n\t\t//}\n\n\t\tif (handled) {\n\t\t\treturn true;\n\t\t} else {\n\t\t\tswitch (action) {\n\t\t\tcase KeyEvent.ACTION_DOWN:\n\t\t\t\treturn super.onKeyDown(keyCode, event);\n\t\t\tcase KeyEvent.ACTION_UP:\n\t\t\t\treturn super.onKeyUp(keyCode, event);\n\t\t\tcase KeyEvent.ACTION_MULTIPLE:\n\t\t\t\treturn super.onKeyMultiple(keyCode, count, event);\n\t\t\tdefault:\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Scrolls up or down by the number of items currently present on screen.\n\t *\n\t * @param direction either {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN}\n\t * @return whether selection was moved\n\t */\n\tboolean pageScroll(int direction) {\n\t\tint nextPage = -1;\n\t\t//TODO this doesn't look correct...\n\t\tif (direction == FOCUS_UP) {\n\t\t\tnextPage = Math.max(0, mSelectedPosition - getChildCount() - 1);\n\t\t} else if (direction == FOCUS_DOWN) {\n\t\t\tnextPage = Math.min(mItemCount - 1, mSelectedPosition + getChildCount() - 1);\n\t\t}\n\n\t\tif (nextPage >= 0) {\n\t\t\tsetSelectionInt(nextPage);\n\t\t\tinvokeOnItemScrollListener();\n\t\t\t//awakenScrollBars();\n\t\t\treturn true;\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Go to the last or first item if possible.\n\t *\n\t * @param direction either {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN}.\n\t *\n\t * @return Whether selection was moved.\n\t */\n\tboolean fullScroll(int direction) {\n\t\tboolean moved = false;\n\t\tif (direction == FOCUS_UP) {\n\t\t\tmLayoutMode = LAYOUT_SET_SELECTION;\n\t\t\tsetSelectionInt(0);\n\t\t\tinvokeOnItemScrollListener();\n\t\t\tmoved = true;\n\t\t} else if (direction == FOCUS_DOWN) {\n\t\t\tmLayoutMode = LAYOUT_SET_SELECTION;\n\t\t\tsetSelectionInt(mItemCount - 1);\n\t\t\tinvokeOnItemScrollListener();\n\t\t\tmoved = true;\n\t\t}\n\n\t\tif (moved) {\n\t\t\t//awakenScrollBars();\n\t\t}\n\n\t\treturn moved;\n\t}\n\n\n\n\t@Override\n\tprotected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {\n\t\tsuper.onFocusChanged(gainFocus, direction, previouslyFocusedRect);\n\n\t\tint closestChildIndex = -1;\n\t\tif (gainFocus && previouslyFocusedRect != null) {\n\t\t\tpreviouslyFocusedRect.offset(getScrollX(), getScrollY());\n\n\t\t\t// figure out which item should be selected based on previously\n\t\t\t// focused rect\n\t\t\tRect otherRect = mTempRect;\n\t\t\tint minDistance = Integer.MAX_VALUE;\n\t\t\tfinal int childCount = getChildCount();\n\t\t\tfor (int i = 0; i < childCount; i++) {\n\t\t\t\t// only consider view's on appropriate edge of grid\n\t\t\t\tif (!mGridBuilder.isCandidateSelection(i, direction)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tfinal View other = getChildAt(i);\n\t\t\t\tother.getDrawingRect(otherRect);\n\t\t\t\toffsetDescendantRectToMyCoords(other, otherRect);\n\t\t\t\tint distance = getDistance(previouslyFocusedRect, otherRect, direction);\n\n\t\t\t\tif (distance < minDistance) {\n\t\t\t\t\tminDistance = distance;\n\t\t\t\t\tclosestChildIndex = i;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (closestChildIndex >= 0) {\n\t\t\tsetSelection(closestChildIndex + mFirstPosition);\n\t\t} else {\n\t\t\trequestLayout();\n\t\t}\n\t}\n\n\n\n\t/**\n\t * Describes how the child views are horizontally aligned. Defaults to Gravity.LEFT\n\t *\n\t * @param gravity the gravity to apply to this grid's children\n\t *\n\t * @attr ref android.R.styleable#JessGridView_gravity\n\t */\n\tpublic void setGravity(int gravity) {\n\t\tif (mGravity != gravity) {\n\t\t\tmGravity = gravity;\n\t\t\trequestLayoutIfNecessary();\n\t\t}\n\t}\n\n\t/**\n\t * Set the amount of horizontal (x) spacing to place between each item\n\t * in the grid.\n\t *\n\t * @param horizontalSpacing The amount of horizontal space between items,\n\t * in pixels.\n\t *\n\t * @attr ref android.R.styleable#JessGridView_horizontalSpacing\n\t */\n\tpublic void setHorizontalSpacing(int horizontalSpacing) {\n\t\tif (horizontalSpacing != mRequestedHorizontalSpacing) {\n\t\t\tmRequestedHorizontalSpacing = horizontalSpacing;\n\t\t\trequestLayoutIfNecessary();\n\t\t}\n\t}\n\n\n\t/**\n\t * Set the amount of vertical (y) spacing to place between each item\n\t * in the grid.\n\t *\n\t * @param verticalSpacing The amount of vertical space between items,\n\t * in pixels.\n\t *\n\t * @attr ref android.R.styleable#JessGridView_verticalSpacing\n\t */\n\tpublic void setVerticalSpacing(int verticalSpacing) {\n\t\tif (verticalSpacing != mRequestedVerticalSpacing) {\n\t\t\tmRequestedVerticalSpacing = verticalSpacing;\n\t\t\trequestLayoutIfNecessary();\n\t\t}\n\t}\n\n\t/**\n\t * Control how items are stretched to fill their space.\n\t *\n\t * @param stretchMode Either {@link #NO_STRETCH},\n\t * {@link #STRETCH_SPACING}, {@link #STRETCH_SPACING_UNIFORM}, or {@link #STRETCH_COLUMN_WIDTH}.\n\t *\n\t * @attr ref android.R.styleable#JessGridView_stretchMode\n\t */\n\tpublic void setStretchMode(int stretchMode) {\n\t\tif (stretchMode != mStretchMode) {\n\t\t\tmStretchMode = stretchMode;\n\t\t\trequestLayoutIfNecessary();\n\t\t}\n\t}\n\n\tpublic int getStretchMode() {\n\t\treturn mStretchMode;\n\t}\n\n\t/**\n\t * Set the width of columns in the grid.  (Only used in vertical scroll mode)\n\t *\n\t * @param columnWidth The column width, in pixels.\n\t *\n\t * @attr ref android.R.styleable#JessGridView_columnWidth\n\t */\n\tpublic void setColumnWidth(int columnWidth) {\n\t\tif (columnWidth != mRequestedColumnWidth) {\n\t\t\tmRequestedColumnWidth = columnWidth;\n\t\t\trequestLayoutIfNecessary();\n\t\t}\n\t}\n\n\t/**\n\t * Set the height of rows in the grid.  (Only used in horizontal scroll mode)\n\t *\n\t * @param rowHeight The row height, in pixels.\n\t *\n\t * @attr ref android.R.styleable#JessGridView_rowHeight\n\t */\n\tpublic void setRowHeight(int rowHeight) {\n\t\tif (rowHeight != mRequestedRowHeight) {\n\t\t\tmRequestedRowHeight = rowHeight;\n\t\t\trequestLayoutIfNecessary();\n\t\t}\n\t}\n\n\t/**\n\t * Set the number of columns in the grid\n\t *\n\t * @param numColumns The desired number of columns.\n\t *\n\t * @attr ref android.R.styleable#JessGridView_numColumns\n\t */\n\tpublic void setNumColumns(int numColumns) {\n\t\tif (numColumns != mRequestedNumColumns) {\n\t\t\tmRequestedNumColumns = numColumns;\n\t\t\trequestLayoutIfNecessary();\n\t\t}\n\t}\n\n\t/**\n\t * Set the number of rows in the grid\n\t *\n\t * @param numRows The desired number of rows.\n\t *\n\t * @attr ref android.R.styleable#JessGridView_numRows\n\t */\n\tpublic void setNumRows(int numRows) {\n\t\tif (numRows != mRequestedNumRows) {\n\t\t\tmRequestedNumRows = numRows;\n\t\t\trequestLayoutIfNecessary();\n\t\t}\n\t}\n\n\n\n\t@Override\n\tprotected int computeVerticalScrollExtent() {\n\t\tfinal int count = getChildCount();\n\t\tif (count > 0 && mScrollVertically) {\n\t\t\tfinal int numColumns = mNumColumns;\n\t\t\tfinal int rowCount = (count + numColumns - 1) / numColumns;\n\n\t\t\tint extent = rowCount * 100;\n\n\t\t\tView view = getChildAt(0);\n\t\t\tfinal int top = view.getTop();\n\t\t\tint height = view.getHeight();\n\t\t\tif (height > 0) {\n\t\t\t\textent += (top * 100) / height;\n\t\t\t}\n\n\t\t\tview = getChildAt(count - 1);\n\t\t\tfinal int bottom = view.getBottom();\n\t\t\theight = view.getHeight();\n\t\t\tif (height > 0) {\n\t\t\t\textent -= ((bottom - getHeight()) * 100) / height;\n\t\t\t}\n\n\t\t\treturn extent;\n\t\t}\n\t\treturn 0;\n\t}\n\n\t@Override\n\tprotected int computeVerticalScrollOffset() {\n\t\tif (mFirstPosition >= 0 && getChildCount() > 0 && mScrollVertically) {\n\t\t\tfinal View view = getChildAt(0);\n\t\t\tfinal int top = view.getTop();\n\t\t\tint height = view.getHeight();\n\t\t\tif (height > 0) {\n\t\t\t\tfinal int numColumns = mNumColumns;\n\t\t\t\tfinal int whichRow = mFirstPosition / numColumns;\n\t\t\t\tfinal int rowCount = (mItemCount + numColumns - 1) / numColumns;\n\t\t\t\treturn Math.max(whichRow * 100 - (top * 100) / height +\n\t\t\t\t\t\t(int) ((float) getScrollY() / getHeight() * rowCount * 100), 0);\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\t@Override\n\tprotected int computeVerticalScrollRange() {\n\t\t// TODO: Account for vertical spacing too\n\t\tif (!mScrollVertically) {\n\t\t\treturn 0;\n\t\t}\n\t\tfinal int numColumns = mNumColumns;\n\t\tfinal int rowCount = (mItemCount + numColumns - 1) / numColumns;\n\t\treturn Math.max(rowCount * 100, 0);\n\t}\n\n\t@Override\n\tprotected int computeHorizontalScrollExtent() {\n\t\tfinal int count = getChildCount();\n\t\tif (count > 0 && !mScrollVertically) {\n\t\t\tfinal int numRows = mNumRows;\n\t\t\tfinal int columnCount = (count + numRows - 1) / numRows;\n\n\t\t\tint extent = columnCount * 100;\n\n\t\t\tView view = getChildAt(0);\n\t\t\tfinal int left = view.getLeft();\n\t\t\tint width = view.getWidth();\n\t\t\tif (width > 0) {\n\t\t\t\textent += (left * 100) / width;\n\t\t\t}\n\n\t\t\tview = getChildAt(count - 1);\n\t\t\tfinal int right = view.getRight();\n\t\t\twidth = view.getWidth();\n\t\t\tif (width > 0) {\n\t\t\t\textent -= ((right - getWidth()) * 100) / width;\n\t\t\t}\n\n\t\t\treturn extent;\n\t\t}\n\t\treturn 0;\n\t}\n\n\t@Override\n\tprotected int computeHorizontalScrollOffset() {\n\t\tif (mFirstPosition >= 0 && getChildCount() > 0 && !mScrollVertically) {\n\t\t\tfinal View view = getChildAt(0);\n\t\t\tfinal int left = view.getLeft();\n\t\t\tint width = view.getWidth();\n\t\t\tif (width > 0) {\n\t\t\t\tfinal int numRows = mNumRows;\n\t\t\t\tfinal int whichColumn = mFirstPosition / numRows;\n\t\t\t\tfinal int columnCount = (mItemCount + numRows - 1) / numRows;\n\t\t\t\treturn Math.max(whichColumn * 100 - (left * 100) / width +\n\t\t\t\t\t\t(int) ((float) getScrollX() / getWidth() * columnCount * 100), 0);\n\t\t\t}\n\t\t}\n\t\treturn 0;\n\t}\n\n\t@Override\n\tprotected int computeHorizontalScrollRange() {\n\t\t// TODO: Account for horizontal spacing too\n\t\tif (mScrollVertically) {\n\t\t\treturn 0;\n\t\t}\n\t\tfinal int numRows = mNumRows;\n\t\tfinal int columnCount = (mItemCount + numRows - 1) / numRows;\n\t\treturn Math.max(columnCount * 100, 0);\n\t}\n\n\n\tprivate abstract class GridBuilder {\n\n\t\tprotected abstract View makeAndAddView(int position, int y, boolean flow, int childrenLeft,\n\t\t\t\tboolean selected, int where);\n\n\t\tprotected abstract void fillGap(boolean down);\n\n\n\t\tprotected abstract void onMeasure(int widthMeasureSpec, int heightMeasureSpec);\n\n\t\tprotected abstract void layoutChildren();\n\n\t\tprotected abstract void setSelectionInt(int position);\n\n\t\tprotected abstract boolean arrowScroll(int direction);\n\n\t\tprotected abstract boolean isCandidateSelection(int childIndex, int direction);\n\t}\n\n\tprivate class VerticalGridBuilder extends GridBuilder {\n\n\t\t/**\n\t\t * Obtain the view and add it to our list of children. The view can be made\n\t\t * fresh, converted from an unused view, or used as is if it was in the\n\t\t * recycle bin.\n\t\t *\n\t\t * @param position Logical position in the list\n\t\t * @param y Top or bottom edge of the view to add\n\t\t * @param flow if true, align top edge to y. If false, align bottom edge to\n\t\t *        y.\n\t\t * @param childrenLeft Left edge where children should be positioned\n\t\t * @param selected Is this position selected?\n\t\t * @param where to add new item in the list\n\t\t * @return View that was added\n\t\t */\n\t\t@Override\n\t\tprotected View makeAndAddView(int position, int y, boolean flow, int childrenLeft,\n\t\t\t\tboolean selected, int where) {\n\t\t\tView child;\n\t\t\tif (DEBUG) Log.i(TAG, \"makeAndAddView() - start - position: \" + position + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tif (!mDataChanged) {\n\t\t\t\t// Try to use an existing view for this position\n\t\t\t\tchild = mRecycler.getActiveView(position);\n\t\t\t\tif (child != null) {\n\t\t\t\t\t// Found it -- we're using an existing child\n\t\t\t\t\t// This just needs to be positioned\n\t\t\t\t\tsetupChild(child, position, y, flow, childrenLeft, selected, true, where);\n\t\t\t\t\tif (DEBUG) Log.i(TAG, \"makeAndAddView() - end - position: \" + position + \"reused a view\");\n\t\t\t\t\treturn child;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Make a new view for this position, or convert an unused view if\n\t\t\t// possible\n\t\t\tchild = obtainView(position, mIsScrap);\n\n\t\t\t// This needs to be positioned and measured\n\t\t\tsetupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0], where);\n\t\t\tif (DEBUG) Log.i(TAG, \"makeAndAddView() - end - position: \" + position + \"did NOT reuse a view - scrap: \" + mIsScrap[0]);\n\t\t\treturn child;\n\t\t}\n\n\t\t@Override\n\t\tprotected void fillGap(boolean down) {\n\n\t\t\tfinal int numColumns = mNumColumns;\n\t\t\tfinal int verticalSpacing = mVerticalSpacing;\n\n\t\t\tfinal int count = getChildCount();\n\n\t\t\tif (down) {\n\t\t\t\tfinal int startOffset = count > 0 ?\n\t\t\t\t\t\tgetChildAt(count - 1).getBottom() + verticalSpacing : getListPaddingTop();\n\t\t\t\t\t\tint position = mFirstPosition + count;\n\t\t\t\t\t\tif (mStackFromBottom) {\n\t\t\t\t\t\t\tposition += numColumns - 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfillDown(position, startOffset);\n\t\t\t\t\t\tcorrectTooHigh(numColumns, verticalSpacing, getChildCount());\n\t\t\t} else {\n\t\t\t\tfinal int startOffset = count > 0 ?\n\t\t\t\t\t\tgetChildAt(0).getTop() - verticalSpacing : getHeight() - getListPaddingBottom();\n\t\t\t\t\t\tint position = mFirstPosition;\n\t\t\t\t\t\tif (!mStackFromBottom) {\n\t\t\t\t\t\t\tposition -= numColumns;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tposition--;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfillUp(position, startOffset);\n\t\t\t\t\t\tcorrectTooLow(numColumns, verticalSpacing, getChildCount());\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Fills the list from pos down to the end of the list view.\n\t\t *\n\t\t * @param pos The first position to put in the list\n\t\t *\n\t\t * @param nextTop The location where the top of the item associated with pos\n\t\t *        should be drawn\n\t\t *\n\t\t * @return The view that is currently selected, if it happens to be in the\n\t\t *         range that we draw.\n\t\t */\n\t\tprivate View fillDown(int pos, int nextTop) {\n\t\t\tif (DEBUG) Log.i(TAG, \"fillDown() pos: \" + pos + \" nextTop: \" + nextTop + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tView selectedView = null;\n\n\t\t\tfinal int end = (getBottom() - getTop()) - mListPadding.bottom;\n\n\t\t\twhile (nextTop < end && pos < mItemCount) {\n\t\t\t\tView temp = makeRow(pos, nextTop, true);\n\t\t\t\tif (temp != null) {\n\t\t\t\t\tselectedView = temp;\n\t\t\t\t}\n\n\t\t\t\t// mReferenceView will change with each call to makeRow()\n\t\t\t\t// do not cache in a local variable outside of this loop\n\t\t\t\tnextTop = mReferenceView.getBottom() + mVerticalSpacing;\n\n\t\t\t\tpos += mNumColumns;\n\t\t\t}\n\n\t\t\treturn selectedView;\n\t\t}\n\n\t\tprivate View makeRow(int startPos, int y, boolean flow) {\n\t\t\tif (DEBUG) Log.i(TAG, \"makeRow() startPos: \" + startPos + \" y: \" + y + \" flow: \" + flow + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tfinal int columnWidth = mColumnWidth;\n\t\t\tfinal int horizontalSpacing = mHorizontalSpacing;\n\n\t\t\tint last;\n\t\t\tint nextLeft = mListPadding.left +\n\t\t\t((mStretchMode == STRETCH_SPACING_UNIFORM) ? horizontalSpacing : 0);\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\tlast = Math.min(startPos + mNumColumns, mItemCount);\n\t\t\t} else {\n\t\t\t\tlast = startPos + 1;\n\t\t\t\tstartPos = Math.max(0, startPos - mNumColumns + 1);\n\n\t\t\t\tif (last - startPos < mNumColumns) {\n\t\t\t\t\tnextLeft += (mNumColumns - (last - startPos)) * (columnWidth + horizontalSpacing);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tView selectedView = null;\n\n\t\t\tfinal boolean hasFocus = shouldShowSelector();\n\t\t\tfinal boolean inClick = touchModeDrawsInPressedState();\n\t\t\tfinal int selectedPosition = mSelectedPosition;\n\n\t\t\tView child = null;\n\t\t\tfor (int pos = startPos; pos < last; pos++) {\n\t\t\t\t// is this the selected item?\n\t\t\t\tboolean selected = pos == selectedPosition;\n\t\t\t\t// does the list view have focus or contain focus\n\n\t\t\t\tfinal int where = flow ? -1 : pos - startPos;\n\t\t\t\tchild = makeAndAddView(pos, y, flow, nextLeft, selected, where);\n\n\t\t\t\tnextLeft += columnWidth;\n\t\t\t\tif (pos < last - 1) {\n\t\t\t\t\tnextLeft += horizontalSpacing;\n\t\t\t\t}\n\n\t\t\t\tif (selected && (hasFocus || inClick)) {\n\t\t\t\t\tselectedView = child;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmReferenceView = child;\n\n\t\t\tif (selectedView != null) {\n\t\t\t\tmReferenceViewInSelectedRow = mReferenceView;\n\t\t\t}\n\n\t\t\treturn selectedView;\n\t\t}\n\n\t\t/**\n\t\t * Fills the list from pos up to the top of the list view.\n\t\t *\n\t\t * @param pos The first position to put in the list\n\t\t *\n\t\t * @param nextBottom The location where the bottom of the item associated\n\t\t *        with pos should be drawn\n\t\t *\n\t\t * @return The view that is currently selected\n\t\t */\n\t\tprivate View fillUp(int pos, int nextBottom) {\n\t\t\tif (DEBUG) Log.i(TAG, \"fillLeft() pos: \" + pos + \" nextBottom: \" + nextBottom + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tView selectedView = null;\n\n\t\t\tfinal int end = mListPadding.top;\n\n\t\t\twhile (nextBottom > end && pos >= 0) {\n\n\t\t\t\tView temp = makeRow(pos, nextBottom, false);\n\t\t\t\tif (temp != null) {\n\t\t\t\t\tselectedView = temp;\n\t\t\t\t}\n\n\t\t\t\tnextBottom = mReferenceView.getTop() - mVerticalSpacing;\n\n\t\t\t\tmFirstPosition = pos;\n\n\t\t\t\tpos -= mNumColumns;\n\t\t\t}\n\n\t\t\tif (mStackFromBottom) {\n\t\t\t\tmFirstPosition = Math.max(0, pos + 1);\n\t\t\t}\n\n\t\t\treturn selectedView;\n\t\t}\n\n\t\t/**\n\t\t * Fills the list from top to bottom, starting with mFirstPosition\n\t\t *\n\t\t * @param nextTop The location where the top of the first item should be\n\t\t *        drawn\n\t\t *\n\t\t * @return The view that is currently selected\n\t\t */\n\t\tprivate View fillFromTop(int nextTop) {\n\t\t\tif (DEBUG) Log.i(TAG, \"fillFromTop() nextLeft: \" + nextTop + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tmFirstPosition = Math.min(mFirstPosition, mSelectedPosition);\n\t\t\tmFirstPosition = Math.min(mFirstPosition, mItemCount - 1);\n\t\t\tif (mFirstPosition < 0) {\n\t\t\t\tmFirstPosition = 0;\n\t\t\t}\n\t\t\tmFirstPosition -= mFirstPosition % mNumColumns;\n\t\t\treturn fillDown(mFirstPosition, nextTop);\n\t\t}\n\n\t\tprivate View fillFromBottom(int lastPosition, int nextBottom) {\n\t\t\tif (DEBUG) Log.i(TAG, \"fillFromBotom() lastPosition: \" + lastPosition + \" nextBottom: \" + nextBottom + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tlastPosition = Math.max(lastPosition, mSelectedPosition);\n\t\t\tlastPosition = Math.min(lastPosition, mItemCount - 1);\n\n\t\t\tfinal int invertedPosition = mItemCount - 1 - lastPosition;\n\t\t\tlastPosition = mItemCount - 1 - (invertedPosition - (invertedPosition % mNumColumns));\n\n\t\t\treturn fillUp(lastPosition, nextBottom);\n\t\t}\n\n\t\tprivate View fillSelection(int childrenTop, int childrenBottom) {\n\t\t\tif (DEBUG) Log.i(TAG, \"fillSelection() childrenTop: \" + childrenTop + \" childrenBottom: \" + childrenBottom + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tfinal int selectedPosition = reconcileSelectedPosition();\n\t\t\tfinal int numColumns = mNumColumns;\n\t\t\tfinal int verticalSpacing = mVerticalSpacing;\n\n\t\t\tint rowStart;\n\t\t\tint rowEnd = -1;\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\trowStart = selectedPosition - (selectedPosition % numColumns);\n\t\t\t} else {\n\t\t\t\tfinal int invertedSelection = mItemCount - 1 - selectedPosition;\n\n\t\t\t\trowEnd = mItemCount - 1 - (invertedSelection - (invertedSelection % numColumns));\n\t\t\t\trowStart = Math.max(0, rowEnd - numColumns + 1);\n\t\t\t}\n\n\t\t\tfinal int fadingEdgeLength = getVerticalFadingEdgeLength();\n\t\t\tfinal int topSelectionPixel = getTopSelectionPixel(childrenTop, fadingEdgeLength, rowStart);\n\n\t\t\tfinal View sel = makeRow(mStackFromBottom ? rowEnd : rowStart, topSelectionPixel, true);\n\t\t\tmFirstPosition = rowStart;\n\n\t\t\tfinal View referenceView = mReferenceView;\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\tfillDown(rowStart + numColumns, referenceView.getBottom() + verticalSpacing);\n\t\t\t\tpinToBottom(childrenBottom);\n\t\t\t\tfillUp(rowStart - numColumns, referenceView.getTop() - verticalSpacing);\n\t\t\t\tadjustViewsUpOrDown();\n\t\t\t} else {\n\t\t\t\tfinal int bottomSelectionPixel = getBottomSelectionPixel(childrenBottom,\n\t\t\t\t\t\tfadingEdgeLength, numColumns, rowStart);\n\t\t\t\tfinal int offset = bottomSelectionPixel - referenceView.getBottom();\n\t\t\t\toffsetChildrenTopAndBottom(offset);\n\t\t\t\tfillUp(rowStart - 1, referenceView.getTop() - verticalSpacing);\n\t\t\t\tpinToTop(childrenTop);\n\t\t\t\tfillDown(rowEnd + numColumns, referenceView.getBottom() + verticalSpacing);\n\t\t\t\tadjustViewsUpOrDown();\n\t\t\t}\n\n\t\t\treturn sel;\n\t\t}\n\n\n\t\t/**\n\t\t * Layout during a scroll that results from tracking motion events. Places\n\t\t * the mMotionPosition view at the offset specified by mMotionViewTop, and\n\t\t * then build surrounding views from there.\n\t\t *\n\t\t * @param position the position at which to start filling\n\t\t * @param top the top of the view at that position\n\t\t * @return The selected view, or null if the selected view is outside the\n\t\t *         visible area.\n\t\t */\n\t\tprivate View fillSpecific(int position, int top) {\n\t\t\tif (DEBUG) Log.i(TAG, \"fillSpecific() position: \" + position + \" top: \" + top + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tfinal int numColumns = mNumColumns;\n\n\t\t\tint motionRowStart;\n\t\t\tint motionRowEnd = -1;\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\tmotionRowStart = position - (position % numColumns);\n\t\t\t} else {\n\t\t\t\tfinal int invertedSelection = mItemCount - 1 - position;\n\n\t\t\t\tmotionRowEnd = mItemCount - 1 - (invertedSelection - (invertedSelection % numColumns));\n\t\t\t\tmotionRowStart = Math.max(0, motionRowEnd - numColumns + 1);\n\t\t\t}\n\n\t\t\tfinal View temp = makeRow(mStackFromBottom ? motionRowEnd : motionRowStart, top, true);\n\n\t\t\t// Possibly changed again in fillUp if we add rows above this one.\n\t\t\tmFirstPosition = motionRowStart;\n\n\t\t\tfinal View referenceView = mReferenceView;\n\t\t\t// We didn't have anything to layout, bail out\n\t\t\tif (referenceView == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tfinal int verticalSpacing = mVerticalSpacing;\n\n\t\t\tView above;\n\t\t\tView below;\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\tabove = fillUp(motionRowStart - numColumns, referenceView.getTop() - verticalSpacing);\n\t\t\t\tadjustViewsUpOrDown();\n\t\t\t\tbelow = fillDown(motionRowStart + numColumns, referenceView.getBottom() + verticalSpacing);\n\t\t\t\t// Check if we have dragged the bottom of the grid too high\n\t\t\t\tfinal int childCount = getChildCount();\n\t\t\t\tif (childCount > 0) {\n\t\t\t\t\tcorrectTooHigh(numColumns, verticalSpacing, childCount);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tbelow = fillDown(motionRowEnd + numColumns, referenceView.getBottom() + verticalSpacing);\n\t\t\t\tadjustViewsUpOrDown();\n\t\t\t\tabove = fillUp(motionRowStart - 1, referenceView.getTop() - verticalSpacing);\n\t\t\t\t// Check if we have dragged the bottom of the grid too high\n\t\t\t\tfinal int childCount = getChildCount();\n\t\t\t\tif (childCount > 0) {\n\t\t\t\t\tcorrectTooLow(numColumns, verticalSpacing, childCount);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (temp != null) {\n\t\t\t\treturn temp;\n\t\t\t} else if (above != null) {\n\t\t\t\treturn above;\n\t\t\t} else {\n\t\t\t\treturn below;\n\t\t\t}\n\t\t}\n\n\t\tprivate void correctTooHigh(int numColumns, int verticalSpacing, int childCount) {\n\t\t\tif (DEBUG) Log.i(TAG, \"correctTooLeft() numColumns: \" + numColumns + \" verticalSpacing: \" + verticalSpacing + \" mFirstPosition: \" + mFirstPosition);\n\t\t\t// First see if the last item is visible\n\t\t\tfinal int lastPosition = mFirstPosition + childCount - 1;\n\t\t\tif (lastPosition == mItemCount - 1 && childCount > 0) {\n\t\t\t\t// Get the last child ...\n\t\t\t\tfinal View lastChild = getChildAt(childCount - 1);\n\n\t\t\t\t// ... and its bottom edge\n\t\t\t\tfinal int lastBottom = lastChild.getBottom();\n\t\t\t\t// This is bottom of our drawable area\n\t\t\t\tfinal int end = (getBottom() - getTop()) - mListPadding.bottom;\n\n\t\t\t\t// This is how far the bottom edge of the last view is from the bottom of the\n\t\t\t\t// drawable area\n\t\t\t\tint bottomOffset = end - lastBottom;\n\n\t\t\t\tfinal View firstChild = getChildAt(0);\n\t\t\t\tfinal int firstTop = firstChild.getTop();\n\n\t\t\t\t// Make sure we are 1) Too high, and 2) Either there are more rows above the\n\t\t\t\t// first row or the first row is scrolled off the top of the drawable area\n\t\t\t\tif (bottomOffset > 0 && (mFirstPosition > 0 || firstTop < mListPadding.top))  {\n\t\t\t\t\tif (mFirstPosition == 0) {\n\t\t\t\t\t\t// Don't pull the top too far down\n\t\t\t\t\t\tbottomOffset = Math.min(bottomOffset, mListPadding.top - firstTop);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Move everything down\n\t\t\t\t\toffsetChildrenTopAndBottom(bottomOffset);\n\t\t\t\t\tif (mFirstPosition > 0) {\n\t\t\t\t\t\t// Fill the gap that was opened above mFirstPosition with more rows, if\n\t\t\t\t\t\t// possible\n\t\t\t\t\t\tfillUp(mFirstPosition - (mStackFromBottom ? 1 : numColumns),\n\t\t\t\t\t\t\t\tfirstChild.getTop() - verticalSpacing);\n\t\t\t\t\t\t// Close up the remaining gap\n\t\t\t\t\t\tadjustViewsUpOrDown();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void correctTooLow(int numColumns, int verticalSpacing, int childCount) {\n\t\t\tif (DEBUG) Log.i(TAG, \"correctTooLow() numColumns: \" + numColumns + \" verticalSpacing: \" + verticalSpacing + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tif (mFirstPosition == 0 && childCount > 0) {\n\t\t\t\t// Get the first child ...\n\t\t\t\tfinal View firstChild = getChildAt(0);\n\n\t\t\t\t// ... and its top edge\n\t\t\t\tfinal int firstTop = firstChild.getTop();\n\n\t\t\t\t// This is top of our drawable area\n\t\t\t\tfinal int start = mListPadding.top;\n\n\t\t\t\t// This is bottom of our drawable area\n\t\t\t\tfinal int end = (getBottom() - getTop()) - mListPadding.bottom;\n\n\t\t\t\t// This is how far the top edge of the first view is from the top of the\n\t\t\t\t// drawable area\n\t\t\t\tint topOffset = firstTop - start;\n\t\t\t\tfinal View lastChild = getChildAt(childCount - 1);\n\t\t\t\tfinal int lastBottom = lastChild.getBottom();\n\t\t\t\tfinal int lastPosition = mFirstPosition + childCount - 1;\n\n\t\t\t\t// Make sure we are 1) Too low, and 2) Either there are more rows below the\n\t\t\t\t// last row or the last row is scrolled off the bottom of the drawable area\n\t\t\t\tif (topOffset > 0 && (lastPosition < mItemCount - 1 || lastBottom > end))  {\n\t\t\t\t\tif (lastPosition == mItemCount - 1 ) {\n\t\t\t\t\t\t// Don't pull the bottom too far up\n\t\t\t\t\t\ttopOffset = Math.min(topOffset, lastBottom - end);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Move everything up\n\t\t\t\t\toffsetChildrenTopAndBottom(-topOffset);\n\t\t\t\t\tif (lastPosition < mItemCount - 1) {\n\t\t\t\t\t\t// Fill the gap that was opened below the last position with more rows, if\n\t\t\t\t\t\t// possible\n\t\t\t\t\t\tfillDown(lastPosition + (!mStackFromBottom ? 1 : numColumns),\n\t\t\t\t\t\t\t\tlastChild.getBottom() + verticalSpacing);\n\t\t\t\t\t\t// Close up the remaining gap\n\t\t\t\t\t\tadjustViewsUpOrDown();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Fills the grid based on positioning the new selection at a specific\n\t\t * location. The selection may be moved so that it does not intersect the\n\t\t * faded edges. The grid is then filled upwards and downwards from there.\n\t\t *\n\t\t * @param selectedTop Where the selected item should be\n\t\t * @param childrenTop Where to start drawing children\n\t\t * @param childrenBottom Last pixel where children can be drawn\n\t\t * @return The view that currently has selection\n\t\t */\n\t\tprivate View fillFromSelection(int selectedTop, int childrenTop, int childrenBottom) {\n\t\t\tif (DEBUG) Log.i(TAG, \"fillFromSelection() selectedTop: \" + selectedTop + \" childrenTop: \" + childrenTop + \" childrenBottom: \" + childrenBottom + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tfinal int fadingEdgeLength = getVerticalFadingEdgeLength();\n\t\t\tfinal int selectedPosition = mSelectedPosition;\n\t\t\tfinal int numColumns = mNumColumns;\n\t\t\tfinal int verticalSpacing = mVerticalSpacing;\n\n\t\t\tint rowStart;\n\t\t\tint rowEnd = -1;\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\trowStart = selectedPosition - (selectedPosition % numColumns);\n\t\t\t} else {\n\t\t\t\tint invertedSelection = mItemCount - 1 - selectedPosition;\n\n\t\t\t\trowEnd = mItemCount - 1 - (invertedSelection - (invertedSelection % numColumns));\n\t\t\t\trowStart = Math.max(0, rowEnd - numColumns + 1);\n\t\t\t}\n\n\t\t\tView sel;\n\t\t\tView referenceView;\n\n\t\t\tint topSelectionPixel = getTopSelectionPixel(childrenTop, fadingEdgeLength, rowStart);\n\t\t\tint bottomSelectionPixel = getBottomSelectionPixel(childrenBottom, fadingEdgeLength,\n\t\t\t\t\tnumColumns, rowStart);\n\n\t\t\tsel = makeRow(mStackFromBottom ? rowEnd : rowStart, selectedTop, true);\n\t\t\t// Possibly changed again in fillUp if we add rows above this one.\n\t\t\tmFirstPosition = rowStart;\n\n\t\t\treferenceView = mReferenceView;\n\t\t\tadjustForTopFadingEdge(referenceView, topSelectionPixel, bottomSelectionPixel);\n\t\t\tadjustForBottomFadingEdge(referenceView, topSelectionPixel, bottomSelectionPixel);\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\tfillUp(rowStart - numColumns, referenceView.getTop() - verticalSpacing);\n\t\t\t\tadjustViewsUpOrDown();\n\t\t\t\tfillDown(rowStart + numColumns, referenceView.getBottom() + verticalSpacing);\n\t\t\t} else {\n\t\t\t\tfillDown(rowEnd + numColumns, referenceView.getBottom() + verticalSpacing);\n\t\t\t\tadjustViewsUpOrDown();\n\t\t\t\tfillUp(rowStart - 1, referenceView.getTop() - verticalSpacing);\n\t\t\t}\n\n\n\t\t\treturn sel;\n\t\t}\n\n\n\t\t/**\n\t\t * Calculate the bottom-most pixel we can draw the selection into\n\t\t *\n\t\t * @param childrenBottom Bottom pixel were children can be drawn\n\t\t * @param fadingEdgeLength Length of the fading edge in pixels, if present\n\t\t * @param numColumns Number of columns in the grid\n\t\t * @param rowStart The start of the row that will contain the selection\n\t\t * @return The bottom-most pixel we can draw the selection into\n\t\t */\n\t\tprivate int getBottomSelectionPixel(int childrenBottom, int fadingEdgeLength,\n\t\t\t\tint numColumns, int rowStart) {\n\t\t\t// Last pixel we can draw the selection into\n\t\t\tint bottomSelectionPixel = childrenBottom;\n\t\t\tif (rowStart + numColumns - 1 < mItemCount - 1) {\n\t\t\t\tbottomSelectionPixel -= fadingEdgeLength;\n\t\t\t}\n\t\t\treturn bottomSelectionPixel;\n\t\t}\n\n\t\t/**\n\t\t * Calculate the top-most pixel we can draw the selection into\n\t\t *\n\t\t * @param childrenTop Top pixel were children can be drawn\n\t\t * @param fadingEdgeLength Length of the fading edge in pixels, if present\n\t\t * @param rowStart The start of the row that will contain the selection\n\t\t * @return The top-most pixel we can draw the selection into\n\t\t */\n\t\tprivate int getTopSelectionPixel(int childrenTop, int fadingEdgeLength, int rowStart) {\n\t\t\t// first pixel we can draw the selection into\n\t\t\tint topSelectionPixel = childrenTop;\n\t\t\tif (rowStart > 0) {\n\t\t\t\ttopSelectionPixel += fadingEdgeLength;\n\t\t\t}\n\t\t\treturn topSelectionPixel;\n\t\t}\n\n\t\t/**\n\t\t * Move all views upwards so the selected row does not interesect the bottom\n\t\t * fading edge (if necessary).\n\t\t *\n\t\t * @param childInSelectedRow A child in the row that contains the selection\n\t\t * @param topSelectionPixel The topmost pixel we can draw the selection into\n\t\t * @param bottomSelectionPixel The bottommost pixel we can draw the\n\t\t *        selection into\n\t\t */\n\t\tprivate void adjustForBottomFadingEdge(View childInSelectedRow,\n\t\t\t\tint topSelectionPixel, int bottomSelectionPixel) {\n\t\t\t// Some of the newly selected item extends below the bottom of the\n\t\t\t// list\n\t\t\tif (childInSelectedRow.getBottom() > bottomSelectionPixel) {\n\n\t\t\t\t// Find space available above the selection into which we can\n\t\t\t\t// scroll upwards\n\t\t\t\tint spaceAbove = childInSelectedRow.getTop() - topSelectionPixel;\n\n\t\t\t\t// Find space required to bring the bottom of the selected item\n\t\t\t\t// fully into view\n\t\t\t\tint spaceBelow = childInSelectedRow.getBottom() - bottomSelectionPixel;\n\t\t\t\tint offset = Math.min(spaceAbove, spaceBelow);\n\n\t\t\t\t// Now offset the selected item to get it into view\n\t\t\t\toffsetChildrenTopAndBottom(-offset);\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Move all views upwards so the selected row does not interesect the top\n\t\t * fading edge (if necessary).\n\t\t *\n\t\t * @param childInSelectedRow A child in the row that contains the selection\n\t\t * @param topSelectionPixel The topmost pixel we can draw the selection into\n\t\t * @param bottomSelectionPixel The bottommost pixel we can draw the\n\t\t *        selection into\n\t\t */\n\t\tprivate void adjustForTopFadingEdge(View childInSelectedRow,\n\t\t\t\tint topSelectionPixel, int bottomSelectionPixel) {\n\t\t\t// Some of the newly selected item extends above the top of the list\n\t\t\tif (childInSelectedRow.getTop() < topSelectionPixel) {\n\t\t\t\t// Find space required to bring the top of the selected item\n\t\t\t\t// fully into view\n\t\t\t\tint spaceAbove = topSelectionPixel - childInSelectedRow.getTop();\n\n\t\t\t\t// Find space available below the selection into which we can\n\t\t\t\t// scroll downwards\n\t\t\t\tint spaceBelow = bottomSelectionPixel - childInSelectedRow.getBottom();\n\t\t\t\tint offset = Math.min(spaceAbove, spaceBelow);\n\n\t\t\t\t// Now offset the selected item to get it into view\n\t\t\t\toffsetChildrenTopAndBottom(offset);\n\t\t\t}\n\t\t}\n\n\t\tprivate void determineColumns(int availableSpace) {\n\t\t\tfinal int requestedHorizontalSpacing = mRequestedHorizontalSpacing;\n\t\t\tfinal int stretchMode = mStretchMode;\n\t\t\tfinal int requestedColumnWidth = mRequestedColumnWidth;\n\t\t\tmVerticalSpacing = mRequestedVerticalSpacing;\n\n\t\t\tif (mRequestedNumColumns == AUTO_FIT) {\n\t\t\t\tif (requestedColumnWidth > 0) {\n\t\t\t\t\t// Client told us to pick the number of columns\n\t\t\t\t\tmNumColumns = (availableSpace + requestedHorizontalSpacing) /\n\t\t\t\t\t(requestedColumnWidth + requestedHorizontalSpacing);\n\t\t\t\t} else {\n\t\t\t\t\t// Just make up a number if we don't have enough info\n\t\t\t\t\tmNumColumns = 2;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// We picked the columns\n\t\t\t\tmNumColumns = mRequestedNumColumns;\n\t\t\t}\n\n\t\t\tif (mNumColumns <= 0) {\n\t\t\t\tmNumColumns = 1;\n\t\t\t}\n\n\t\t\tswitch (stretchMode) {\n\t\t\tcase NO_STRETCH:\n\t\t\t\t// Nobody stretches\n\t\t\t\tmColumnWidth = requestedColumnWidth;\n\t\t\t\tmHorizontalSpacing = requestedHorizontalSpacing;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tint spaceLeftOver = 0;\n\t\t\t\tswitch (stretchMode) {\n\t\t\t\tcase STRETCH_COLUMN_WIDTH:\n\t\t\t\t\t// Stretch the columns\n\t\t\t\t\tspaceLeftOver = availableSpace - (mNumColumns * requestedColumnWidth) -\n\t\t\t\t\t((mNumColumns - 1) * requestedHorizontalSpacing);\n\t\t\t\t\tmColumnWidth = requestedColumnWidth + spaceLeftOver / mNumColumns;\n\t\t\t\t\tmHorizontalSpacing = requestedHorizontalSpacing;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase STRETCH_SPACING:\n\t\t\t\t\t// Stretch the spacing between columns\n\t\t\t\t\tspaceLeftOver = availableSpace - (mNumColumns * requestedColumnWidth) -\n\t\t\t\t\t((mNumColumns - 1) * requestedHorizontalSpacing);\n\t\t\t\t\tmColumnWidth = requestedColumnWidth;\n\t\t\t\t\tif (mNumColumns > 1) {\n\t\t\t\t\t\tmHorizontalSpacing = requestedHorizontalSpacing +\n\t\t\t\t\t\tspaceLeftOver / (mNumColumns - 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmHorizontalSpacing = requestedHorizontalSpacing + spaceLeftOver;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase STRETCH_SPACING_UNIFORM:\n\t\t\t\t\t// Stretch the spacing between columns\n\t\t\t\t\tspaceLeftOver = availableSpace - (mNumColumns * requestedColumnWidth) -\n\t\t\t\t\t((mNumColumns + 1) * requestedHorizontalSpacing);\n\t\t\t\t\tmColumnWidth = requestedColumnWidth;\n\t\t\t\t\tif (mNumColumns > 1) {\n\t\t\t\t\t\tmHorizontalSpacing = requestedHorizontalSpacing +\n\t\t\t\t\t\tspaceLeftOver / (mNumColumns + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmHorizontalSpacing = ((requestedHorizontalSpacing * 2) + spaceLeftOver) / 2;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Fills the grid based on positioning the new selection relative to the old\n\t\t * selection. The new selection will be placed at, above, or below the\n\t\t * location of the new selection depending on how the selection is moving.\n\t\t * The selection will then be pinned to the visible part of the screen,\n\t\t * excluding the edges that are faded. The grid is then filled upwards and\n\t\t * downwards from there.\n\t\t *\n\t\t * @param delta Which way we are moving\n\t\t * @param childrenTop Where to start drawing children\n\t\t * @param childrenBottom Last pixel where children can be drawn\n\t\t * @return The view that currently has selection\n\t\t */\n\t\tprivate View moveSelection(int delta, int childrenTop, int childrenBottom) {\n\t\t\tif (DEBUG) Log.i(TAG, \"moveSelection() delta: \" + delta + \" childrenTop: \" + childrenTop + \" childrenBottom: \" + childrenBottom + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tfinal int fadingEdgeLength = getVerticalFadingEdgeLength();\n\t\t\tfinal int selectedPosition = mSelectedPosition;\n\t\t\tfinal int numColumns = mNumColumns;\n\t\t\tfinal int verticalSpacing = mVerticalSpacing;\n\n\t\t\tint oldRowStart;\n\t\t\tint rowStart;\n\t\t\tint rowEnd = -1;\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\toldRowStart = (selectedPosition - delta) - ((selectedPosition - delta) % numColumns);\n\n\t\t\t\trowStart = selectedPosition - (selectedPosition % numColumns);\n\t\t\t} else {\n\t\t\t\tint invertedSelection = mItemCount - 1 - selectedPosition;\n\n\t\t\t\trowEnd = mItemCount - 1 - (invertedSelection - (invertedSelection % numColumns));\n\t\t\t\trowStart = Math.max(0, rowEnd - numColumns + 1);\n\n\t\t\t\tinvertedSelection = mItemCount - 1 - (selectedPosition - delta);\n\t\t\t\toldRowStart = mItemCount - 1 - (invertedSelection - (invertedSelection % numColumns));\n\t\t\t\toldRowStart = Math.max(0, oldRowStart - numColumns + 1);\n\t\t\t}\n\n\t\t\tfinal int rowDelta = rowStart - oldRowStart;\n\n\t\t\tfinal int topSelectionPixel = getTopSelectionPixel(childrenTop, fadingEdgeLength, rowStart);\n\t\t\tfinal int bottomSelectionPixel = getBottomSelectionPixel(childrenBottom, fadingEdgeLength,\n\t\t\t\t\tnumColumns, rowStart);\n\n\t\t\t// Possibly changed again in fillUp if we add rows above this one.\n\t\t\tmFirstPosition = rowStart;\n\n\t\t\tView sel;\n\t\t\tView referenceView;\n\n\t\t\tif (rowDelta > 0) {\n\t\t\t\t/*\n\t\t\t\t * Case 1: Scrolling down.\n\t\t\t\t */\n\n\t\t\t\tfinal int oldBottom = mReferenceViewInSelectedRow == null ? 0 :\n\t\t\t\t\tmReferenceViewInSelectedRow.getBottom();\n\n\t\t\t\tsel = makeRow(mStackFromBottom ? rowEnd : rowStart, oldBottom + verticalSpacing, true);\n\t\t\t\treferenceView = mReferenceView;\n\n\t\t\t\tadjustForBottomFadingEdge(referenceView, topSelectionPixel, bottomSelectionPixel);\n\t\t\t} else if (rowDelta < 0) {\n\t\t\t\t/*\n\t\t\t\t * Case 2: Scrolling up.\n\t\t\t\t */\n\t\t\t\tfinal int oldTop = mReferenceViewInSelectedRow == null ?\n\t\t\t\t\t\t0 : mReferenceViewInSelectedRow .getTop();\n\n\t\t\t\tsel = makeRow(mStackFromBottom ? rowEnd : rowStart, oldTop - verticalSpacing, false);\n\t\t\t\treferenceView = mReferenceView;\n\n\t\t\t\tadjustForTopFadingEdge(referenceView, topSelectionPixel, bottomSelectionPixel);\n\t\t\t} else {\n\t\t\t\t/*\n\t\t\t\t * Keep selection where it was\n\t\t\t\t */\n\t\t\t\tfinal int oldTop = mReferenceViewInSelectedRow == null ?\n\t\t\t\t\t\t0 : mReferenceViewInSelectedRow .getTop();\n\n\t\t\t\tsel = makeRow(mStackFromBottom ? rowEnd : rowStart, oldTop, true);\n\t\t\t\treferenceView = mReferenceView;\n\t\t\t}\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\tfillUp(rowStart - numColumns, referenceView.getTop() - verticalSpacing);\n\t\t\t\tadjustViewsUpOrDown();\n\t\t\t\tfillDown(rowStart + numColumns, referenceView.getBottom() + verticalSpacing);\n\t\t\t} else {\n\t\t\t\tfillDown(rowEnd + numColumns, referenceView.getBottom() + verticalSpacing);\n\t\t\t\tadjustViewsUpOrDown();\n\t\t\t\tfillUp(rowStart - 1, referenceView.getTop() - verticalSpacing);\n\t\t\t}\n\n\t\t\treturn sel;\n\t\t}\n\n\t\t@Override\n\t\tprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n\t\t\tint widthMode = MeasureSpec.getMode(widthMeasureSpec);\n\t\t\tint heightMode = MeasureSpec.getMode(heightMeasureSpec);\n\t\t\tint widthSize = MeasureSpec.getSize(widthMeasureSpec);\n\t\t\tint heightSize = MeasureSpec.getSize(heightMeasureSpec);\n\t\t\tif (DEBUG) Log.i(TAG, \"vertical onMeasure heightMode: \" + heightMode);\n\t\t\tif (widthMode == MeasureSpec.UNSPECIFIED) {\n\t\t\t\tif (mColumnWidth > 0) {\n\t\t\t\t\twidthSize = mColumnWidth + mListPadding.left + mListPadding.right;\n\t\t\t\t} else {\n\t\t\t\t\twidthSize = mListPadding.left + mListPadding.right;\n\t\t\t\t}\n\t\t\t\twidthSize += getVerticalScrollbarWidth();\n\t\t\t}\n\n\t\t\tint childWidth = widthSize - mListPadding.left - mListPadding.right;\n\t\t\tdetermineColumns(childWidth);\n\n\t\t\tint childHeight = 0;\n\n\t\t\tmItemCount = mAdapter == null ? 0 : mAdapter.getCount();\n\t\t\tfinal int count = mItemCount;\n\t\t\tif (count > 0) {\n\t\t\t\tfinal View child = obtainView(0, mIsScrap);\n\n\t\t\t\tTwoWayAbsListView.LayoutParams p = (TwoWayAbsListView.LayoutParams)child.getLayoutParams();\n\t\t\t\tif (p == null) {\n\t\t\t\t\tp = new TwoWayAbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,\n\t\t\t\t\t\t\tViewGroup.LayoutParams.WRAP_CONTENT, 0);\n\t\t\t\t\tchild.setLayoutParams(p);\n\t\t\t\t}\n\t\t\t\tp.viewType = mAdapter.getItemViewType(0);\n\t\t\t\tp.forceAdd = true;\n\n\t\t\t\tint childHeightSpec = getChildMeasureSpec(\n\t\t\t\t\t\tMeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, p.height);\n\t\t\t\tint childWidthSpec = getChildMeasureSpec(\n\t\t\t\t\t\tMeasureSpec.makeMeasureSpec(mColumnWidth, MeasureSpec.EXACTLY), 0, p.width);\n\t\t\t\tchild.measure(childWidthSpec, childHeightSpec);\n\n\t\t\t\tchildHeight = child.getMeasuredHeight();\n\n\t\t\t\tif (mRecycler.shouldRecycleViewType(p.viewType)) {\n\t\t\t\t\tmRecycler.addScrapView(child);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (heightMode == MeasureSpec.UNSPECIFIED) {\n\t\t\t\theightSize = mListPadding.top + mListPadding.bottom + childHeight +\n\t\t\t\tgetVerticalFadingEdgeLength() * 2;\n\t\t\t}\n\n\t\t\tif (heightMode == MeasureSpec.AT_MOST) {\n\t\t\t\tint ourSize =  mListPadding.top + mListPadding.bottom;\n\n\t\t\t\tfinal int numColumns = mNumColumns;\n\t\t\t\tfor (int i = 0; i < count; i += numColumns) {\n\t\t\t\t\tourSize += childHeight;\n\t\t\t\t\tif (i + numColumns < count) {\n\t\t\t\t\t\tourSize += mVerticalSpacing;\n\t\t\t\t\t}\n\t\t\t\t\tif (ourSize >= heightSize) {\n\t\t\t\t\t\tourSize = heightSize;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\theightSize = ourSize;\n\t\t\t}\n\n\t\t\tsetMeasuredDimension(widthSize, heightSize);\n\t\t\tmWidthMeasureSpec = widthMeasureSpec;\n\t\t\tif (DEBUG) Log.i(TAG, \"Vertical onMeasure widthSize: \" + widthSize + \" heightSize: \" + heightSize);\n\t\t}\n\n\t\t@Override\n\t\tprotected void layoutChildren() {\n\t\t\tfinal int childrenTop = mListPadding.top;\n\t\t\tfinal int childrenBottom = getBottom() - getTop() - mListPadding.bottom;\n\n\t\t\tint childCount = getChildCount();\n\t\t\tint index;\n\t\t\tint delta = 0;\n\n\t\t\tView sel;\n\t\t\tView oldSel = null;\n\t\t\tView oldFirst = null;\n\t\t\tView newSel = null;\n\n\t\t\t// Remember stuff we will need down below\n\t\t\tswitch (mLayoutMode) {\n\t\t\tcase LAYOUT_SET_SELECTION:\n\t\t\t\tindex = mNextSelectedPosition - mFirstPosition;\n\t\t\t\tif (index >= 0 && index < childCount) {\n\t\t\t\t\tnewSel = getChildAt(index);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase LAYOUT_FORCE_TOP:\n\t\t\tcase LAYOUT_FORCE_BOTTOM:\n\t\t\tcase LAYOUT_SPECIFIC:\n\t\t\tcase LAYOUT_SYNC:\n\t\t\t\tbreak;\n\t\t\tcase LAYOUT_MOVE_SELECTION:\n\t\t\t\tif (mNextSelectedPosition >= 0) {\n\t\t\t\t\tdelta = mNextSelectedPosition - mSelectedPosition;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t// Remember the previously selected view\n\t\t\t\tindex = mSelectedPosition - mFirstPosition;\n\t\t\t\tif (index >= 0 && index < childCount) {\n\t\t\t\t\toldSel = getChildAt(index);\n\t\t\t\t}\n\n\t\t\t\t// Remember the previous first child\n\t\t\t\toldFirst = getChildAt(0);\n\t\t\t}\n\n\t\t\tboolean dataChanged = mDataChanged;\n\t\t\tif (dataChanged) {\n\t\t\t\thandleDataChanged();\n\t\t\t}\n\n\t\t\t// Handle the empty set by removing all views that are visible\n\t\t\t// and calling it a day\n\t\t\tif (mItemCount == 0) {\n\t\t\t\tresetList();\n\t\t\t\tinvokeOnItemScrollListener();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsetSelectedPositionInt(mNextSelectedPosition);\n\n\t\t\t// Pull all children into the RecycleBin.\n\t\t\t// These views will be reused if possible\n\t\t\tfinal int firstPosition = mFirstPosition;\n\t\t\tfinal RecycleBin recycleBin = mRecycler;\n\n\t\t\tif (dataChanged) {\n\t\t\t\tfor (int i = 0; i < childCount; i++) {\n\t\t\t\t\trecycleBin.addScrapView(getChildAt(i));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trecycleBin.fillActiveViews(childCount, firstPosition);\n\t\t\t}\n\n\t\t\t// Clear out old views\n\t\t\t//removeAllViewsInLayout();\n\t\t\tdetachAllViewsFromParent();\n\n\t\t\tswitch (mLayoutMode) {\n\t\t\tcase LAYOUT_SET_SELECTION:\n\t\t\t\tif (newSel != null) {\n\t\t\t\t\tsel = fillFromSelection(newSel.getTop(), childrenTop, childrenBottom);\n\t\t\t\t} else {\n\t\t\t\t\tsel = fillSelection(childrenTop, childrenBottom);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase LAYOUT_FORCE_TOP:\n\t\t\t\tmFirstPosition = 0;\n\t\t\t\tsel = fillFromTop(childrenTop);\n\t\t\t\tadjustViewsUpOrDown();\n\t\t\t\tbreak;\n\t\t\tcase LAYOUT_FORCE_BOTTOM:\n\t\t\t\tsel = fillUp(mItemCount - 1, childrenBottom);\n\t\t\t\tadjustViewsUpOrDown();\n\t\t\t\tbreak;\n\t\t\tcase LAYOUT_SPECIFIC:\n\t\t\t\tsel = fillSpecific(mSelectedPosition, mSpecificTop);\n\t\t\t\tbreak;\n\t\t\tcase LAYOUT_SYNC:\n\t\t\t\tsel = fillSpecific(mSyncPosition, mSpecificTop);\n\t\t\t\tbreak;\n\t\t\tcase LAYOUT_MOVE_SELECTION:\n\t\t\t\t// Move the selection relative to its old position\n\t\t\t\tsel = moveSelection(delta, childrenTop, childrenBottom);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tif (childCount == 0) {\n\t\t\t\t\tif (!mStackFromBottom) {\n\t\t\t\t\t\tsetSelectedPositionInt(mAdapter == null || isInTouchMode() ?\n\t\t\t\t\t\t\t\tINVALID_POSITION : 0);\n\t\t\t\t\t\tsel = fillFromTop(childrenTop);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfinal int last = mItemCount - 1;\n\t\t\t\t\t\tsetSelectedPositionInt(mAdapter == null || isInTouchMode() ?\n\t\t\t\t\t\t\t\tINVALID_POSITION : last);\n\t\t\t\t\t\tsel = fillFromBottom(last, childrenBottom);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (mSelectedPosition >= 0 && mSelectedPosition < mItemCount) {\n\t\t\t\t\t\tsel = fillSpecific(mSelectedPosition, oldSel == null ?\n\t\t\t\t\t\t\t\tchildrenTop : oldSel.getTop());\n\t\t\t\t\t} else if (mFirstPosition < mItemCount)  {\n\t\t\t\t\t\tsel = fillSpecific(mFirstPosition, oldFirst == null ?\n\t\t\t\t\t\t\t\tchildrenTop : oldFirst.getTop());\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsel = fillSpecific(0, childrenTop);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Flush any cached views that did not get reused above\n\t\t\trecycleBin.scrapActiveViews();\n\n\t\t\tif (sel != null) {\n\t\t\t\tpositionSelector(sel);\n\t\t\t\tmSelectedTop = sel.getTop();\n\t\t\t} else if (mTouchMode > TOUCH_MODE_DOWN && mTouchMode < TOUCH_MODE_SCROLL) {\n\t\t\t\tView child = getChildAt(mMotionPosition - mFirstPosition);\n\t\t\t\tif (child != null) positionSelector(child);\n\t\t\t} else {\n\t\t\t\tmSelectedTop = 0;\n\t\t\t\tmSelectorRect.setEmpty();\n\t\t\t}\n\n\t\t\tmLayoutMode = LAYOUT_NORMAL;\n\t\t\tmDataChanged = false;\n\t\t\tmNeedSync = false;\n\t\t\tsetNextSelectedPositionInt(mSelectedPosition);\n\n\t\t\tupdateScrollIndicators();\n\n\t\t\tif (mItemCount > 0) {\n\t\t\t\tcheckSelectionChanged();\n\t\t\t}\n\n\t\t\tinvokeOnItemScrollListener();\n\n\t\t}\n\n\t\t/**\n\t\t * Make sure views are touching the top or bottom edge, as appropriate for\n\t\t * our gravity\n\t\t */\n\t\tprivate void adjustViewsUpOrDown() {\n\t\t\tfinal int childCount = getChildCount();\n\n\t\t\tif (childCount > 0) {\n\t\t\t\tint delta;\n\t\t\t\tView child;\n\n\t\t\t\tif (!mStackFromBottom) {\n\t\t\t\t\t// Uh-oh -- we came up short. Slide all views up to make them\n\t\t\t\t\t// align with the top\n\t\t\t\t\tchild = getChildAt(0);\n\t\t\t\t\tdelta = child.getTop() - mListPadding.top;\n\t\t\t\t\tif (mFirstPosition != 0) {\n\t\t\t\t\t\t// It's OK to have some space above the first item if it is\n\t\t\t\t\t\t// part of the vertical spacing\n\t\t\t\t\t\tdelta -= mVerticalSpacing;\n\t\t\t\t\t}\n\t\t\t\t\tif (delta < 0) {\n\t\t\t\t\t\t// We only are looking to see if we are too low, not too high\n\t\t\t\t\t\tdelta = 0;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// we are too high, slide all views down to align with bottom\n\t\t\t\t\tchild = getChildAt(childCount - 1);\n\t\t\t\t\tdelta = child.getBottom() - (getHeight() - mListPadding.bottom);\n\n\t\t\t\t\tif (mFirstPosition + childCount < mItemCount) {\n\t\t\t\t\t\t// It's OK to have some space below the last item if it is\n\t\t\t\t\t\t// part of the vertical spacing\n\t\t\t\t\t\tdelta += mVerticalSpacing;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (delta > 0) {\n\t\t\t\t\t\t// We only are looking to see if we are too high, not too low\n\t\t\t\t\t\tdelta = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (delta != 0) {\n\t\t\t\t\toffsetChildrenTopAndBottom(-delta);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Add a view as a child and make sure it is measured (if necessary) and\n\t\t * positioned properly.\n\t\t *\n\t\t * @param child The view to add\n\t\t * @param position The position of the view\n\t\t * @param y The y position relative to which this view will be positioned\n\t\t * @param flow if true, align top edge to y. If false, align bottom edge\n\t\t *        to y.\n\t\t * @param childrenLeft Left edge where children should be positioned\n\t\t * @param selected Is this position selected?\n\t\t * @param recycled Has this view been pulled from the recycle bin? If so it\n\t\t *        does not need to be remeasured.\n\t\t * @param where Where to add the item in the list\n\t\t *\n\t\t */\n\t\tprivate void setupChild(View child, int position, int y, boolean flow, int childrenLeft,\n\t\t\t\tboolean selected, boolean recycled, int where) {\n\t\t\tboolean isSelected = selected && shouldShowSelector();\n\t\t\tfinal boolean updateChildSelected = isSelected != child.isSelected();\n\t\t\tfinal int mode = mTouchMode;\n\t\t\tfinal boolean isPressed = mode > TOUCH_MODE_DOWN && mode < TOUCH_MODE_SCROLL &&\n\t\t\tmMotionPosition == position;\n\t\t\tfinal boolean updateChildPressed = isPressed != child.isPressed();\n\n\t\t\tboolean needToMeasure = !recycled || updateChildSelected || child.isLayoutRequested();\n\n\t\t\t// Respect layout params that are already in the view. Otherwise make\n\t\t\t// some up...\n\t\t\tTwoWayAbsListView.LayoutParams p = (TwoWayAbsListView.LayoutParams)child.getLayoutParams();\n\t\t\tif (p == null) {\n\t\t\t\tp = new TwoWayAbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,\n\t\t\t\t\t\tViewGroup.LayoutParams.WRAP_CONTENT, 0);\n\t\t\t}\n\t\t\tp.viewType = mAdapter.getItemViewType(position);\n\n\t\t\tif (recycled && !p.forceAdd) {\n\t\t\t\tattachViewToParent(child, where, p);\n\t\t\t} else {\n\t\t\t\tp.forceAdd = false;\n\t\t\t\taddViewInLayout(child, where, p, true);\n\t\t\t}\n\n\t\t\tif (updateChildSelected) {\n\t\t\t\tchild.setSelected(isSelected);\n\t\t\t\tif (isSelected) {\n\t\t\t\t\trequestFocus();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (updateChildPressed) {\n\t\t\t\tchild.setPressed(isPressed);\n\t\t\t}\n\n\t\t\tif (needToMeasure) {\n\t\t\t\tint childHeightSpec = ViewGroup.getChildMeasureSpec(\n\t\t\t\t\t\tMeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, p.height);\n\n\t\t\t\tint childWidthSpec = ViewGroup.getChildMeasureSpec(\n\t\t\t\t\t\tMeasureSpec.makeMeasureSpec(mColumnWidth, MeasureSpec.EXACTLY), 0, p.width);\n\t\t\t\tchild.measure(childWidthSpec, childHeightSpec);\n\t\t\t} else {\n\t\t\t\tcleanupLayoutState(child);\n\t\t\t}\n\n\t\t\tfinal int w = child.getMeasuredWidth();\n\t\t\tfinal int h = child.getMeasuredHeight();\n\n\t\t\tint childLeft;\n\t\t\tfinal int childTop = flow ? y : y - h;\n\n\t\t\tswitch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {\n\t\t\tcase Gravity.LEFT:\n\t\t\t\tchildLeft = childrenLeft;\n\t\t\t\tbreak;\n\t\t\tcase Gravity.CENTER_HORIZONTAL:\n\t\t\t\tchildLeft = childrenLeft + ((mColumnWidth - w) / 2);\n\t\t\t\tbreak;\n\t\t\tcase Gravity.RIGHT:\n\t\t\t\tchildLeft = childrenLeft + mColumnWidth - w;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tchildLeft = childrenLeft;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (needToMeasure) {\n\t\t\t\tfinal int childRight = childLeft + w;\n\t\t\t\tfinal int childBottom = childTop + h;\n\t\t\t\tchild.layout(childLeft, childTop, childRight, childBottom);\n\t\t\t} else {\n\t\t\t\tchild.offsetLeftAndRight(childLeft - child.getLeft());\n\t\t\t\tchild.offsetTopAndBottom(childTop - child.getTop());\n\t\t\t}\n\n\t\t\tif (mCachingStarted) {\n\t\t\t\tchild.setDrawingCacheEnabled(true);\n\t\t\t}\n\t\t}\n\n\t\tprivate void pinToTop(int childrenTop) {\n\t\t\tif (mFirstPosition == 0) {\n\t\t\t\tfinal int top = getChildAt(0).getTop();\n\t\t\t\tfinal int offset = childrenTop - top;\n\t\t\t\tif (offset < 0) {\n\t\t\t\t\toffsetChildrenTopAndBottom(offset);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void pinToBottom(int childrenBottom) {\n\t\t\tfinal int count = getChildCount();\n\t\t\tif (mFirstPosition + count == mItemCount) {\n\t\t\t\tfinal int bottom = getChildAt(count - 1).getBottom();\n\t\t\t\tfinal int offset = childrenBottom - bottom;\n\t\t\t\tif (offset > 0) {\n\t\t\t\t\toffsetChildrenTopAndBottom(offset);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Makes the item at the supplied position selected.\n\t\t *\n\t\t * @param position the position of the new selection\n\t\t */\n\t\t@Override\n\t\tprotected void setSelectionInt(int position) {\n\t\t\tint previousSelectedPosition = mNextSelectedPosition;\n\n\t\t\tsetNextSelectedPositionInt(position);\n\t\t\tTwoWayGridView.this.layoutChildren();\n\n\t\t\tfinal int next = mStackFromBottom ? mItemCount - 1  - mNextSelectedPosition :\n\t\t\t\tmNextSelectedPosition;\n\t\t\tfinal int previous = mStackFromBottom ? mItemCount - 1\n\t\t\t\t\t- previousSelectedPosition : previousSelectedPosition;\n\n\t\t\tfinal int nextRow = next / mNumColumns;\n\t\t\tfinal int previousRow = previous / mNumColumns;\n\n\t\t\tif (nextRow != previousRow) {\n\t\t\t\t//awakenScrollBars();\n\t\t\t}\n\n\t\t}\n\n\n\t\t/**\n\t\t * Scrolls to the next or previous item, horizontally or vertically.\n\t\t *\n\t\t * @param direction either {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},\n\t\t *        {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN}\n\t\t *\n\t\t * @return whether selection was moved\n\t\t */\n\t\t@Override\n\t\tprotected boolean arrowScroll(int direction) {\n\t\t\tfinal int selectedPosition = mSelectedPosition;\n\t\t\tfinal int numColumns = mNumColumns;\n\n\t\t\tint startOfRowPos;\n\t\t\tint endOfRowPos;\n\n\t\t\tboolean moved = false;\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\tstartOfRowPos = (selectedPosition / numColumns) * numColumns;\n\t\t\t\tendOfRowPos = Math.min(startOfRowPos + numColumns - 1, mItemCount - 1);\n\t\t\t} else {\n\t\t\t\tfinal int invertedSelection = mItemCount - 1 - selectedPosition;\n\t\t\t\tendOfRowPos = mItemCount - 1 - (invertedSelection / numColumns) * numColumns;\n\t\t\t\tstartOfRowPos = Math.max(0, endOfRowPos - numColumns + 1);\n\t\t\t}\n\n\t\t\tswitch (direction) {\n\t\t\tcase FOCUS_UP:\n\t\t\t\tif (startOfRowPos > 0) {\n\t\t\t\t\tmLayoutMode = LAYOUT_MOVE_SELECTION;\n\t\t\t\t\tsetSelectionInt(Math.max(0, selectedPosition - numColumns));\n\t\t\t\t\tmoved = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase FOCUS_DOWN:\n\t\t\t\tif (endOfRowPos < mItemCount - 1) {\n\t\t\t\t\tmLayoutMode = LAYOUT_MOVE_SELECTION;\n\t\t\t\t\tsetSelectionInt(Math.min(selectedPosition + numColumns, mItemCount - 1));\n\t\t\t\t\tmoved = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase FOCUS_LEFT:\n\t\t\t\tif (selectedPosition > startOfRowPos) {\n\t\t\t\t\tmLayoutMode = LAYOUT_MOVE_SELECTION;\n\t\t\t\t\tsetSelectionInt(Math.max(0, selectedPosition - 1));\n\t\t\t\t\tmoved = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase FOCUS_RIGHT:\n\t\t\t\tif (selectedPosition < endOfRowPos) {\n\t\t\t\t\tmLayoutMode = LAYOUT_MOVE_SELECTION;\n\t\t\t\t\tsetSelectionInt(Math.min(selectedPosition + 1, mItemCount - 1));\n\t\t\t\t\tmoved = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (moved) {\n\t\t\t\tplaySoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));\n\t\t\t\tinvokeOnItemScrollListener();\n\t\t\t}\n\n\t\t\tif (moved) {\n\t\t\t\t//awakenScrollBars();\n\t\t\t}\n\n\t\t\treturn moved;\n\t\t}\n\n\t\t/**\n\t\t * Is childIndex a candidate for next focus given the direction the focus\n\t\t * change is coming from?\n\t\t * @param childIndex The index to check.\n\t\t * @param direction The direction, one of\n\t\t *        {FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}\n\t\t * @return Whether childIndex is a candidate.\n\t\t */\n\t\t@Override\n\t\tprotected boolean isCandidateSelection(int childIndex, int direction) {\n\t\t\tfinal int count = getChildCount();\n\t\t\tfinal int invertedIndex = count - 1 - childIndex;\n\n\t\t\tint rowStart;\n\t\t\tint rowEnd;\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\trowStart = childIndex - (childIndex % mNumColumns);\n\t\t\t\trowEnd = Math.max(rowStart + mNumColumns - 1, count);\n\t\t\t} else {\n\t\t\t\trowEnd = count - 1 - (invertedIndex - (invertedIndex % mNumColumns));\n\t\t\t\trowStart = Math.max(0, rowEnd - mNumColumns + 1);\n\t\t\t}\n\n\t\t\tswitch (direction) {\n\t\t\tcase View.FOCUS_RIGHT:\n\t\t\t\t// coming from left, selection is only valid if it is on left\n\t\t\t\t// edge\n\t\t\t\treturn childIndex == rowStart;\n\t\t\tcase View.FOCUS_DOWN:\n\t\t\t\t// coming from top; only valid if in top row\n\t\t\t\treturn rowStart == 0;\n\t\t\tcase View.FOCUS_LEFT:\n\t\t\t\t// coming from right, must be on right edge\n\t\t\t\treturn childIndex == rowEnd;\n\t\t\tcase View.FOCUS_UP:\n\t\t\t\t// coming from bottom, need to be in last row\n\t\t\t\treturn rowEnd == count - 1;\n\t\t\tcase View.FOCUS_FORWARD:\n\t\t\t\t// coming from top-left, need to be first in top row\n\t\t\t\treturn childIndex == rowStart && rowStart == 0;\n\t\t\tcase View.FOCUS_BACKWARD:\n\t\t\t\t// coming from bottom-right, need to be last in bottom row\n\t\t\t\treturn childIndex == rowEnd && rowEnd == count - 1;\n\t\t\tdefault:\n\t\t\t\tthrow new IllegalArgumentException(\"direction must be one of \"\n\t\t\t\t  + \"{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT, \"\n\t\t\t\t  + \"FOCUS_FORWARD, FOCUS_BACKWARD}\");\n\t\t\t}\n\t\t}\n\n\t}\n\n\n\t/////////////////////////////////////////////////////////////////////////////////////////////////////\n\t//\n\t// Horizontal Grid Builder\n\t//\n\t/////////////////////////////////////////////////////////////////////////////////////////////////////\n\tprivate class HorizontalGridBuilder extends GridBuilder {\n\t\t/**\n\t\t * Obtain the view and add it to our list of children. The view can be made\n\t\t * fresh, converted from an unused view, or used as is if it was in the\n\t\t * recycle bin.\n\t\t *\n\t\t * @param position Logical position in the list\n\t\t * @param x Left or Right edge of the view to add\n\t\t * @param flow if true, align left edge to x. If false, align right edge to\n\t\t *        x.\n\t\t * @param childrenTop Top edge where children should be positioned\n\t\t * @param selected Is this position selected?\n\t\t * @param where to add new item in the list\n\t\t * @return View that was added\n\t\t */\n\t\t@Override\n\t\tprotected View makeAndAddView(int position, int x, boolean flow, int childrenTop,\n\t\t\t\tboolean selected, int where) {\n\t\t\tView child;\n\n\t\t\tif (!mDataChanged) {\n\t\t\t\t// Try to use an existing view for this position\n\t\t\t\tchild = mRecycler.getActiveView(position);\n\t\t\t\tif (child != null) {\n\t\t\t\t\t// Found it -- we're using an existing child\n\t\t\t\t\t// This just needs to be positioned\n\t\t\t\t\tsetupChild(child, position, x, flow, childrenTop, selected, true, where);\n\t\t\t\t\tif (DEBUG) Log.i(TAG, \"makeAndAddView() - end - position: \" + position + \" reused a view\");\n\t\t\t\t\treturn child;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Make a new view for this position, or convert an unused view if\n\t\t\t// possible\n\t\t\tchild = obtainView(position, mIsScrap);\n\n\t\t\t// This needs to be positioned and measured\n\t\t\tsetupChild(child, position, x, flow, childrenTop, selected, mIsScrap[0], where);\n\t\t\tif (DEBUG) Log.i(TAG, \"makeAndAddView() - end - position: \" + position + \"did NOT reuse a view - scrap: \" + mIsScrap[0]);\n\t\t\treturn child;\n\t\t}\n\n\t\t@Override\n\t\tprotected void fillGap(boolean right) {\n\t\t\tfinal int numRows = mNumRows;\n\t\t\tfinal int horizontalSpacing = mHorizontalSpacing;\n\n\t\t\tfinal int count = getChildCount();\n\n\t\t\tif (right) {\n\t\t\t\tfinal int startOffset = count > 0 ?\n\t\t\t\t\t\tgetChildAt(count - 1).getRight() + horizontalSpacing : getListPaddingLeft();\n\t\t\t\t\t\tint position = mFirstPosition + count;\n\t\t\t\t\t\tif (mStackFromBottom) {\n\t\t\t\t\t\t\tposition += numRows - 1;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfillRight(position, startOffset);\n\t\t\t\t\t\tcorrectTooLeft(numRows, horizontalSpacing, getChildCount());\n\t\t\t} else {\n\t\t\t\tfinal int startOffset = count > 0 ?\n\t\t\t\t\t\tgetChildAt(0).getLeft() - horizontalSpacing : getWidth() - getListPaddingRight();\n\t\t\t\t\t\tint position = mFirstPosition;\n\t\t\t\t\t\tif (!mStackFromBottom) {\n\t\t\t\t\t\t\tposition -= numRows;\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tposition--;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tfillLeft(position, startOffset);\n\t\t\t\t\t\tcorrectTooRight(numRows, horizontalSpacing, getChildCount());\n\t\t\t}\n\t\t}\n\n\n\t\t/**\n\t\t * Fills the list from pos right to the end of the list view.\n\t\t *\n\t\t * @param pos The first position to put in the list\n\t\t *\n\t\t * @param nextLeft The location where the left of the item associated with pos\n\t\t *        should be drawn\n\t\t *\n\t\t * @return The view that is currently selected, if it happens to be in the\n\t\t *         range that we draw.\n\t\t */\n\t\tprivate View fillRight(int pos, int nextLeft) {\n\t\t\tif (DEBUG) Log.i(TAG, \"fillRight() pos: \" + pos + \" nextLeft: \" + nextLeft + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tView selectedView = null;\n\n\t\t\tfinal int end = (getRight() - getLeft()) - mListPadding.right;\n\n\t\t\twhile (nextLeft < end && pos < mItemCount) {\n\t\t\t\tView temp = makeColumn(pos, nextLeft, true);\n\t\t\t\tif (temp != null) {\n\t\t\t\t\tselectedView = temp;\n\t\t\t\t}\n\n\t\t\t\t// mReferenceView will change with each call to makeRow()\n\t\t\t\t// do not cache in a local variable outside of this loop\n\t\t\t\tnextLeft = mReferenceView.getRight() + mHorizontalSpacing;\n\n\t\t\t\tpos += mNumRows;\n\t\t\t}\n\n\t\t\treturn selectedView;\n\t\t}\n\n\t\tprivate View makeColumn(int startPos, int x, boolean flow) {\n\t\t\tif (DEBUG) Log.i(TAG, \"makeColumn() startPos: \" + startPos + \" x: \" + x + \" flow: \" + flow + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tfinal int rowHeight = mRowHeight;\n\t\t\tfinal int verticalSpacing = mVerticalSpacing;\n\n\t\t\tint last;\n\t\t\tint nextTop = mListPadding.top +\n\t\t\t((mStretchMode == STRETCH_SPACING_UNIFORM) ? verticalSpacing : 0);\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\tlast = Math.min(startPos + mNumRows, mItemCount);\n\t\t\t} else {\n\t\t\t\tlast = startPos + 1;\n\t\t\t\tstartPos = Math.max(0, startPos - mNumRows + 1);\n\n\t\t\t\tif (last - startPos < mNumRows) {\n\t\t\t\t\tnextTop += (mNumRows - (last - startPos)) * (rowHeight + verticalSpacing);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tView selectedView = null;\n\n\t\t\tfinal boolean hasFocus = shouldShowSelector();\n\t\t\tfinal boolean inClick = touchModeDrawsInPressedState();\n\t\t\tfinal int selectedPosition = mSelectedPosition;\n\n\t\t\tView child = null;\n\t\t\tfor (int pos = startPos; pos < last; pos++) {\n\t\t\t\t// is this the selected item?\n\t\t\t\tboolean selected = pos == selectedPosition;\n\t\t\t\t// does the list view have focus or contain focus\n\n\t\t\t\tfinal int where = flow ? -1 : pos - startPos;\n\t\t\t\tchild = makeAndAddView(pos, x, flow, nextTop, selected, where);\n\n\t\t\t\tnextTop += rowHeight;\n\t\t\t\tif (pos < last - 1) {\n\t\t\t\t\tnextTop += verticalSpacing;\n\t\t\t\t}\n\n\t\t\t\tif (selected && (hasFocus || inClick)) {\n\t\t\t\t\tselectedView = child;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tmReferenceView = child;\n\n\t\t\tif (selectedView != null) {\n\t\t\t\tmReferenceViewInSelectedRow = mReferenceView;\n\t\t\t}\n\n\t\t\treturn selectedView;\n\t\t}\n\n\n\t\t/**\n\t\t * Fills the list from pos to the left of the list view.\n\t\t *\n\t\t * @param pos The first position to put in the list\n\t\t *\n\t\t * @param nextRight The location where the right of the item associated\n\t\t *        with pos should be drawn\n\t\t *\n\t\t * @return The view that is currently selected\n\t\t */\n\t\tprivate View fillLeft(int pos, int nextRight) {\n\t\t\tif (DEBUG) Log.i(TAG, \"fillLeft() pos: \" + pos + \" nextRight: \" + nextRight + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tView selectedView = null;\n\n\t\t\tfinal int end = mListPadding.left;\n\n\t\t\twhile (nextRight > end && pos >= 0) {\n\n\t\t\t\tView temp = makeColumn(pos, nextRight, false);\n\t\t\t\tif (temp != null) {\n\t\t\t\t\tselectedView = temp;\n\t\t\t\t}\n\n\t\t\t\tnextRight = mReferenceView.getLeft() - mHorizontalSpacing;\n\n\t\t\t\tmFirstPosition = pos;\n\n\t\t\t\tpos -= mNumRows;\n\t\t\t}\n\n\t\t\tif (mStackFromBottom) {\n\t\t\t\tmFirstPosition = Math.max(0, pos + 1);\n\t\t\t}\n\n\t\t\treturn selectedView;\n\t\t}\n\n\t\t/**\n\t\t * Fills the list from left to right, starting with mFirstPosition\n\t\t *\n\t\t * @param nextLeft The location where the left of the first item should be\n\t\t *        drawn\n\t\t *\n\t\t * @return The view that is currently selected\n\t\t */\n\t\tprivate View fillFromTop(int nextLeft) {\n\t\t\tif (DEBUG) Log.i(TAG, \"fillFromTop() nextLeft: \" + nextLeft + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tmFirstPosition = Math.min(mFirstPosition, mSelectedPosition);\n\t\t\tmFirstPosition = Math.min(mFirstPosition, mItemCount - 1);\n\t\t\tif (mFirstPosition < 0) {\n\t\t\t\tmFirstPosition = 0;\n\t\t\t}\n\t\t\tmFirstPosition -= mFirstPosition % mNumRows;\n\t\t\treturn fillRight(mFirstPosition, nextLeft);\n\t\t}\n\n\t\tprivate View fillFromBottom(int lastPosition, int nextRight) {\n\t\t\tif (DEBUG) Log.i(TAG, \"fillFromBotom() lastPosition: \" + lastPosition + \" nextRight: \" + nextRight + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tlastPosition = Math.max(lastPosition, mSelectedPosition);\n\t\t\tlastPosition = Math.min(lastPosition, mItemCount - 1);\n\n\t\t\tfinal int invertedPosition = mItemCount - 1 - lastPosition;\n\t\t\tlastPosition = mItemCount - 1 - (invertedPosition - (invertedPosition % mNumRows));\n\n\t\t\treturn fillLeft(lastPosition, nextRight);\n\t\t}\n\n\t\tprivate View fillSelection(int childrenLeft, int childrenRight) {\n\t\t\tif (DEBUG) Log.i(TAG, \"fillSelection() childrenLeft: \" + childrenLeft + \" childrenRight: \" + childrenRight + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tfinal int selectedPosition = reconcileSelectedPosition();\n\t\t\tfinal int numRows = mNumRows;\n\t\t\tfinal int horizontalSpacing = mHorizontalSpacing;\n\n\t\t\tint columnStart;\n\t\t\tint columnEnd = -1;\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\tcolumnStart = selectedPosition - (selectedPosition % numRows);\n\t\t\t} else {\n\t\t\t\tfinal int invertedSelection = mItemCount - 1 - selectedPosition;\n\n\t\t\t\tcolumnEnd = mItemCount - 1 - (invertedSelection - (invertedSelection % numRows));\n\t\t\t\tcolumnStart = Math.max(0, columnEnd - numRows + 1);\n\t\t\t}\n\n\t\t\tfinal int fadingEdgeLength = getHorizontalFadingEdgeLength();\n\t\t\tfinal int leftSelectionPixel = getLeftSelectionPixel(childrenLeft, fadingEdgeLength, columnStart);\n\n\t\t\tfinal View sel = makeColumn(mStackFromBottom ? columnEnd : columnStart, leftSelectionPixel, true);\n\t\t\tmFirstPosition = columnStart;\n\n\t\t\tfinal View referenceView = mReferenceView;\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\tfillRight(columnStart + numRows, referenceView.getRight() + horizontalSpacing);\n\t\t\t\tpinToRight(childrenRight);\n\t\t\t\tfillLeft(columnStart - numRows, referenceView.getLeft() - horizontalSpacing);\n\t\t\t\tadjustViewsLeftOrRight();\n\t\t\t} else {\n\t\t\t\tfinal int rightSelectionPixel = getRightSelectionPixel(childrenRight,\n\t\t\t\t\t\tfadingEdgeLength, numRows, columnStart);\n\t\t\t\tfinal int offset = rightSelectionPixel - referenceView.getRight();\n\t\t\t\toffsetChildrenLeftAndRight(offset);\n\t\t\t\tfillLeft(columnStart - 1, referenceView.getLeft() - horizontalSpacing);\n\t\t\t\tpinToLeft(childrenLeft);\n\t\t\t\tfillRight(columnEnd + numRows, referenceView.getRight() + horizontalSpacing);\n\t\t\t\tadjustViewsLeftOrRight();\n\t\t\t}\n\n\t\t\treturn sel;\n\t\t}\n\n\n\t\t/**\n\t\t * Layout during a scroll that results from tracking motion events. Places\n\t\t * the mMotionPosition view at the offset specified by mMotionViewLeft, and\n\t\t * then build surrounding views from there.\n\t\t *\n\t\t * @param position the position at which to start filling\n\t\t * @param left the left of the view at that position\n\t\t * @return The selected view, or null if the selected view is outside the\n\t\t *         visible area.\n\t\t */\n\t\tprivate View fillSpecific(int position, int left) {\n\t\t\tif (DEBUG) Log.i(TAG, \"fillSpecific() position: \" + position + \" left: \" + left + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tfinal int numRows = mNumRows;\n\n\t\t\tint motionColumnStart;\n\t\t\tint motionColumnEnd = -1;\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\t//TODO don't understand what this is doing....\n\t\t\t\tmotionColumnStart = position - (position % numRows);\n\t\t\t} else {\n\t\t\t\t//TODO don't understand what this is doing....\n\t\t\t\tfinal int invertedSelection = mItemCount - 1 - position;\n\n\t\t\t\tmotionColumnEnd = mItemCount - 1 - (invertedSelection - (invertedSelection % numRows));\n\t\t\t\tmotionColumnStart = Math.max(0, motionColumnEnd - numRows + 1);\n\t\t\t}\n\n\t\t\tfinal View temp = makeColumn(mStackFromBottom ? motionColumnEnd : motionColumnStart, left, true);\n\n\t\t\t// Possibly changed again in fillUp if we add rows above this one.\n\t\t\tmFirstPosition = motionColumnStart;\n\n\t\t\tfinal View referenceView = mReferenceView;\n\t\t\t// We didn't have anything to layout, bail out\n\t\t\tif (referenceView == null) {\n\t\t\t\treturn null;\n\t\t\t}\n\n\t\t\tfinal int horizontalSpacing = mHorizontalSpacing;\n\n\t\t\tView leftOf;\n\t\t\tView rightOf;\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\tleftOf = fillLeft(motionColumnStart - numRows, referenceView.getLeft() - horizontalSpacing);\n\t\t\t\tadjustViewsLeftOrRight();\n\t\t\t\trightOf = fillRight(motionColumnStart + numRows, referenceView.getRight() + horizontalSpacing);\n\t\t\t\t// Check if we have dragged the bottom of the grid too high\n\t\t\t\tfinal int childCount = getChildCount();\n\t\t\t\tif (childCount > 0) {\n\t\t\t\t\tcorrectTooLeft(numRows, horizontalSpacing, childCount);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trightOf = fillRight(motionColumnEnd + numRows, referenceView.getRight() + horizontalSpacing);\n\t\t\t\tadjustViewsLeftOrRight();\n\t\t\t\tleftOf = fillLeft(motionColumnStart - 1, referenceView.getLeft() - horizontalSpacing);\n\t\t\t\t// Check if we have dragged the right of the grid too high\n\t\t\t\tfinal int childCount = getChildCount();\n\t\t\t\tif (childCount > 0) {\n\t\t\t\t\tcorrectTooRight(numRows, horizontalSpacing, childCount);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (temp != null) {\n\t\t\t\treturn temp;\n\t\t\t} else if (leftOf != null) {\n\t\t\t\treturn leftOf;\n\t\t\t} else {\n\t\t\t\treturn rightOf;\n\t\t\t}\n\t\t}\n\n\n\t\tprivate void correctTooLeft(int numRows, int horizontalSpacing, int childCount) {\n\t\t\tif (DEBUG) Log.i(TAG, \"correctTooLeft() numRows: \" + numRows + \" horizontalSpacing: \" + horizontalSpacing + \" mFirstPosition: \" + mFirstPosition);\n\t\t\t// First see if the last item is visible\n\t\t\tfinal int lastPosition = mFirstPosition + childCount - 1;\n\t\t\tif (lastPosition == mItemCount - 1 && childCount > 0) {\n\t\t\t\t// Get the last child ...\n\t\t\t\tfinal View lastChild = getChildAt(childCount - 1);\n\n\t\t\t\t// ... and its right edge\n\t\t\t\tfinal int lastRight = lastChild.getRight();\n\t\t\t\t// This is end of our drawable area\n\t\t\t\tfinal int end = (getRight() - getLeft()) - mListPadding.right;\n\n\t\t\t\t// This is how far the right edge of the last view is from the right\n\t\t\t\t// edge of the drawable area\n\t\t\t\tint rightOffset = end - lastRight;\n\n\t\t\t\tfinal View firstChild = getChildAt(0);\n\t\t\t\tfinal int firstLeft = firstChild.getLeft();\n\n\t\t\t\t// Make sure we are 1) Too Left, and 2) Either there are more columns to left of\n\t\t\t\t// the first column or the first column is scrolled off the top of the drawable area\n\t\t\t\tif (rightOffset > 0 && (mFirstPosition > 0 || firstLeft < mListPadding.left))  {\n\t\t\t\t\tif (mFirstPosition == 0) {\n\t\t\t\t\t\t// Don't pull the left too far right\n\t\t\t\t\t\trightOffset = Math.min(rightOffset, mListPadding.left - firstLeft);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Move everything right\n\t\t\t\t\toffsetChildrenLeftAndRight(rightOffset);\n\t\t\t\t\tif (mFirstPosition > 0) {\n\t\t\t\t\t\t// Fill the gap that was opened above mFirstPosition with more columns, if\n\t\t\t\t\t\t// possible\n\t\t\t\t\t\tfillLeft(mFirstPosition - (mStackFromBottom ? 1 : numRows),\n\t\t\t\t\t\t\t\tfirstChild.getLeft() - horizontalSpacing);\n\t\t\t\t\t\t// Close up the remaining gap\n\t\t\t\t\t\tadjustViewsLeftOrRight();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void correctTooRight(int numRows, int horizontalSpacing, int childCount) {\n\t\t\tif (DEBUG) Log.i(TAG, \"correctTooRight() numRows: \" + numRows + \" horizontalSpacing: \" + horizontalSpacing + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tif (mFirstPosition == 0 && childCount > 0) {\n\t\t\t\t// Get the first child ...\n\t\t\t\tfinal View firstChild = getChildAt(0);\n\n\t\t\t\t// ... and its left edge\n\t\t\t\tfinal int firstLeft = firstChild.getLeft();\n\n\t\t\t\t// This is left of our drawable area\n\t\t\t\tfinal int start = mListPadding.left;\n\n\t\t\t\t// This is right of our drawable area\n\t\t\t\tfinal int end = (getRight() - getLeft()) - mListPadding.right;\n\n\t\t\t\t// This is how far the left edge of the first view is from the left of the\n\t\t\t\t// drawable area\n\t\t\t\tint leftOffset = firstLeft - start;\n\t\t\t\tfinal View lastChild = getChildAt(childCount - 1);\n\t\t\t\tfinal int lastRight = lastChild.getRight();\n\t\t\t\tfinal int lastPosition = mFirstPosition + childCount - 1;\n\n\t\t\t\t// Make sure we are 1) Too right, and 2) Either there are more columns to right of the\n\t\t\t\t// last column or the last column is scrolled off the right of the drawable area\n\t\t\t\tif (leftOffset > 0 && (lastPosition < mItemCount - 1 || lastRight > end))  {\n\t\t\t\t\tif (lastPosition == mItemCount - 1 ) {\n\t\t\t\t\t\t// Don't pull the right too far left\n\t\t\t\t\t\tleftOffset = Math.min(leftOffset, lastRight - end);\n\t\t\t\t\t}\n\n\t\t\t\t\t// Move everything left\n\t\t\t\t\toffsetChildrenLeftAndRight(-leftOffset);\n\t\t\t\t\tif (lastPosition < mItemCount - 1) {\n\t\t\t\t\t\t// Fill the gap that was opened to right of the last position with\n\t\t\t\t\t\t// more columns, if possible\n\t\t\t\t\t\tfillRight(lastPosition + (!mStackFromBottom ? 1 : numRows),\n\t\t\t\t\t\t\t\tlastChild.getRight() + horizontalSpacing);\n\t\t\t\t\t\t// Close up the remaining gap\n\t\t\t\t\t\tadjustViewsLeftOrRight();\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\n\t\t/**\n\t\t * Fills the grid based on positioning the new selection relative to the old\n\t\t * selection. The new selection will be placed at, to left of, or to right of the\n\t\t * location of the new selection depending on how the selection is moving.\n\t\t * The selection will then be pinned to the visible part of the screen,\n\t\t * excluding the edges that are faded. The grid is then filled leftwards and\n\t\t * rightwards from there.\n\t\t *\n\t\t * @param delta Which way we are moving\n\t\t * @param childrenLeft Where to start drawing children\n\t\t * @param childrenRight Last pixel where children can be drawn\n\t\t * @return The view that currently has selection\n\t\t */\n\t\tprivate View moveSelection(int delta, int childrenLeft, int childrenRight) {\n\t\t\tif (DEBUG) Log.i(TAG, \"moveSelection() delta: \" + delta + \" childrenLeft: \" + childrenLeft + \" childrenRight: \" + childrenRight + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tfinal int fadingEdgeLength = getHorizontalFadingEdgeLength();\n\t\t\tfinal int selectedPosition = mSelectedPosition;\n\t\t\tfinal int numRows = mNumRows;\n\t\t\tfinal int horizontalSpacing = mHorizontalSpacing;\n\n\t\t\tint oldColumnStart;\n\t\t\tint columnStart;\n\t\t\tint columnEnd = -1;\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\toldColumnStart = (selectedPosition - delta) - ((selectedPosition - delta) % numRows);\n\n\t\t\t\tcolumnStart = selectedPosition - (selectedPosition % numRows);\n\t\t\t} else {\n\t\t\t\tint invertedSelection = mItemCount - 1 - selectedPosition;\n\n\t\t\t\tcolumnEnd = mItemCount - 1 - (invertedSelection - (invertedSelection % numRows));\n\t\t\t\tcolumnStart = Math.max(0, columnEnd - numRows + 1);\n\n\t\t\t\tinvertedSelection = mItemCount - 1 - (selectedPosition - delta);\n\t\t\t\toldColumnStart = mItemCount - 1 - (invertedSelection - (invertedSelection % numRows));\n\t\t\t\toldColumnStart = Math.max(0, oldColumnStart - numRows + 1);\n\t\t\t}\n\n\t\t\tfinal int rowDelta = columnStart - oldColumnStart;\n\n\t\t\tfinal int leftSelectionPixel = getLeftSelectionPixel(childrenLeft, fadingEdgeLength, columnStart);\n\t\t\tfinal int rightSelectionPixel = getRightSelectionPixel(childrenRight, fadingEdgeLength,\n\t\t\t\t\tnumRows, columnStart);\n\n\t\t\t// Possibly changed again in fillLeft if we add rows above this one.\n\t\t\tmFirstPosition = columnStart;\n\n\t\t\tView sel;\n\t\t\tView referenceView;\n\n\t\t\tif (rowDelta > 0) {\n\t\t\t\t/*\n\t\t\t\t * Case 1: Scrolling right.\n\t\t\t\t */\n\n\t\t\t\tfinal int oldRight = mReferenceViewInSelectedRow == null ? 0 :\n\t\t\t\t\tmReferenceViewInSelectedRow.getRight();\n\n\t\t\t\tsel = makeColumn(mStackFromBottom ? columnEnd : columnStart, oldRight + horizontalSpacing, true);\n\t\t\t\treferenceView = mReferenceView;\n\n\t\t\t\tadjustForRightFadingEdge(referenceView, leftSelectionPixel, rightSelectionPixel);\n\t\t\t} else if (rowDelta < 0) {\n\t\t\t\t/*\n\t\t\t\t * Case 2: Scrolling left.\n\t\t\t\t */\n\t\t\t\tfinal int oldTop = mReferenceViewInSelectedRow == null ?\n\t\t\t\t\t\t0 : mReferenceViewInSelectedRow.getLeft();\n\n\t\t\t\tsel = makeColumn(mStackFromBottom ? columnEnd : columnStart, oldTop - horizontalSpacing, false);\n\t\t\t\treferenceView = mReferenceView;\n\n\t\t\t\tadjustForLeftFadingEdge(referenceView, leftSelectionPixel, rightSelectionPixel);\n\t\t\t} else {\n\t\t\t\t/*\n\t\t\t\t * Keep selection where it was\n\t\t\t\t */\n\t\t\t\tfinal int oldTop = mReferenceViewInSelectedRow == null ?\n\t\t\t\t\t\t0 : mReferenceViewInSelectedRow.getLeft();\n\n\t\t\t\tsel = makeColumn(mStackFromBottom ? columnEnd : columnStart, oldTop, true);\n\t\t\t\treferenceView = mReferenceView;\n\t\t\t}\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\tfillLeft(columnStart - numRows, referenceView.getLeft() - horizontalSpacing);\n\t\t\t\tadjustViewsLeftOrRight();\n\t\t\t\tfillRight(columnStart + numRows, referenceView.getRight() + horizontalSpacing);\n\t\t\t} else {\n\t\t\t\tfillRight(columnStart + numRows, referenceView.getRight() + horizontalSpacing);\n\t\t\t\tadjustViewsLeftOrRight();\n\t\t\t\tfillLeft(columnStart - 1, referenceView.getLeft() - horizontalSpacing);\n\t\t\t}\n\n\t\t\treturn sel;\n\t\t}\n\n\n\t\t@Override\n\t\tprotected void layoutChildren() {\n\t\t\tfinal int childrenLeft = mListPadding.left;\n\t\t\tfinal int childrenRight = getRight() - getLeft() - mListPadding.right;\n\n\t\t\tint childCount = getChildCount();\n\t\t\tint index;\n\t\t\tint delta = 0;\n\n\t\t\tView sel;\n\t\t\tView oldSel = null;\n\t\t\tView oldFirst = null;\n\t\t\tView newSel = null;\n\n\t\t\t// Remember stuff we will need down below\n\t\t\tswitch (mLayoutMode) {\n\t\t\tcase LAYOUT_SET_SELECTION:\n\t\t\t\tindex = mNextSelectedPosition - mFirstPosition;\n\t\t\t\tif (index >= 0 && index < childCount) {\n\t\t\t\t\tnewSel = getChildAt(index);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase LAYOUT_FORCE_TOP:\n\t\t\tcase LAYOUT_FORCE_BOTTOM:\n\t\t\tcase LAYOUT_SPECIFIC:\n\t\t\tcase LAYOUT_SYNC:\n\t\t\t\tbreak;\n\t\t\tcase LAYOUT_MOVE_SELECTION:\n\t\t\t\tif (mNextSelectedPosition >= 0) {\n\t\t\t\t\tdelta = mNextSelectedPosition - mSelectedPosition;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\t// Remember the previously selected view\n\t\t\t\tindex = mSelectedPosition - mFirstPosition;\n\t\t\t\tif (index >= 0 && index < childCount) {\n\t\t\t\t\toldSel = getChildAt(index);\n\t\t\t\t}\n\n\t\t\t\t// Remember the previous first child\n\t\t\t\toldFirst = getChildAt(0);\n\t\t\t}\n\n\t\t\tboolean dataChanged = mDataChanged;\n\t\t\tif (dataChanged) {\n\t\t\t\thandleDataChanged();\n\t\t\t}\n\n\t\t\t// Handle the empty set by removing all views that are visible\n\t\t\t// and calling it a day\n\t\t\tif (mItemCount == 0) {\n\t\t\t\tresetList();\n\t\t\t\tinvokeOnItemScrollListener();\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tsetSelectedPositionInt(mNextSelectedPosition);\n\n\t\t\t// Pull all children into the RecycleBin.\n\t\t\t// These views will be reused if possible\n\t\t\tfinal int firstPosition = mFirstPosition;\n\t\t\tfinal RecycleBin recycleBin = mRecycler;\n\n\t\t\tif (dataChanged) {\n\t\t\t\tfor (int i = 0; i < childCount; i++) {\n\t\t\t\t\trecycleBin.addScrapView(getChildAt(i));\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\trecycleBin.fillActiveViews(childCount, firstPosition);\n\t\t\t}\n\n\t\t\t// Clear out old views\n\t\t\t//removeAllViewsInLayout();\n\t\t\tdetachAllViewsFromParent();\n\n\t\t\tswitch (mLayoutMode) {\n\t\t\tcase LAYOUT_SET_SELECTION:\n\t\t\t\tif (newSel != null) {\n\t\t\t\t\tsel = fillFromSelection(newSel.getLeft(), childrenLeft, childrenRight);\n\t\t\t\t} else {\n\t\t\t\t\tsel = fillSelection(childrenLeft, childrenRight);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase LAYOUT_FORCE_TOP:\n\t\t\t\tmFirstPosition = 0;\n\t\t\t\tsel = fillFromTop(childrenLeft);\n\t\t\t\tadjustViewsLeftOrRight();\n\t\t\t\tbreak;\n\t\t\tcase LAYOUT_FORCE_BOTTOM:\n\t\t\t\tsel = fillRight(mItemCount - 1, childrenRight);\n\t\t\t\tadjustViewsLeftOrRight();\n\t\t\t\tbreak;\n\t\t\tcase LAYOUT_SPECIFIC:\n\t\t\t\tsel = fillSpecific(mSelectedPosition, mSpecificTop);\n\t\t\t\tbreak;\n\t\t\tcase LAYOUT_SYNC:\n\t\t\t\tsel = fillSpecific(mSyncPosition, mSpecificTop);\n\t\t\t\tbreak;\n\t\t\tcase LAYOUT_MOVE_SELECTION:\n\t\t\t\t// Move the selection relative to its old position\n\t\t\t\tsel = moveSelection(delta, childrenLeft, childrenRight);\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tif (childCount == 0) {\n\t\t\t\t\tif (!mStackFromBottom) {\n\t\t\t\t\t\tsetSelectedPositionInt(mAdapter == null || isInTouchMode() ?\n\t\t\t\t\t\t\t\tINVALID_POSITION : 0);\n\t\t\t\t\t\tsel = fillFromTop(childrenLeft);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tfinal int last = mItemCount - 1;\n\t\t\t\t\t\tsetSelectedPositionInt(mAdapter == null || isInTouchMode() ?\n\t\t\t\t\t\t\t\tINVALID_POSITION : last);\n\t\t\t\t\t\tsel = fillFromBottom(last, childrenRight);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (mSelectedPosition >= 0 && mSelectedPosition < mItemCount) {\n\t\t\t\t\t\tsel = fillSpecific(mSelectedPosition, oldSel == null ?\n\t\t\t\t\t\t\t\tchildrenLeft : oldSel.getLeft());\n\t\t\t\t\t} else if (mFirstPosition < mItemCount)  {\n\t\t\t\t\t\tsel = fillSpecific(mFirstPosition, oldFirst == null ?\n\t\t\t\t\t\t\t\tchildrenLeft : oldFirst.getLeft());\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsel = fillSpecific(0, childrenLeft);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\t// Flush any cached views that did not get reused above\n\t\t\trecycleBin.scrapActiveViews();\n\n\t\t\tif (sel != null) {\n\t\t\t\tpositionSelector(sel);\n\t\t\t\tmSelectedTop = sel.getLeft();\n\t\t\t} else if (mTouchMode > TOUCH_MODE_DOWN && mTouchMode < TOUCH_MODE_SCROLL) {\n\t\t\t\tView child = getChildAt(mMotionPosition - mFirstPosition);\n\t\t\t\tif (child != null) positionSelector(child);\n\t\t\t} else {\n\t\t\t\tmSelectedTop = 0;\n\t\t\t\tmSelectorRect.setEmpty();\n\t\t\t}\n\n\t\t\tmLayoutMode = LAYOUT_NORMAL;\n\t\t\tmDataChanged = false;\n\t\t\tmNeedSync = false;\n\t\t\tsetNextSelectedPositionInt(mSelectedPosition);\n\n\t\t\tupdateScrollIndicators();\n\n\t\t\tif (mItemCount > 0) {\n\t\t\t\tcheckSelectionChanged();\n\t\t\t}\n\n\t\t\tinvokeOnItemScrollListener();\n\t\t}\n\n\n\n\t\t@Override\n\t\tprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {\n\t\t\tint widthMode = MeasureSpec.getMode(widthMeasureSpec);\n\t\t\tint heightMode = MeasureSpec.getMode(heightMeasureSpec);\n\t\t\tint widthSize = MeasureSpec.getSize(widthMeasureSpec);\n\t\t\tint heightSize = MeasureSpec.getSize(heightMeasureSpec);\n\t\t\tif (DEBUG) Log.i(TAG, \"horizontal onMeasure heightMode: \" + heightMode);\n\t\t\tif (heightMode == MeasureSpec.UNSPECIFIED) {\n\t\t\t\tif (mRowHeight > 0) {\n\t\t\t\t\theightSize = mRowHeight + mListPadding.top + mListPadding.bottom;\n\t\t\t\t} else {\n\t\t\t\t\theightSize = mListPadding.top + mListPadding.bottom;\n\t\t\t\t}\n\t\t\t\theightSize += getHorizontalScrollbarHeight();\n\t\t\t}\n\n\t\t\tint childHeight = heightSize - mListPadding.top - mListPadding.bottom;\n\t\t\tdetermineRows(childHeight);\n\n\t\t\tint childWidth = 0;\n\n\t\t\tmItemCount = mAdapter == null ? 0 : mAdapter.getCount();\n\t\t\tfinal int count = mItemCount;\n\t\t\tif (count > 0) {\n\t\t\t\tfinal View child = obtainView(0, mIsScrap);\n\n\t\t\t\tTwoWayAbsListView.LayoutParams p = (TwoWayAbsListView.LayoutParams)child.getLayoutParams();\n\t\t\t\tif (p == null) {\n\t\t\t\t\tp = new TwoWayAbsListView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,\n\t\t\t\t\t\t\tViewGroup.LayoutParams.FILL_PARENT, 0);\n\t\t\t\t\tchild.setLayoutParams(p);\n\t\t\t\t}\n\t\t\t\tp.viewType = mAdapter.getItemViewType(0);\n\t\t\t\tp.forceAdd = true;\n\n\t\t\t\tint childHeightSpec = getChildMeasureSpec(\n\t\t\t\t\t\tMeasureSpec.makeMeasureSpec(mRowHeight, MeasureSpec.UNSPECIFIED), 0, p.height);\n\t\t\t\tint childWidthSpec = getChildMeasureSpec(\n\t\t\t\t\t\tMeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY), 0, p.width);\n\t\t\t\tchild.measure(childWidthSpec, childHeightSpec);\n\n\t\t\t\tchildWidth = child.getMeasuredWidth();\n\n\t\t\t\tif (mRecycler.shouldRecycleViewType(p.viewType)) {\n\t\t\t\t\tmRecycler.addScrapView(child);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (widthMode == MeasureSpec.UNSPECIFIED) {\n\t\t\t\twidthSize = mListPadding.left + mListPadding.right + childWidth +\n\t\t\t\tgetHorizontalFadingEdgeLength() * 2;\n\t\t\t}\n\n\t\t\tif (widthMode == MeasureSpec.AT_MOST) {\n\t\t\t\tint ourSize =  mListPadding.left + mListPadding.right;\n\n\t\t\t\tfinal int numRows = mNumRows;\n\t\t\t\tfor (int i = 0; i < count; i += numRows) {\n\t\t\t\t\tourSize += childWidth;\n\t\t\t\t\tif (i + numRows < count) {\n\t\t\t\t\t\tourSize += mHorizontalSpacing;\n\t\t\t\t\t}\n\t\t\t\t\tif (ourSize >= widthSize) {\n\t\t\t\t\t\tourSize = widthSize;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\twidthSize = ourSize;\n\t\t\t}\n\n\t\t\tsetMeasuredDimension(widthSize, heightSize);\n\t\t\tmWidthMeasureSpec = widthMeasureSpec;\n\t\t\tif (DEBUG) Log.i(TAG, \"Horizontal onMeasure widthSize: \" + widthSize + \" heightSize: \" + heightSize);\n\t\t}\n\n\n\t\tprivate void determineRows(int availableSpace) {\n\t\t\tfinal int requestedVerticalSpacing = mRequestedVerticalSpacing;\n\t\t\tfinal int stretchMode = mStretchMode;\n\t\t\tfinal int requestedRowHeight = mRequestedRowHeight;\n\t\t\tmHorizontalSpacing = mRequestedHorizontalSpacing;\n\n\t\t\tif (mRequestedNumRows == AUTO_FIT) {\n\t\t\t\tif (requestedRowHeight > 0) {\n\t\t\t\t\t// Client told us to pick the number of rows\n\t\t\t\t\tmNumRows = (availableSpace + mRequestedVerticalSpacing) /\n\t\t\t\t\t(requestedRowHeight + mRequestedVerticalSpacing);\n\t\t\t\t} else {\n\t\t\t\t\t// Just make up a number if we don't have enough info\n\t\t\t\t\tmNumRows = 2;\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// We picked the rows\n\t\t\t\tmNumRows = mRequestedNumRows;\n\t\t\t}\n\n\t\t\tif (mNumRows <= 0) {\n\t\t\t\tmNumRows = 1;\n\t\t\t}\n\n\t\t\tswitch (stretchMode) {\n\t\t\tcase NO_STRETCH:\n\t\t\t\t// Nobody stretches\n\t\t\t\tmRowHeight = mRequestedRowHeight;\n\t\t\t\tmVerticalSpacing = mRequestedVerticalSpacing;\n\t\t\t\tbreak;\n\n\t\t\tdefault:\n\t\t\t\tint spaceLeftOver = 0;\n\t\t\t\tswitch (stretchMode) {\n\t\t\t\tcase STRETCH_COLUMN_WIDTH:\n\t\t\t\t\tspaceLeftOver = availableSpace - (mNumRows * requestedRowHeight) -\n\t\t\t\t\t((mNumRows - 1) * requestedVerticalSpacing);\n\t\t\t\t\t// Stretch the rows\n\t\t\t\t\tmRowHeight = requestedRowHeight + spaceLeftOver / mNumRows;\n\t\t\t\t\tmVerticalSpacing = requestedVerticalSpacing;\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase STRETCH_SPACING:\n\t\t\t\t\tspaceLeftOver = availableSpace - (mNumRows * requestedRowHeight) -\n\t\t\t\t\t((mNumRows - 1) * requestedVerticalSpacing);\n\t\t\t\t\t// Stretch the spacing between rows\n\t\t\t\t\tmRowHeight = requestedRowHeight;\n\t\t\t\t\tif (mNumRows > 1) {\n\t\t\t\t\t\tmVerticalSpacing = requestedVerticalSpacing +\n\t\t\t\t\t\tspaceLeftOver / (mNumRows - 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmVerticalSpacing = requestedVerticalSpacing + spaceLeftOver;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\n\t\t\t\tcase STRETCH_SPACING_UNIFORM:\n\t\t\t\t\t// Stretch the spacing between rows\n\t\t\t\t\tspaceLeftOver = availableSpace - (mNumRows * requestedRowHeight) -\n\t\t\t\t\t((mNumRows + 1) * requestedVerticalSpacing);\n\t\t\t\t\tmRowHeight = requestedRowHeight;\n\t\t\t\t\tif (mNumRows > 1) {\n\t\t\t\t\t\tmVerticalSpacing = requestedVerticalSpacing + spaceLeftOver / (mNumRows + 1);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmVerticalSpacing = ((requestedVerticalSpacing * 2) + spaceLeftOver) / 2;\n\t\t\t\t\t}\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tif (DEBUG) Log.i(TAG, \"determineRows() mRowHeight: \" + mRowHeight + \" mVerticalSpacing: \" + mVerticalSpacing + \" mStretchMode: \" + mStretchMode);\n\t\t}\n\n\t\t/**\n\t\t * Fills the grid based on positioning the new selection at a specific\n\t\t * location. The selection may be moved so that it does not intersect the\n\t\t * faded edges. The grid is then filled upwards and downwards from there.\n\t\t *\n\t\t * @param selectedLeft Where the selected item should be\n\t\t * @param childrenLeft Where to start drawing children\n\t\t * @param childrenRight Last pixel where children can be drawn\n\t\t * @return The view that currently has selection\n\t\t */\n\t\tprivate View fillFromSelection(int selectedLeft, int childrenLeft, int childrenRight) {\n\t\t\tif (DEBUG) Log.i(TAG, \"fillFromSelection() selectedLeft: \" + selectedLeft + \" childrenLeft: \" + childrenLeft + \" childrenRight: \" + childrenRight + \" mFirstPosition: \" + mFirstPosition);\n\t\t\tfinal int fadingEdgeLength = getHorizontalFadingEdgeLength();\n\t\t\tfinal int selectedPosition = mSelectedPosition;\n\t\t\tfinal int numRows = mNumRows;\n\t\t\tfinal int horizontalSpacing = mHorizontalSpacing;\n\n\t\t\tint columnStart;\n\t\t\tint columnEnd = -1;\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\tcolumnStart = selectedPosition - (selectedPosition % numRows);\n\t\t\t} else {\n\t\t\t\tint invertedSelection = mItemCount - 1 - selectedPosition;\n\n\t\t\t\tcolumnEnd = mItemCount - 1 - (invertedSelection - (invertedSelection % numRows));\n\t\t\t\tcolumnStart = Math.max(0, columnEnd - numRows + 1);\n\t\t\t}\n\n\t\t\tView sel;\n\t\t\tView referenceView;\n\n\t\t\tint leftSelectionPixel = getLeftSelectionPixel(childrenLeft, fadingEdgeLength, columnStart);\n\t\t\tint rightSelectionPixel = getRightSelectionPixel(childrenRight, fadingEdgeLength,\n\t\t\t\t\tnumRows, columnStart);\n\n\t\t\tsel = makeColumn(mStackFromBottom ? columnEnd : columnStart, selectedLeft, true);\n\t\t\t// Possibly changed again in fillLeft if we add rows above this one.\n\t\t\tmFirstPosition = columnStart;\n\n\t\t\treferenceView = mReferenceView;\n\t\t\tadjustForLeftFadingEdge(referenceView, leftSelectionPixel, rightSelectionPixel);\n\t\t\tadjustForRightFadingEdge(referenceView, leftSelectionPixel, rightSelectionPixel);\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\tfillLeft(columnStart - numRows, referenceView.getLeft() - horizontalSpacing);\n\t\t\t\tadjustViewsLeftOrRight();\n\t\t\t\tfillRight(columnStart + numRows, referenceView.getRight() + horizontalSpacing);\n\t\t\t} else {\n\t\t\t\tfillRight(columnEnd + numRows, referenceView.getRight() + horizontalSpacing);\n\t\t\t\tadjustViewsLeftOrRight();\n\t\t\t\tfillLeft(columnStart - 1, referenceView.getLeft() - horizontalSpacing);\n\t\t\t}\n\n\n\t\t\treturn sel;\n\t\t}\n\n\t\t/**\n\t\t * Calculate the right-most pixel we can draw the selection into\n\t\t *\n\t\t * @param childrenRight Right pixel were children can be drawn\n\t\t * @param fadingEdgeLength Length of the fading edge in pixels, if present\n\t\t * @param numColumns Number of columns in the grid\n\t\t * @param rowStart The start of the row that will contain the selection\n\t\t * @return The right-most pixel we can draw the selection into\n\t\t */\n\t\tprivate int getRightSelectionPixel(int childrenRight, int fadingEdgeLength,\n\t\t\t\tint numColumns, int rowStart) {\n\t\t\t// Last pixel we can draw the selection into\n\t\t\tint rightSelectionPixel = childrenRight;\n\t\t\tif (rowStart + numColumns - 1 < mItemCount - 1) {\n\t\t\t\trightSelectionPixel -= fadingEdgeLength;\n\t\t\t}\n\t\t\treturn rightSelectionPixel;\n\t\t}\n\n\t\t/**\n\t\t * Calculate the left-most pixel we can draw the selection into\n\t\t *\n\t\t * @param childrenLeft Left pixel were children can be drawn\n\t\t * @param fadingEdgeLength Length of the fading edge in pixels, if present\n\t\t * @param rowStart The start of the row that will contain the selection\n\t\t * @return The left-most pixel we can draw the selection into\n\t\t */\n\t\tprivate int getLeftSelectionPixel(int childrenLeft, int fadingEdgeLength, int rowStart) {\n\t\t\t// first pixel we can draw the selection into\n\t\t\tint leftSelectionPixel = childrenLeft;\n\t\t\tif (rowStart > 0) {\n\t\t\t\tleftSelectionPixel += fadingEdgeLength;\n\t\t\t}\n\t\t\treturn leftSelectionPixel;\n\t\t}\n\n\n\n\t\t/**\n\t\t * Move all views left so the selected row does not interesect the right\n\t\t * fading edge (if necessary).\n\t\t *\n\t\t * @param childInSelectedRow A child in the row that contains the selection\n\t\t * @param leftSelectionPixel The leftmost pixel we can draw the selection into\n\t\t * @param rightSelectionPixel The rightmost pixel we can draw the\n\t\t *        selection into\n\t\t */\n\t\tprivate void adjustForRightFadingEdge(View childInSelectedRow,\n\t\t\t\tint leftSelectionPixel, int rightSelectionPixel) {\n\t\t\t// Some of the newly selected item extends below the bottom of the\n\t\t\t// list\n\t\t\tif (childInSelectedRow.getRight() > rightSelectionPixel) {\n\n\t\t\t\t// Find space available to the left the selection into which we can\n\t\t\t\t// scroll upwards\n\t\t\t\tint spaceToLeft = childInSelectedRow.getLeft() - leftSelectionPixel;\n\n\t\t\t\t// Find space required to bring the right of the selected item\n\t\t\t\t// fully into view\n\t\t\t\tint spaceToRight = childInSelectedRow.getRight() - rightSelectionPixel;\n\t\t\t\tint offset = Math.min(spaceToLeft, spaceToRight);\n\n\t\t\t\t// Now offset the selected item to get it into view\n\t\t\t\toffsetChildrenLeftAndRight(-offset);\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Move all views right so the selected row does not interesect the left\n\t\t * fading edge (if necessary).\n\t\t *\n\t\t * @param childInSelectedRow A child in the row that contains the selection\n\t\t * @param leftSelectionPixel The leftmost pixel we can draw the selection into\n\t\t * @param rightSelectionPixel The rightmost pixel we can draw the\n\t\t *        selection into\n\t\t */\n\t\tprivate void adjustForLeftFadingEdge(View childInSelectedRow,\n\t\t\t\tint leftSelectionPixel, int rightSelectionPixel) {\n\t\t\t// Some of the newly selected item extends above the top of the list\n\t\t\tif (childInSelectedRow.getLeft() < leftSelectionPixel) {\n\t\t\t\t// Find space required to bring the top of the selected item\n\t\t\t\t// fully into view\n\t\t\t\tint spaceToLeft = leftSelectionPixel - childInSelectedRow.getLeft();\n\n\t\t\t\t// Find space available below the selection into which we can\n\t\t\t\t// scroll downwards\n\t\t\t\tint spaceToRight = rightSelectionPixel - childInSelectedRow.getRight();\n\t\t\t\tint offset = Math.min(spaceToLeft, spaceToRight);\n\n\t\t\t\t// Now offset the selected item to get it into view\n\t\t\t\toffsetChildrenLeftAndRight(offset);\n\t\t\t}\n\t\t}\n\n\n\t\t/**\n\t\t * Make sure views are touching the top or bottom edge, as appropriate for\n\t\t * our gravity\n\t\t */\n\t\tprivate void adjustViewsLeftOrRight() {\n\t\t\tfinal int childCount = getChildCount();\n\n\t\t\tif (childCount > 0) {\n\t\t\t\tint delta;\n\t\t\t\tView child;\n\n\t\t\t\tif (!mStackFromBottom) {\n\t\t\t\t\t// Uh-oh -- we came up short. Slide all views left to make them\n\t\t\t\t\t// align with the left\n\t\t\t\t\tchild = getChildAt(0);\n\t\t\t\t\tdelta = child.getLeft() - mListPadding.left;\n\t\t\t\t\tif (mFirstPosition != 0) {\n\t\t\t\t\t\t// It's OK to have some space to left the first item if it is\n\t\t\t\t\t\t// part of the horizontal spacing\n\t\t\t\t\t\tdelta -= mHorizontalSpacing;\n\t\t\t\t\t}\n\t\t\t\t\tif (delta < 0) {\n\t\t\t\t\t\t// We only are looking to see if we are too right, not too left\n\t\t\t\t\t\tdelta = 0;\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// we are too left, slide all views right to align with right\n\t\t\t\t\tchild = getChildAt(childCount - 1);\n\t\t\t\t\tdelta = child.getRight() - (getWidth() - mListPadding.right);\n\n\t\t\t\t\tif (mFirstPosition + childCount < mItemCount) {\n\t\t\t\t\t\t// It's OK to have some space to right of the last item if it is\n\t\t\t\t\t\t// part of the horizontal spacing\n\t\t\t\t\t\tdelta += mHorizontalSpacing;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (delta > 0) {\n\t\t\t\t\t\t// We only are looking to see if we are too left, not too right\n\t\t\t\t\t\tdelta = 0;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (delta != 0) {\n\t\t\t\t\toffsetChildrenLeftAndRight(-delta);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\n\t\t/**\n\t\t * Add a view as a child and make sure it is measured (if necessary) and\n\t\t * positioned properly.\n\t\t *\n\t\t * @param child The view to add\n\t\t * @param position The position of the view\n\t\t * @param x The x position relative to which this view will be positioned\n\t\t * @param flow if true, align left edge to x. If false, align right edge\n\t\t *        to x.\n\t\t * @param childrenTop Top edge where children should be positioned\n\t\t * @param selected Is this position selected?\n\t\t * @param recycled Has this view been pulled from the recycle bin? If so it\n\t\t *        does not need to be remeasured.\n\t\t * @param where Where to add the item in the list\n\t\t *\n\t\t */\n\t\tprivate void setupChild(View child, int position, int x, boolean flow, int childrenTop,\n\t\t\t\tboolean selected, boolean recycled, int where) {\n\t\t\tboolean isSelected = selected && shouldShowSelector();\n\t\t\tfinal boolean updateChildSelected = isSelected != child.isSelected();\n\t\t\tfinal int mode = mTouchMode;\n\t\t\tfinal boolean isPressed = mode > TOUCH_MODE_DOWN && mode < TOUCH_MODE_SCROLL &&\n\t\t\tmMotionPosition == position;\n\t\t\tfinal boolean updateChildPressed = isPressed != child.isPressed();\n\n\t\t\tboolean needToMeasure = !recycled || updateChildSelected || child.isLayoutRequested();\n\n\t\t\t// Respect layout params that are already in the view. Otherwise make\n\t\t\t// some up...\n\t\t\tTwoWayAbsListView.LayoutParams p = (TwoWayAbsListView.LayoutParams)child.getLayoutParams();\n\t\t\tif (p == null) {\n\t\t\t\tp = new TwoWayAbsListView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,\n\t\t\t\t\t\tViewGroup.LayoutParams.FILL_PARENT, 0);\n\t\t\t}\n\t\t\tp.viewType = mAdapter.getItemViewType(position);\n\n\t\t\tif (recycled && !p.forceAdd) {\n\t\t\t\tattachViewToParent(child, where, p);\n\t\t\t} else {\n\t\t\t\tp.forceAdd = false;\n\t\t\t\taddViewInLayout(child, where, p, true);\n\t\t\t}\n\n\t\t\tif (updateChildSelected) {\n\t\t\t\tchild.setSelected(isSelected);\n\t\t\t\tif (isSelected) {\n\t\t\t\t\trequestFocus();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (updateChildPressed) {\n\t\t\t\tchild.setPressed(isPressed);\n\t\t\t}\n\n\t\t\tif (needToMeasure) {\n\t\t\t\tint childWidthSpec = ViewGroup.getChildMeasureSpec(\n\t\t\t\t\t\tMeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), 0, p.width);\n\n\t\t\t\tint childHeightSpec = ViewGroup.getChildMeasureSpec(\n\t\t\t\t\t\tMeasureSpec.makeMeasureSpec(mRowHeight, MeasureSpec.EXACTLY), 0, p.height);\n\t\t\t\tchild.measure(childWidthSpec, childHeightSpec);\n\t\t\t} else {\n\t\t\t\tcleanupLayoutState(child);\n\t\t\t}\n\n\t\t\tfinal int w = child.getMeasuredWidth();\n\t\t\tfinal int h = child.getMeasuredHeight();\n\n\t\t\tfinal int childLeft = flow ? x : x - w;\n\t\t\tint childTop;\n\n\t\t\tswitch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) {\n\t\t\tcase Gravity.TOP:\n\t\t\t\tchildTop = childrenTop;\n\t\t\t\tbreak;\n\t\t\tcase Gravity.CENTER_HORIZONTAL:\n\t\t\t\tchildTop = childrenTop + ((mRowHeight - h) / 2);\n\t\t\t\tbreak;\n\t\t\tcase Gravity.RIGHT:\n\t\t\t\tchildTop = childrenTop + mRowHeight - h;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tchildTop = childrenTop;\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (needToMeasure) {\n\t\t\t\tfinal int childRight = childLeft + w;\n\t\t\t\tfinal int childBottom = childTop + h;\n\t\t\t\tchild.layout(childLeft, childTop, childRight, childBottom);\n\t\t\t} else {\n\t\t\t\tchild.offsetLeftAndRight(childLeft - child.getLeft());\n\t\t\t\tchild.offsetTopAndBottom(childTop - child.getTop());\n\t\t\t}\n\n\t\t\tif (mCachingStarted) {\n\t\t\t\tchild.setDrawingCacheEnabled(true);\n\t\t\t}\n\t\t}\n\n\t\tprivate void pinToLeft(int childrenLeft) {\n\t\t\tif (mFirstPosition == 0) {\n\t\t\t\tfinal int left = getChildAt(0).getLeft();\n\t\t\t\tfinal int offset = childrenLeft - left;\n\t\t\t\tif (offset < 0) {\n\t\t\t\t\toffsetChildrenLeftAndRight(offset);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tprivate void pinToRight(int childrenRight) {\n\t\t\tfinal int count = getChildCount();\n\t\t\tif (mFirstPosition + count == mItemCount) {\n\t\t\t\tfinal int right = getChildAt(count - 1).getRight();\n\t\t\t\tfinal int offset = childrenRight - right;\n\t\t\t\tif (offset > 0) {\n\t\t\t\t\toffsetChildrenLeftAndRight(offset);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Makes the item at the supplied position selected.\n\t\t *\n\t\t * @param position the position of the new selection\n\t\t */\n\t\t@Override\n\t\tprotected void setSelectionInt(int position) {\n\t\t\tint previousSelectedPosition = mNextSelectedPosition;\n\n\t\t\tsetNextSelectedPositionInt(position);\n\t\t\tTwoWayGridView.this.layoutChildren();\n\n\t\t\tfinal int next = mStackFromBottom ? mItemCount - 1  - mNextSelectedPosition :\n\t\t\t\tmNextSelectedPosition;\n\t\t\tfinal int previous = mStackFromBottom ? mItemCount - 1\n\t\t\t\t\t- previousSelectedPosition : previousSelectedPosition;\n\n\t\t\tfinal int nextColumn = next / mNumRows;\n\t\t\tfinal int previousColumn = previous / mNumRows;\n\n\t\t\tif (nextColumn != previousColumn) {\n\t\t\t\t//awakenScrollBars();\n\t\t\t}\n\n\t\t}\n\n\t\t/**\n\t\t * Scrolls to the next or previous item, horizontally or vertically.\n\t\t *\n\t\t * @param direction either {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT},\n\t\t *        {@link View#FOCUS_UP} or {@link View#FOCUS_DOWN}\n\t\t *\n\t\t * @return whether selection was moved\n\t\t */\n\t\t@Override\n\t\tprotected boolean arrowScroll(int direction) {\n\t\t\tfinal int selectedPosition = mSelectedPosition;\n\t\t\tfinal int numRows = mNumRows;\n\n\t\t\tint startOfColumnPos;\n\t\t\tint endOfColumnPos;\n\n\t\t\tboolean moved = false;\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\tstartOfColumnPos = (selectedPosition / numRows) * numRows;\n\t\t\t\tendOfColumnPos = Math.min(startOfColumnPos + numRows - 1, mItemCount - 1);\n\t\t\t} else {\n\t\t\t\tfinal int invertedSelection = mItemCount - 1 - selectedPosition;\n\t\t\t\tendOfColumnPos = mItemCount - 1 - (invertedSelection / numRows) * numRows;\n\t\t\t\tstartOfColumnPos = Math.max(0, endOfColumnPos - numRows + 1);\n\t\t\t}\n\n\t\t\tswitch (direction) {\n\t\t\tcase FOCUS_LEFT:\n\t\t\t\tif (startOfColumnPos > 0) {\n\t\t\t\t\tmLayoutMode = LAYOUT_MOVE_SELECTION;\n\t\t\t\t\tsetSelectionInt(Math.max(0, selectedPosition - numRows));\n\t\t\t\t\tmoved = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase FOCUS_RIGHT:\n\t\t\t\tif (startOfColumnPos < mItemCount - 1) {\n\t\t\t\t\tmLayoutMode = LAYOUT_MOVE_SELECTION;\n\t\t\t\t\tsetSelectionInt(Math.min(selectedPosition + numRows, mItemCount - 1));\n\t\t\t\t\tmoved = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase FOCUS_UP:\n\t\t\t\tif (selectedPosition > startOfColumnPos) {\n\t\t\t\t\tmLayoutMode = LAYOUT_MOVE_SELECTION;\n\t\t\t\t\tsetSelectionInt(Math.max(0, selectedPosition - 1));\n\t\t\t\t\tmoved = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\tcase FOCUS_DOWN:\n\t\t\t\tif (selectedPosition < endOfColumnPos) {\n\t\t\t\t\tmLayoutMode = LAYOUT_MOVE_SELECTION;\n\t\t\t\t\tsetSelectionInt(Math.min(selectedPosition + 1, mItemCount - 1));\n\t\t\t\t\tmoved = true;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tif (moved) {\n\t\t\t\tplaySoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));\n\t\t\t\tinvokeOnItemScrollListener();\n\t\t\t}\n\n\t\t\tif (moved) {\n\t\t\t\t//awakenScrollBars();\n\t\t\t}\n\n\t\t\treturn moved;\n\t\t}\n\n\t\t/**\n\t\t * Is childIndex a candidate for next focus given the direction the focus\n\t\t * change is coming from?\n\t\t * @param childIndex The index to check.\n\t\t * @param direction The direction, one of\n\t\t *        {FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}\n\t\t * @return Whether childIndex is a candidate.\n\t\t */\n\t\t@Override\n\t\tprotected boolean isCandidateSelection(int childIndex, int direction) {\n\t\t\tfinal int count = getChildCount();\n\t\t\tfinal int invertedIndex = count - 1 - childIndex;\n\t\t\tfinal int numRows = mNumRows;\n\n\t\t\tint columnStart;\n\t\t\tint columnEnd;\n\n\t\t\tif (!mStackFromBottom) {\n\t\t\t\tcolumnStart = childIndex - (childIndex % numRows);\n\t\t\t\tcolumnEnd = Math.max(columnStart + numRows - 1, count);\n\t\t\t} else {\n\t\t\t\tcolumnEnd = count - 1 - (invertedIndex - (invertedIndex % numRows));\n\t\t\t\tcolumnStart = Math.max(0, columnEnd - numRows + 1);\n\t\t\t}\n\n\t\t\tswitch (direction) {\n\t\t\tcase View.FOCUS_RIGHT:\n\t\t\t\t// coming from left, selection is only valid if it is on left\n\t\t\t\t// edge\n\t\t\t\treturn childIndex == columnStart;\n\t\t\tcase View.FOCUS_DOWN:\n\t\t\t\t// coming from top; only valid if in top row\n\t\t\t\treturn columnStart == 0;\n\t\t\tcase View.FOCUS_LEFT:\n\t\t\t\t// coming from right, must be on right edge\n\t\t\t\treturn childIndex == columnStart;\n\t\t\tcase View.FOCUS_UP:\n\t\t\t\t// coming from bottom, need to be in last row\n\t\t\t\treturn columnStart == count - 1;\n\t\t\tcase View.FOCUS_FORWARD:\n\t\t\t\t// coming from top-left, need to be first in top row\n\t\t\t\treturn childIndex == columnStart && columnStart == 0;\n\t\t\tcase View.FOCUS_BACKWARD:\n\t\t\t\t// coming from bottom-right, need to be last in bottom row\n\t\t\t\treturn childIndex == columnEnd && columnEnd == count - 1;\n\t\t\tdefault:\n\t\t\t\tthrow new IllegalArgumentException(\"direction must be one of \"\n\t\t\t\t\t\t+ \"{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT, \"\n\t\t\t\t\t\t+ \"FOCUS_FORWARD, FOCUS_BACKWARD}.\");\n\n\t\t\t}\n\t\t}\n\n\t}\n\n}\n\n"
  },
  {
    "path": "project.properties",
    "content": "# This file is automatically generated by Android Tools.\n# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n#\n# This file must be checked in Version Control Systems.\n#\n# To customize properties used by the Ant build system edit\n# \"ant.properties\", and override values to adapt the script to your\n# project structure.\n#\n# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):\n#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt\n\n# Indicates whether an apk should be generated for each density.\nsplit.density=false\n# Project target.\ntarget=android-7\nandroid.library=true\n"
  },
  {
    "path": "sample/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n\tpackage=\"com.jess.demo\"\n\tandroid:versionCode=\"1\"\n\tandroid:versionName=\"1.0\" >\n\n\t<uses-sdk\n\t\tandroid:minSdkVersion=\"5\" />\n\n\t<application\n\t\tandroid:icon=\"@drawable/icon\"\n\t\tandroid:label=\"@string/app_name\" >\n\t\t<activity\n\t\t\tandroid:name=\"com.jess.demo.MainActivity\"\n\t\t\tandroid:label=\"@string/app_name\" >\n\t\t\t<intent-filter>\n\t\t\t\t<action\n\t\t\t\t\tandroid:name=\"android.intent.action.MAIN\" />\n\n\t\t\t\t<category\n\t\t\t\t\tandroid:name=\"android.intent.category.LAUNCHER\" />\n\t\t\t</intent-filter>\n\t\t</activity>\n\t</application>\n\n</manifest>"
  },
  {
    "path": "sample/ant.properties",
    "content": "# This file is used to override default values used by the Ant build system.\n#\n# This file must be checked into Version Control Systems, as it is\n# integral to the build system of your project.\n\n# This file is only used by the Ant script.\n\n# You can use this to override default values such as\n#  'source.dir' for the location of your java source folder and\n#  'out.dir' for the location of your output folder.\n\n# You can also use it define how the release builds are signed by declaring\n# the following properties:\n#  'key.store' for the location of your keystore and\n#  'key.alias' for the name of the key to use.\n# The password will be asked during the build when you use the 'release' target.\n\n"
  },
  {
    "path": "sample/proguard-project.txt",
    "content": "# To enable ProGuard in your project, edit project.properties\n# to define the proguard.config property as described in that file.\n#\n# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in ${sdk.dir}/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the ProGuard\n# include property in project.properties.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "sample/project.properties",
    "content": "# This file is automatically generated by Android Tools.\n# Do not modify this file -- YOUR CHANGES WILL BE ERASED!\n#\n# This file must be checked in Version Control Systems.\n#\n# To customize properties used by the Ant build system edit\n# \"ant.properties\", and override values to adapt the script to your\n# project structure.\n#\n# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):\n#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt\n\n# Project target.\ntarget=android-16\nandroid.library.reference.1=../lib\n"
  },
  {
    "path": "sample/res/layout/main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.jess.ui.TwoWayGridView\n    xmlns:android=\"http://schemas.android.com/apk/res/android\" \n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:background=\"#E8E8E8\"\n    android:id=\"@+id/gridview\"\n    android:layout_width=\"fill_parent\" \n    android:layout_height=\"fill_parent\"\n    app:cacheColorHint=\"#E8E8E8\"\n    app:columnWidth=\"80dp\"\n    app:rowHeight=\"80dp\"\n    app:numColumns=\"auto_fit\"\n    app:numRows=\"auto_fit\"\n    app:verticalSpacing=\"16dp\"\n    app:horizontalSpacing=\"16dp\"\n    app:stretchMode=\"spacingWidthUniform\"\n    app:scrollDirectionPortrait=\"vertical\"\n    app:scrollDirectionLandscape=\"horizontal\"\n    app:gravity=\"center\"/>\n"
  },
  {
    "path": "sample/res/values/strings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"app_name\">EverGrid</string>\n</resources>\n"
  },
  {
    "path": "sample/src/com/jess/demo/BetterImageView.java",
    "content": "/*\n * BetterImageView\n *\n * Copyright 2012 Jess Anders\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.jess.demo;\n\nimport android.content.Context;\nimport android.graphics.drawable.Drawable;\nimport android.util.AttributeSet;\nimport android.util.Log;\nimport android.widget.ImageView;\n\n\npublic class BetterImageView extends ImageView {\n\tprivate static final String TAG = \"BetterImageView\";\n\tprivate static final boolean DEBUG = false;\n\n\tpublic BetterImageView(Context context) {\n\t\tsuper(context);\n\t}\n\n\tpublic BetterImageView(Context context, AttributeSet attrs) {\n\t\tthis(context, attrs, 0);\n\t}\n\n\tpublic BetterImageView(Context context, AttributeSet attrs, int defStyle) {\n\t\tsuper(context, attrs, defStyle);\n\t}\n\n\t@Override\n\tpublic void invalidateDrawable(Drawable dr) {\n\t\tDrawable currentDrawable = getDrawable();\n\t\tif (DEBUG) Log.i(TAG, \"invalidateDrawable: \" + dr + \" current drawable: \" + currentDrawable);\n\t\tif (dr == currentDrawable) {\n\t\t\t/* we invalidate the whole view in this case because it's very\n\t\t\t * hard to know where the drawable actually is. This is made\n\t\t\t * complicated because of the offsets and transformations that\n\t\t\t * can be applied. In theory we could get the drawable's bounds\n\t\t\t * and run them through the transformation and offsets, but this\n\t\t\t * is probably not worth the effort.\n\t\t\t */\n\t\t\tLog.i(TAG, \"invalidateDrawable - setting imageDrawable\");\n\t\t\t//destroyDrawingCache();\n\t\t\tdrawableStateChanged();\n\t\t\tforceLayout();\n\t\t\tsetImageDrawable(currentDrawable);\n\t\t\tinvalidate();\n\t\t} else {\n\t\t\tsuper.invalidateDrawable(dr);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "sample/src/com/jess/demo/ImageThumbnailAdapter.java",
    "content": "package com.jess.demo;\n\nimport java.lang.ref.SoftReference;\nimport java.util.LinkedList;\nimport java.util.Map;\nimport java.util.concurrent.ConcurrentHashMap;\n\nimport android.content.ContentResolver;\nimport android.content.ContentUris;\nimport android.content.Context;\nimport android.database.Cursor;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.graphics.BitmapFactory.Options;\nimport android.net.Uri;\nimport android.os.Handler;\nimport android.provider.MediaStore;\nimport android.util.Log;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.CursorAdapter;\nimport android.widget.ImageView;\n\nimport com.jess.ui.TwoWayAbsListView;\n\npublic class ImageThumbnailAdapter extends CursorAdapter {\n\tpublic static final String[] IMAGE_PROJECTION = {\n\t\tMediaStore.Images.ImageColumns._ID,\n\t\tMediaStore.Images.ImageColumns.DISPLAY_NAME,\n\t};\n\n\tpublic static final int IMAGE_ID_COLUMN = 0;\n\tpublic static final int IMAGE_NAME_COLUMN = 1;\n\n\tpublic static final boolean DEBUG = false;\n\n\tprivate static final String TAG = \"ImageThumbnailAdapter\";\n\n\tprivate static float IMAGE_WIDTH = 80;\n\tprivate static float IMAGE_HEIGHT = 80;\n\tprivate static float IMAGE_PADDING = 6;\n\n\tprivate static final Map<Long, SoftReference<ReplaceableBitmapDrawable>> sImageCache =\n\t\tnew ConcurrentHashMap<Long, SoftReference<ReplaceableBitmapDrawable>>();\n\n\tprivate static Options sBitmapOptions = new Options();\n\n\tprivate final Context mContext;\n\tprivate Bitmap mDefaultBitmap;\n\tprivate final ContentResolver mContentResolver;\n\tprivate final Handler mHandler;\n\tprivate float mScale;\n\tprivate int mImageWidth;\n\tprivate int mImageHeight;\n\tprivate int mImagePadding;\n\n\tpublic ImageThumbnailAdapter(Context context, Cursor c) {\n\t\tthis(context, c, true);\n\t}\n\n\tpublic ImageThumbnailAdapter(Context context, Cursor c, boolean autoRequery) {\n\t\tsuper(context, c, autoRequery);\n\t\tmContext = context;\n\t\tinit(c);\n\t\tmContentResolver = context.getContentResolver();\n\t\tmHandler = new Handler();\n\t}\n\t\n\t\n\tprivate void init(Cursor c) {\n\t\tmDefaultBitmap = BitmapFactory.decodeResource(mContext.getResources(),\n\t\t\t\tR.drawable.spinner_black_76);\n\n\t\tmScale = mContext.getResources().getDisplayMetrics().density;\n\t\tmImageWidth = (int)(IMAGE_WIDTH * mScale);\n\t\tmImageHeight = (int)(IMAGE_HEIGHT * mScale);\n\t\tmImagePadding = (int)(IMAGE_PADDING * mScale);\n\t\tsBitmapOptions.inSampleSize = 4;\n\t}\n\n\t@Override\n\tpublic int getItemViewType(int position) {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int getViewTypeCount() {\n\t\treturn 1;\n\t}\n\n\t@Override\n\tpublic void bindView(View view, Context context, Cursor cursor) {\n\t\tint id = cursor.getInt(IMAGE_ID_COLUMN);\n\t\t((ImageView)view).setImageDrawable(getCachedThumbnailAsync(\n\t\t\t\tContentUris.withAppendedId(MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, id)));\n\t}\n\n\t@Override\n\tpublic View newView(Context context, Cursor cursor, ViewGroup parent) {\n\t\tImageView imageView = new BetterImageView(mContext.getApplicationContext());\n\t\timageView.setScaleType(ImageView.ScaleType.FIT_XY);\n\t\timageView.setLayoutParams(new TwoWayAbsListView.LayoutParams(mImageWidth, mImageHeight));\n\t\timageView.setPadding(mImagePadding, mImagePadding, mImagePadding, mImagePadding);\n\t\treturn imageView;\n\t}\n\n\tpublic void cleanup() {\n\t\tcleanupCache();\n\t}\n\n\n\tprivate static Bitmap loadThumbnail(ContentResolver cr, Uri uri) {\n\t\t\n\t\treturn MediaStore.Images.Thumbnails.getThumbnail(\n\t\t\t\tcr, ContentUris.parseId(uri), MediaStore.Images.Thumbnails.MINI_KIND, sBitmapOptions);\n\t}\n\n\n\t/**\n\t * Retrieves a drawable from the image cache, identified by the specified id.\n\t * If the drawable does not exist in the cache, it is loaded asynchronously and added to the cache.\n\t * If the drawable cannot be added to the cache, the specified default drawable is\n\t * returned.\n\t *\n\t * @param uri The uri of the drawable to retrieve\n\t *\n\t * @return The drawable identified by id or defaultImage\n\t */\n\tprivate ReplaceableBitmapDrawable getCachedThumbnailAsync(Uri uri) {\n\t\tReplaceableBitmapDrawable drawable = null;\n\t\tlong id = ContentUris.parseId(uri);\n\n\t\tWorkQueue wq = WorkQueue.getInstance();\n\t\tsynchronized(wq.mQueue) {\n\t\t\tSoftReference<ReplaceableBitmapDrawable> reference = sImageCache.get(id);\n\t\t\tif (reference != null) {\n\t\t\t\tdrawable = reference.get();\n\t\t\t}\n\n\t\t\tif (drawable == null || !drawable.isLoaded()) {\n\t\t\t\tdrawable = new ReplaceableBitmapDrawable(mDefaultBitmap);\n\t\t\t\tsImageCache.put(id, new SoftReference<ReplaceableBitmapDrawable>(drawable));\n\t\t\t\tImageLoadingArgs args = new ImageLoadingArgs(mContentResolver, mHandler, drawable, uri);\n\t\t\t\twq.execute(new ImageLoader(args));\n\n\t\t\t}\n\t\t}\n\n\t\treturn drawable;\n\t}\n\n\t/**\n\t * Removes all the callbacks from the drawables stored in the memory cache. This\n\t * method must be called from the onDestroy() method of any activity using the\n\t * cached drawables. Failure to do so will result in the entire activity being\n\t * leaked.\n\t */\n\tpublic static void cleanupCache() {\n\t\tfor (SoftReference<ReplaceableBitmapDrawable> reference : sImageCache.values()) {\n\t\t\tfinal ReplaceableBitmapDrawable drawable = reference.get();\n\t\t\tif (drawable != null) drawable.setCallback(null);\n\t\t}\n\t}\n\n\t/**\n\t * Deletes the specified drawable from the cache.\n\t *\n\t * @param uri The uri of the drawable to delete from the cache\n\t */\n\tpublic static void deleteCachedCover(Uri uri) {\n\t\tsImageCache.remove(ContentUris.parseId(uri));\n\t}\n\n\n\t/**\n\t * Class to asynchronously perform the loading of the bitmap\n\t */\n\tpublic static class ImageLoader implements Runnable {\n\t\tprotected ImageLoadingArgs mArgs = null;\n\n\t\tpublic ImageLoader(ImageLoadingArgs args) {\n\t\t\tmArgs = args;\n\t\t}\n\n\t\tpublic void run() {\n\t\t\tfinal Bitmap bitmap = loadThumbnail(mArgs.mContentResolver, mArgs.mUri);\n\t\t\tif (DEBUG) Log.i(TAG, \"run() bitmap: \" + bitmap);\n\t\t\tif (bitmap != null) {\n\t\t\t\tfinal ReplaceableBitmapDrawable d = mArgs.mDrawable;\n\t\t\t\tif (d != null) {\n\t\t\t\t\tmArgs.mHandler.post(new Runnable() {\n\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\tif (DEBUG) Log.i(TAG, \"ImageLoader.run() - setting the bitmap for uri: \" + mArgs.mUri);\n\t\t\t\t\t\t\td.setBitmap(bitmap);\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t} else {\n\t\t\t\t\tLog.e(TAG, \"ImageLoader.run() - FastBitmapDrawable is null for uri: \" + mArgs.mUri);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tLog.e(TAG, \"ImageLoader.run() - bitmap is null for uri: \" + mArgs.mUri);\n\t\t\t}\n\t\t}\n\n\t\tpublic void cancel() {\n\t\t\tsImageCache.remove(mArgs.mUri);\n\t\t}\n\n\t\t@Override\n\t\tpublic boolean equals(Object obj) {\n\t\t\tif (obj != null && obj instanceof ImageLoader) {\n\t\t\t\tif (mArgs.mUri != null) {\n\t\t\t\t\treturn mArgs.mUri.equals(((ImageLoader)obj).mArgs);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\t@Override\n\t\tpublic int hashCode() {\n\t\t\treturn mArgs.mUri.hashCode();\n\t\t}\n\n\t}\n\n\n\t/**\n\t * Class to hold all the parts necessary to load an image\n\t */\n\tpublic static class ImageLoadingArgs {\n\t\tContentResolver mContentResolver;\n\t\tHandler mHandler;\n\t\tReplaceableBitmapDrawable mDrawable;\n\t\tUri mUri;\n\n\t\t/**\n\t\t * @param contentResolver - ContentResolver to use\n\t\t * @param drawable - FastBitmapDrawable whose underlying bitmap should be replaced with new bitmap\n\t\t * @param uri - Uri of image location\n\t\t */\n\t\tpublic ImageLoadingArgs(ContentResolver contentResolver, Handler handler,\n\t\t\t\tReplaceableBitmapDrawable drawable, Uri uri) {\n\t\t\tmContentResolver = contentResolver;\n\t\t\tmHandler = handler;\n\t\t\tmDrawable = drawable;\n\t\t\tmUri = uri;\n\t\t}\n\t}\n\n\n\tpublic static class WorkQueue {\n\t\tprivate static WorkQueue sInstance = null;\n\t\tprivate static final int NUM_OF_THREADS = 1;\n\t\tprivate static final int MAX_QUEUE_SIZE = 21;\n\n\t\tprivate final int mNumOfThreads;\n\t\tprivate final PoolWorker[] mThreads;\n\t\tprotected final LinkedList<ImageLoader> mQueue;\n\n\t\tpublic static synchronized WorkQueue getInstance() {\n\t\t\tif (sInstance == null) {\n\t\t\t\tsInstance = new WorkQueue(NUM_OF_THREADS);\n\t\t\t}\n\t\t\treturn sInstance;\n\t\t}\n\n\t\tprivate WorkQueue(int nThreads) {\n\t\t\tmNumOfThreads = nThreads;\n\t\t\tmQueue = new LinkedList<ImageLoader>();\n\t\t\tmThreads = new PoolWorker[mNumOfThreads];\n\n\t\t\tfor (int i=0; i < mNumOfThreads; i++) {\n\t\t\t\tmThreads[i] = new PoolWorker();\n\t\t\t\tmThreads[i].start();\n\t\t\t}\n\t\t}\n\n\t\tpublic void execute(ImageLoader r) {\n\t\t\tsynchronized(mQueue) {\n\t\t\t\tmQueue.remove(r);\n\t\t\t\tif (mQueue.size() > MAX_QUEUE_SIZE) {\n\t\t\t\t\tmQueue.removeFirst().cancel();\n\t\t\t\t}\n\t\t\t\tmQueue.addLast(r);\n\t\t\t\tmQueue.notify();\n\t\t\t}\n\t\t}\n\n\t\tprivate class PoolWorker extends Thread {\n\t\t\tprivate boolean mRunning = true;\n\t\t\t@Override\n\t\t\tpublic void run() {\n\t\t\t\tRunnable r;\n\n\t\t\t\twhile (mRunning) {\n\t\t\t\t\tsynchronized(mQueue) {\n\t\t\t\t\t\twhile (mQueue.isEmpty() && mRunning) {\n\t\t\t\t\t\t\ttry\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\tmQueue.wait();\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcatch (InterruptedException ignored)\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tr = mQueue.removeFirst();\n\t\t\t\t\t}\n\n\t\t\t\t\t// If we don't catch RuntimeException,\n\t\t\t\t\t// the pool could leak threads\n\t\t\t\t\ttry {\n\t\t\t\t\t\tr.run();\n\t\t\t\t\t}\n\t\t\t\t\tcatch (RuntimeException e) {\n\t\t\t\t\t\tLog.e(TAG, \"RuntimeException\", e);\n\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tLog.i(TAG, \"PoolWorker finished\");\n\t\t\t}\n\n\t\t\tpublic void stopWorker() {\n\t\t\t\tmRunning = false;\n\t\t\t}\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "sample/src/com/jess/demo/MainActivity.java",
    "content": "package com.jess.demo;\n\nimport android.app.Activity;\nimport android.content.ContentUris;\nimport android.content.Intent;\nimport android.database.Cursor;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.provider.MediaStore;\nimport android.util.Log;\nimport android.view.View;\n\nimport com.jess.ui.R;\nimport com.jess.ui.TwoWayAdapterView;\nimport com.jess.ui.TwoWayAdapterView.OnItemClickListener;\nimport com.jess.ui.TwoWayGridView;\n\npublic class MainActivity extends Activity {\n\tprivate static final String TAG = \"MainActivity\";\n\n\tprivate Cursor mImageCursor;\n\tprivate ImageThumbnailAdapter mAdapter;\n\tprivate TwoWayGridView mImageGrid;\n\n\t@Override\n\tpublic void onCreate(Bundle savedInstanceState) {\n\t\tsuper.onCreate(savedInstanceState);\n\t\tsetContentView(R.layout.main);\n\t\tinitGrid();\n\t}\n\n\n\tprivate void initGrid() {\n\t\tmImageCursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,\n\t\t\t\tImageThumbnailAdapter.IMAGE_PROJECTION, null, null,\n\t\t\t\tMediaStore.Images.ImageColumns.DISPLAY_NAME);\n\t\tmImageGrid = (TwoWayGridView) findViewById(R.id.gridview);\n\t\tmAdapter = new ImageThumbnailAdapter(this, mImageCursor);\n\t\tmImageGrid.setAdapter(mAdapter);\n\n\t\tmImageGrid.setOnItemClickListener(new OnItemClickListener() {\n\t\t\tpublic void onItemClick(TwoWayAdapterView parent, View v, int position, long id) {\n\t\t\t\tLog.i(TAG, \"showing image: \" + mImageCursor.getString(ImageThumbnailAdapter.IMAGE_NAME_COLUMN));\n\t\t\t\tUri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);\n\t\t\t\tIntent intent = new Intent(Intent.ACTION_VIEW, uri);\n\t\t\t\tstartActivity(intent);\n\t\t\t}\n\t\t});\n\t}\n\n\t@Override\n\tprotected void onDestroy() {\n\t\tsuper.onDestroy();\n\t\tmAdapter.cleanup();\n\t}\n\n}"
  },
  {
    "path": "sample/src/com/jess/demo/ReplaceableBitmapDrawable.java",
    "content": "/*\n * ReplaceableBitmapDrawable\n *\n * Copyright 2012 Jess Anders\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage com.jess.demo;\n\n\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.ColorFilter;\nimport android.graphics.PixelFormat;\nimport android.graphics.Rect;\nimport android.graphics.drawable.Drawable;\nimport android.util.Log;\nimport android.view.Gravity;\n\npublic class ReplaceableBitmapDrawable extends Drawable {\n\tprivate static final String TAG = \"ReplaceableBitmapDrawable\";\n\tprivate static final boolean DEBUG = false;\n\n\tprivate Bitmap mBitmap;\n\tprivate boolean mLoaded;\n\tprivate boolean mApplyGravity;\n\tprivate int mGravity;\n\tprivate final Rect mDstRect = new Rect();\n\n\tpublic ReplaceableBitmapDrawable(Bitmap b) {\n\t\tmBitmap = b;\n\t}\n\n\t@Override\n\tpublic void draw(Canvas canvas) {\n\t\tcopyBounds(mDstRect);\n\t\tif (mBitmap != null) {\n\t\t\tif (mApplyGravity) {\n\t\t\t\tGravity.apply(mGravity, super.getIntrinsicWidth(), super.getIntrinsicHeight(),\n\t\t\t\t\t\tgetBounds(), mDstRect);\n\t\t\t\tmApplyGravity = false;\n\t\t\t}\n\t\t\tcanvas.drawBitmap(mBitmap, null, mDstRect, null);\n\n\t\t}\n\t}\n\n\n\tpublic void setGravity(int gravity) {\n\t\tmGravity = gravity;\n\t\tmApplyGravity = true;\n\t}\n\n\t@Override\n\tpublic int getOpacity() {\n\t\treturn PixelFormat.OPAQUE;\n\t}\n\n\t@Override\n\tpublic void setAlpha(int alpha) {\n\t}\n\n\t@Override\n\tpublic void setColorFilter(ColorFilter cf) {\n\t}\n\n\t@Override\n\tpublic int getIntrinsicWidth() {\n\t\tif (mBitmap != null) {\n\t\t\tif (DEBUG) Log.i(TAG, \"getIntrinsicWidth(): \" + mBitmap.getWidth()+ \" \" + this);\n\t\t\treturn mBitmap.getWidth();\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int getIntrinsicHeight() {\n\t\tif (mBitmap != null) {\n\t\t\tif (DEBUG) Log.i(TAG, \"getIntrinsicHeight(): \" + mBitmap.getHeight() + \" \" + this);\n\t\t\treturn mBitmap.getHeight();\n\t\t} else {\n\t\t\treturn 0;\n\t\t}\n\t}\n\n\t@Override\n\tpublic int getMinimumWidth() {\n\t\treturn 0;\n\t}\n\n\t@Override\n\tpublic int getMinimumHeight() {\n\t\treturn 0;\n\t}\n\n\tpublic Bitmap getBitmap() {\n\t\treturn mBitmap;\n\t}\n\n\tpublic void setBitmap(Bitmap bitmap) {\n\t\tmLoaded = true;\n\t\tmBitmap = bitmap;\n\t\tif (DEBUG) Log.i(\"ReplaceableBitmapDrawable\", \"setBitmap() \" + this);\n\t\tinvalidateSelf();\n\t}\n\n\tpublic boolean isLoaded() {\n\t\treturn mLoaded;\n\t}\n}\n"
  }
]