Add new fade in/out feature for drawable containers.

This is used to allow list view's pressed and activated indicators
to fade in an out, though of course it can be used elsewhere as well.

There is a lot of complexity in supporting this in list view.  The
two main things that are being dealt with:

- When recycling views, we need to make sure that the view's drawable
  state doesn't get animated from an old row's state.  The recycler
  now keeps track of which position a view was last in, and if it is
  reused at a new position there is a new View/Drawable API to tell
  it to jump to its current state instead of animating.

- For the pressed indicator to fade out, we need to keep displaying it
  after it is hidden.  There are new variables and code to keep track
  of this state, and tweaks in various places to be able to remember
  the last selected position and continue updating the drawable bounds
  as needed.

Change-Id: Ic96aa1a3c05e519665abf3098892ff2cc4f0ef2f
diff --git a/api/current.xml b/api/current.xml
index d682155..f14df1d 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -1905,7 +1905,7 @@
  type="int"
  transient="false"
  volatile="false"
- value="16843550"
+ value="16843552"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -1916,7 +1916,7 @@
  type="int"
  transient="false"
  volatile="false"
- value="16843549"
+ value="16843551"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -1927,7 +1927,7 @@
  type="int"
  transient="false"
  volatile="false"
- value="16843551"
+ value="16843553"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -3804,6 +3804,17 @@
  visibility="public"
 >
 </field>
+<field name="enterFadeDuration"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843549"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="entries"
  type="int"
  transient="false"
@@ -3848,6 +3859,17 @@
  visibility="public"
 >
 </field>
+<field name="exitFadeDuration"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="16843550"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="expandableListPreferredChildIndicatorLeft"
  type="int"
  transient="false"
@@ -9308,7 +9330,7 @@
  type="int"
  transient="false"
  volatile="false"
- value="16843553"
+ value="16843555"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -9319,7 +9341,7 @@
  type="int"
  transient="false"
  volatile="false"
- value="16843552"
+ value="16843554"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -83932,6 +83954,17 @@
  visibility="public"
 >
 </method>
+<method name="jumpToCurrentState"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="mutate"
  return="android.graphics.drawable.Drawable"
  abstract="false"
@@ -84421,6 +84454,32 @@
 <parameter name="state" type="android.graphics.drawable.DrawableContainer.DrawableContainerState">
 </parameter>
 </method>
+<method name="setEnterFadeDuration"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="ms" type="int">
+</parameter>
+</method>
+<method name="setExitFadeDuration"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="ms" type="int">
+</parameter>
+</method>
 <method name="unscheduleDrawable"
  return="void"
  abstract="false"
@@ -84568,6 +84627,28 @@
  visibility="public"
 >
 </method>
+<method name="getEnterFadeDuration"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getExitFadeDuration"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getOpacity"
  return="int"
  abstract="false"
@@ -84629,6 +84710,32 @@
 <parameter name="constant" type="boolean">
 </parameter>
 </method>
+<method name="setEnterFadeDuration"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="duration" type="int">
+</parameter>
+</method>
+<method name="setExitFadeDuration"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="duration" type="int">
+</parameter>
+</method>
 <method name="setVariablePadding"
  return="void"
  abstract="false"
@@ -190423,6 +190530,17 @@
 <parameter name="newSize" type="int">
 </parameter>
 </method>
+<field name="NOTHING"
+ type="int[]"
+ transient="false"
+ volatile="false"
+ value="null"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
 <field name="WILD_CARD"
  type="int[]"
  transient="false"
@@ -203765,6 +203883,17 @@
  visibility="public"
 >
 </method>
+<method name="jumpDrawablesToCurrentState"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="layout"
  return="void"
  abstract="false"
diff --git a/core/java/android/util/StateSet.java b/core/java/android/util/StateSet.java
index f3d8159..21d8e45 100644
--- a/core/java/android/util/StateSet.java
+++ b/core/java/android/util/StateSet.java
@@ -38,6 +38,7 @@
 public class StateSet {
 
     public static final int[] WILD_CARD = new int[0];
+    public static final int[] NOTHING = new int[] { 0 };
 
     /**
      * Return whether the stateSetOrSpec is matched by all StateSets.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index fd7f4a4..340678d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -8620,6 +8620,16 @@
     }
 
     /**
+     * Call {@link Drawable#jumpToCurrentState() Drawable.jumpToCurrentState()}
+     * on all Drawable objects associated with this view.
+     */
+    public void jumpDrawablesToCurrentState() {
+        if (mBGDrawable != null) {
+            mBGDrawable.jumpToCurrentState();
+        }
+    }
+
+    /**
      * Sets the background color for this view.
      * @param color the color of the background
      */
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 4e90ecd..7629673 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -36,6 +36,7 @@
 import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.SparseBooleanArray;
+import android.util.StateSet;
 import android.view.ActionMode;
 import android.view.Gravity;
 import android.view.HapticFeedbackConstants;
@@ -254,6 +255,17 @@
     Drawable mSelector;
 
     /**
+     * Set to true if we would like to have the selector showing itself.
+     * We still need to draw and position it even if this is false.
+     */
+    boolean mSelectorShowing;
+
+    /**
+     * The current position of the selector in the list.
+     */
+    int mSelectorPosition = INVALID_POSITION;
+
+    /**
      * Defines the selector's location and dimension at drawing time
      */
     Rect mSelectorRect = new Rect();
@@ -1324,6 +1336,7 @@
             setSelectedPositionInt(INVALID_POSITION);
             // Do this before setting mNeedSync since setNextSelectedPosition looks at mNeedSync
             setNextSelectedPositionInt(INVALID_POSITION);
+            mSelectorPosition = INVALID_POSITION;
             mNeedSync = true;
             mSyncRowId = ss.firstId;
             mSyncPosition = ss.position;
@@ -1416,6 +1429,8 @@
         setSelectedPositionInt(INVALID_POSITION);
         setNextSelectedPositionInt(INVALID_POSITION);
         mSelectedTop = 0;
+        mSelectorShowing = false;
+        mSelectorPosition = INVALID_POSITION;
         mSelectorRect.setEmpty();
         invalidate();
     }
@@ -1708,7 +1723,7 @@
             }
 
             if (child != scrapView) {
-                mRecycler.addScrapView(scrapView);
+                mRecycler.addScrapView(scrapView, position);
                 if (mCacheColorHint != 0) {
                     child.setDrawingCacheBackgroundColor(mCacheColorHint);
                 }
@@ -1734,7 +1749,11 @@
         return child;
     }
 
-    void positionSelector(View sel) {
+    void positionSelector(int position, View sel) {
+        if (position != INVALID_POSITION) {
+            mSelectorPosition = position;
+        }
+
         final Rect selectorRect = mSelectorRect;
         selectorRect.set(sel.getLeft(), sel.getTop(), sel.getRight(), sel.getBottom());
         positionSelector(selectorRect.left, selectorRect.top, selectorRect.right,
@@ -1743,7 +1762,9 @@
         final boolean isChildViewEnabled = mIsChildViewEnabled;
         if (sel.isEnabled() != isChildViewEnabled) {
             mIsChildViewEnabled = !isChildViewEnabled;
-            refreshDrawableState();
+            if (mSelectorShowing) {
+                refreshDrawableState();
+            }
         }
     }
 
@@ -1822,7 +1843,7 @@
     }
 
     private void drawSelector(Canvas canvas) {
-        if (shouldShowSelector() && mSelectorRect != null && !mSelectorRect.isEmpty()) {
+        if (!mSelectorRect.isEmpty()) {
             final Drawable selector = mSelector;
             selector.setBounds(mSelectorRect);
             selector.draw(canvas);
@@ -1866,7 +1887,7 @@
         mSelectionRightPadding = padding.right;
         mSelectionBottomPadding = padding.bottom;
         sel.setCallback(this);
-        sel.setState(getDrawableState());
+        updateSelectorState();
     }
 
     /**
@@ -1891,7 +1912,7 @@
         Drawable selector = mSelector;
         Rect selectorRect = mSelectorRect;
         if (selector != null && (isFocused() || touchModeDrawsInPressedState())
-                && selectorRect != null && !selectorRect.isEmpty()) {
+                && !selectorRect.isEmpty()) {
 
             final View v = getChildAt(mSelectedPosition - mFirstPosition);
 
@@ -1926,12 +1947,20 @@
         mScrollDown = down;
     }
 
+    void updateSelectorState() {
+        if (mSelector != null) {
+            if (shouldShowSelector()) {
+                mSelector.setState(getDrawableState());
+            } else {
+                mSelector.setState(StateSet.NOTHING);
+            }
+        }
+    }
+
     @Override
     protected void drawableStateChanged() {
         super.drawableStateChanged();
-        if (mSelector != null) {
-            mSelector.setState(getDrawableState());
-        }
+        updateSelectorState();
     }
 
     @Override
@@ -2141,7 +2170,6 @@
                 } else {
                     mTouchMode = TOUCH_MODE_DONE_WAITING;
                 }
-
             }
         }
     }
@@ -2316,10 +2344,10 @@
                     mLayoutMode = LAYOUT_NORMAL;
 
                     if (!mDataChanged) {
-                        layoutChildren();
                         child.setPressed(true);
-                        positionSelector(child);
                         setPressed(true);
+                        layoutChildren();
+                        positionSelector(mMotionPosition, child);
 
                         final int longPressTimeout = ViewConfiguration.getLongPressTimeout();
                         final boolean longClickable = isLongClickable();
@@ -2566,7 +2594,7 @@
                             setSelectedPositionInt(mMotionPosition);
                             layoutChildren();
                             child.setPressed(true);
-                            positionSelector(child);
+                            positionSelector(mMotionPosition, child);
                             setPressed(true);
                             if (mSelector != null) {
                                 Drawable d = mSelector.getCurrent();
@@ -2576,16 +2604,17 @@
                             }
                             postDelayed(new Runnable() {
                                 public void run() {
+                                    mTouchMode = TOUCH_MODE_REST;
                                     child.setPressed(false);
                                     setPressed(false);
                                     if (!mDataChanged) {
                                         post(performClick);
                                     }
-                                    mTouchMode = TOUCH_MODE_REST;
                                 }
                             }, ViewConfiguration.getPressedStateDuration());
                         } else {
                             mTouchMode = TOUCH_MODE_REST;
+                            updateSelectorState();
                         }
                         return true;
                     } else if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {
@@ -2593,6 +2622,7 @@
                     }
                 }
                 mTouchMode = TOUCH_MODE_REST;
+                updateSelectorState();
                 break;
             case TOUCH_MODE_SCROLL:
                 final int childCount = getChildCount();
@@ -3507,7 +3537,7 @@
                     count++;
                     int position = firstPosition + i;
                     if (position >= headerViewsCount && position < footerViewsStart) {
-                        mRecycler.addScrapView(child);
+                        mRecycler.addScrapView(child, position);
 
                         if (ViewDebug.TRACE_RECYCLER) {
                             ViewDebug.trace(child,
@@ -3528,7 +3558,7 @@
                     count++;
                     int position = firstPosition + i;
                     if (position >= headerViewsCount && position < footerViewsStart) {
-                        mRecycler.addScrapView(child);
+                        mRecycler.addScrapView(child, position);
 
                         if (ViewDebug.TRACE_RECYCLER) {
                             ViewDebug.trace(child,
@@ -3563,8 +3593,15 @@
         if (!inTouchMode && mSelectedPosition != INVALID_POSITION) {
             final int childIndex = mSelectedPosition - mFirstPosition;
             if (childIndex >= 0 && childIndex < getChildCount()) {
-                positionSelector(getChildAt(childIndex));
+                positionSelector(mSelectedPosition, getChildAt(childIndex));
             }
+        } else if (mSelectorPosition != INVALID_POSITION) {
+            final int childIndex = mSelectorPosition - mFirstPosition;
+            if (childIndex >= 0 && childIndex < getChildCount()) {
+                positionSelector(INVALID_POSITION, getChildAt(childIndex));
+            }
+        } else {
+            mSelectorRect.setEmpty();
         }
 
         mBlockLayoutRequests = false;
@@ -3616,7 +3653,7 @@
             setSelectedPositionInt(INVALID_POSITION);
             setNextSelectedPositionInt(INVALID_POSITION);
             mSelectedTop = 0;
-            mSelectorRect.setEmpty();
+            mSelectorShowing = false;
         }
     }
 
@@ -3876,6 +3913,7 @@
         mNextSelectedPosition = INVALID_POSITION;
         mNextSelectedRowId = INVALID_ROW_ID;
         mNeedSync = false;
+        mSelectorPosition = INVALID_POSITION;
         checkSelectionChanged();
     }
 
@@ -4562,6 +4600,13 @@
         @ViewDebug.ExportedProperty(category = "list")
         boolean forceAdd;
 
+        /**
+         * The position the view was removed from when pulled out of the
+         * scrap heap.
+         * @hide
+         */
+        int scrappedFromPosition;
+
         public LayoutParams(Context c, AttributeSet attrs) {
             super(c, attrs);
         }
@@ -4741,23 +4786,12 @@
          * @return A view from the ScrapViews collection. These are unordered.
          */
         View getScrapView(int position) {
-            ArrayList<View> scrapViews;
             if (mViewTypeCount == 1) {
-                scrapViews = mCurrentScrap;
-                int size = scrapViews.size();
-                if (size > 0) {
-                    return scrapViews.remove(size - 1);
-                } else {
-                    return null;
-                }
+                return retrieveFromScrap(mCurrentScrap, position);
             } else {
                 int whichScrap = mAdapter.getItemViewType(position);
                 if (whichScrap >= 0 && whichScrap < mScrapViews.length) {
-                    scrapViews = mScrapViews[whichScrap];
-                    int size = scrapViews.size();
-                    if (size > 0) {
-                        return scrapViews.remove(size - 1);
-                    }
+                    return retrieveFromScrap(mScrapViews[whichScrap], position);
                 }
             }
             return null;
@@ -4768,7 +4802,7 @@
          *
          * @param scrap The view to add
          */
-        void addScrapView(View scrap) {
+        void addScrapView(View scrap, int position) {
             AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
             if (lp == null) {
                 return;
@@ -4784,6 +4818,8 @@
                 return;
             }
 
+            lp.scrappedFromPosition = position;
+
             if (mViewTypeCount == 1) {
                 scrap.dispatchStartTemporaryDetach();
                 mCurrentScrap.add(scrap);
@@ -4810,7 +4846,9 @@
             for (int i = count - 1; i >= 0; i--) {
                 final View victim = activeViews[i];
                 if (victim != null) {
-                    int whichScrap = ((AbsListView.LayoutParams) victim.getLayoutParams()).viewType;
+                    final AbsListView.LayoutParams lp
+                            = (AbsListView.LayoutParams) victim.getLayoutParams();
+                    int whichScrap = lp.viewType;
 
                     activeViews[i] = null;
 
@@ -4826,6 +4864,7 @@
                         scrapViews = mScrapViews[whichScrap];
                     }
                     victim.dispatchStartTemporaryDetach();
+                    lp.scrappedFromPosition = mFirstActivePosition + i;
                     scrapViews.add(victim);
 
                     if (hasListener) {
@@ -4911,4 +4950,22 @@
             }
         }
     }
+
+    static View retrieveFromScrap(ArrayList<View> scrapViews, int position) {
+        int size = scrapViews.size();
+        if (size > 0) {
+            // See if we still have a view for this position.
+            for (int i=0; i<size; i++) {
+                View view = scrapViews.get(i);
+                if (((AbsListView.LayoutParams)view.getLayoutParams())
+                        .scrappedFromPosition == position) {
+                    scrapViews.remove(i);
+                    return view;
+                }
+            }
+            return scrapViews.remove(size - 1);
+        } else {
+            return null;
+        }
+    }
 }
diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java
index f5afb94..f16efbd 100644
--- a/core/java/android/widget/AdapterView.java
+++ b/core/java/android/widget/AdapterView.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.database.DataSetObserver;
-import android.os.Handler;
 import android.os.Parcelable;
 import android.os.SystemClock;
 import android.util.AttributeSet;
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 46c7d33..936a97d 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1009,7 +1009,7 @@
             childHeight = child.getMeasuredHeight();
 
             if (mRecycler.shouldRecycleViewType(p.viewType)) {
-                mRecycler.addScrapView(child);
+                mRecycler.addScrapView(child, -1);
             }
         }
         
@@ -1148,7 +1148,7 @@
 
             if (dataChanged) {
                 for (int i = 0; i < childCount; i++) {
-                    recycleBin.addScrapView(getChildAt(i));
+                    recycleBin.addScrapView(getChildAt(i), firstPosition+i);
                 }
             } else {
                 recycleBin.fillActiveViews(childCount, firstPosition);
@@ -1215,11 +1215,11 @@
             recycleBin.scrapActiveViews();
 
             if (sel != null) {
-               positionSelector(sel);
+               positionSelector(INVALID_POSITION, sel);
                mSelectedTop = sel.getTop();
             } else if (mTouchMode > TOUCH_MODE_DOWN && mTouchMode < TOUCH_MODE_SCROLL) {
                 View child = getChildAt(mMotionPosition - mFirstPosition);
-                if (child != null) positionSelector(child);                
+                if (child != null) positionSelector(mMotionPosition, child);
             } else {
                 mSelectedTop = 0;
                 mSelectorRect.setEmpty();
@@ -1391,6 +1391,11 @@
         if (mCachingStarted) {
             child.setDrawingCacheEnabled(true);
         }
+
+        if (recycled && (((AbsListView.LayoutParams)child.getLayoutParams()).scrappedFromPosition)
+                != position) {
+            child.jumpDrawablesToCurrentState();
+        }
     }
 
     /**
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index b5e103f..e0119e9 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -581,7 +581,7 @@
         final boolean scroll = scrollYDelta != 0;
         if (scroll) {
             scrollListItemsBy(-scrollYDelta);
-            positionSelector(child);
+            positionSelector(INVALID_POSITION, child);
             mSelectedTop = child.getTop();
             invalidate();
         }
@@ -1086,7 +1086,7 @@
 
             if (recycleOnMeasure() && mRecycler.shouldRecycleViewType(
                     ((LayoutParams) child.getLayoutParams()).viewType)) {
-                mRecycler.addScrapView(child);
+                mRecycler.addScrapView(child, -1);
             }
         }
 
@@ -1203,7 +1203,7 @@
             // Recycle the view before we possibly return from the method
             if (recyle && recycleBin.shouldRecycleViewType(
                     ((LayoutParams) child.getLayoutParams()).viewType)) {
-                recycleBin.addScrapView(child);
+                recycleBin.addScrapView(child, -1);
             }
 
             returnedHeight += child.getMeasuredHeight();
@@ -1507,7 +1507,7 @@
             // already cached in mHeaderViews;
             if (dataChanged) {
                 for (int i = 0; i < childCount; i++) {
-                    recycleBin.addScrapView(getChildAt(i));
+                    recycleBin.addScrapView(getChildAt(i), firstPosition+i);
                     if (ViewDebug.TRACE_RECYCLER) {
                         ViewDebug.trace(getChildAt(i),
                                 ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP, index, i);
@@ -1610,19 +1610,19 @@
                         if (focused != null) {
                             focused.clearFocus();
                         }
-                        positionSelector(sel);
+                        positionSelector(INVALID_POSITION, sel);
                     } else {
                         sel.setSelected(false);
                         mSelectorRect.setEmpty();
                     }
                 } else {
-                    positionSelector(sel);
+                    positionSelector(INVALID_POSITION, sel);
                 }
                 mSelectedTop = sel.getTop();
             } else {
                 if (mTouchMode > TOUCH_MODE_DOWN && mTouchMode < TOUCH_MODE_SCROLL) {
                     View child = getChildAt(mMotionPosition - mFirstPosition);
-                    if (child != null) positionSelector(child);
+                    if (child != null) positionSelector(mMotionPosition, child);
                 } else {
                     mSelectedTop = 0;
                     mSelectorRect.setEmpty();
@@ -1703,7 +1703,7 @@
 
 
         if (!mDataChanged) {
-            // Try to use an exsiting view for this position
+            // Try to use an existing view for this position
             child = mRecycler.getActiveView(position);
             if (child != null) {
                 if (ViewDebug.TRACE_RECYCLER) {
@@ -1820,6 +1820,11 @@
         if (mCachingStarted && !child.isDrawingCacheEnabled()) {
             child.setDrawingCacheEnabled(true);
         }
+
+        if (recycled && (((AbsListView.LayoutParams)child.getLayoutParams()).scrappedFromPosition)
+                != position) {
+            child.jumpDrawablesToCurrentState();
+        }
     }
 
     @Override
@@ -2288,6 +2293,7 @@
         }
 
         View selectedView = getSelectedView();
+        int selectedPos = mSelectedPosition;
 
         int nextSelectedPosition = lookForSelectablePositionOnScreen(direction);
         int amountToScroll = amountToScroll(direction, nextSelectedPosition);
@@ -2305,6 +2311,7 @@
             setSelectedPositionInt(nextSelectedPosition);
             setNextSelectedPositionInt(nextSelectedPosition);
             selectedView = getSelectedView();
+            selectedPos = nextSelectedPosition;
             if (mItemsCanFocus && focusResult == null) {
                 // there was no new view found to take focus, make sure we
                 // don't leave focus with the old selection
@@ -2345,7 +2352,7 @@
 
         if (needToRedraw) {
             if (selectedView != null) {
-                positionSelector(selectedView);
+                positionSelector(selectedPos, selectedView);
                 mSelectedTop = selectedView.getTop();
             }
             if (!awakenScrollBars()) {
@@ -2841,7 +2848,7 @@
                 AbsListView.LayoutParams layoutParams = (LayoutParams) first.getLayoutParams();
                 if (recycleBin.shouldRecycleViewType(layoutParams.viewType)) {
                     detachViewFromParent(first);
-                    recycleBin.addScrapView(first);
+                    recycleBin.addScrapView(first, mFirstPosition);
                 } else {
                     removeViewInLayout(first);
                 }
@@ -2872,7 +2879,7 @@
                 AbsListView.LayoutParams layoutParams = (LayoutParams) last.getLayoutParams();
                 if (recycleBin.shouldRecycleViewType(layoutParams.viewType)) {
                     detachViewFromParent(last);
-                    recycleBin.addScrapView(last);
+                    recycleBin.addScrapView(last, mFirstPosition+lastIndex);
                 } else {
                     removeViewInLayout(last);
                 }
diff --git a/core/res/res/drawable/activated_background.xml b/core/res/res/drawable/activated_background.xml
index d92fba1..1047e5b 100644
--- a/core/res/res/drawable/activated_background.xml
+++ b/core/res/res/drawable/activated_background.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:exitFadeDuration="@android:integer/config_mediumAnimTime">
     <item android:state_activated="true" android:drawable="@android:drawable/list_selector_background_selected" />
     <item android:drawable="@color/transparent" />
 </selector>
diff --git a/core/res/res/drawable/activated_background_holo_dark.xml b/core/res/res/drawable/activated_background_holo_dark.xml
index febf2c4..a29bcb9 100644
--- a/core/res/res/drawable/activated_background_holo_dark.xml
+++ b/core/res/res/drawable/activated_background_holo_dark.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:exitFadeDuration="@android:integer/config_mediumAnimTime">
     <item android:state_activated="true" android:drawable="@android:drawable/list_activated_holo" />
     <item android:drawable="@color/transparent" />
 </selector>
diff --git a/core/res/res/drawable/activated_background_holo_light.xml b/core/res/res/drawable/activated_background_holo_light.xml
index febf2c4..a29bcb9 100644
--- a/core/res/res/drawable/activated_background_holo_light.xml
+++ b/core/res/res/drawable/activated_background_holo_light.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:exitFadeDuration="@android:integer/config_mediumAnimTime">
     <item android:state_activated="true" android:drawable="@android:drawable/list_activated_holo" />
     <item android:drawable="@color/transparent" />
 </selector>
diff --git a/core/res/res/drawable/activated_background_light.xml b/core/res/res/drawable/activated_background_light.xml
index 5d5681d..7d737db 100644
--- a/core/res/res/drawable/activated_background_light.xml
+++ b/core/res/res/drawable/activated_background_light.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:exitFadeDuration="@android:integer/config_mediumAnimTime">
     <item android:state_activated="true" android:drawable="@drawable/list_selector_background_selected_light" />
     <item android:drawable="@color/transparent" />
 </selector>
diff --git a/core/res/res/drawable/list_selector_background.xml b/core/res/res/drawable/list_selector_background.xml
index 6fb0661..f5eb12d 100644
--- a/core/res/res/drawable/list_selector_background.xml
+++ b/core/res/res/drawable/list_selector_background.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:exitFadeDuration="@android:integer/config_mediumAnimTime">
 
     <item android:state_window_focused="false" android:drawable="@color/transparent" />
 
diff --git a/core/res/res/drawable/list_selector_background_light.xml b/core/res/res/drawable/list_selector_background_light.xml
index 4da7e21..50a821b 100644
--- a/core/res/res/drawable/list_selector_background_light.xml
+++ b/core/res/res/drawable/list_selector_background_light.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:exitFadeDuration="@android:integer/config_mediumAnimTime">
 
     <item android:state_window_focused="false" android:drawable="@color/transparent" />
 
diff --git a/core/res/res/drawable/list_selector_holo_dark.xml b/core/res/res/drawable/list_selector_holo_dark.xml
index e4c5c52..9a6cb89 100644
--- a/core/res/res/drawable/list_selector_holo_dark.xml
+++ b/core/res/res/drawable/list_selector_holo_dark.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:exitFadeDuration="@android:integer/config_mediumAnimTime">
 
     <item android:state_window_focused="false" android:drawable="@color/transparent" />
 
diff --git a/core/res/res/drawable/list_selector_holo_light.xml b/core/res/res/drawable/list_selector_holo_light.xml
index 17631bd..844259e 100644
--- a/core/res/res/drawable/list_selector_holo_light.xml
+++ b/core/res/res/drawable/list_selector_holo_light.xml
@@ -14,7 +14,8 @@
      limitations under the License.
 -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:exitFadeDuration="@android:integer/config_mediumAnimTime">
 
     <item android:state_window_focused="false" android:drawable="@color/transparent" />
 
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index a2fa1a3..1083452 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2786,6 +2786,10 @@
              same pixel configuration as the screen (for instance: a ARGB 8888 bitmap with
              an RGB 565 screen). -->
         <attr name="dither" format="boolean" />
+        <!-- Amount of time (in milliseconds) to fade in a new state drawable. -->
+        <attr name="enterFadeDuration" format="integer" />
+        <!-- Amount of time (in milliseconds) to fade out an old state drawable. -->
+        <attr name="exitFadeDuration" format="integer" />
     </declare-styleable>
 
     <declare-styleable name="AnimationDrawable">
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index fd7e984..f8752d3 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1358,6 +1358,8 @@
   <public type="attr" name="buttonGroupStyle" />
   <public type="attr" name="alertDialogButtonGroupStyle" />
   <public type="attr" name="homeAsUpIndicator" />
+  <public type="attr" name="enterFadeDuration" />
+  <public type="attr" name="exitFadeDuration" />
 
   <public type="anim" name="animator_fade_in" />
   <public type="anim" name="animator_fade_out" />
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 7b2d9d7..baa9d62 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -423,6 +423,13 @@
     }
 
     /**
+     * If this Drawable does transition animations between states, ask that
+     * it immediately jump to the current state and skip any active animations.
+     */
+    public void jumpToCurrentState() {
+    }
+
+    /**
      * @return The current drawable that will be used by this drawable. For simple drawables, this
      *         is just the drawable itself. For drawables that change state like
      *         {@link StateListDrawable} and {@link LevelListDrawable} this will be the child drawable
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 124d907..e55a746 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -21,6 +21,7 @@
 import android.graphics.ColorFilter;
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
+import android.os.SystemClock;
 
 /**
  * A helper class that contains several {@link Drawable}s and selects which one to use.
@@ -28,6 +29,8 @@
  * You can subclass it to create your own DrawableContainers or directly use one its child classes.
  */
 public class DrawableContainer extends Drawable implements Drawable.Callback {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "DrawableContainer";
 
     /**
      * To be proper, we should have a getter for dither (and alpha, etc.)
@@ -48,6 +51,12 @@
     private int mCurIndex = -1;
     private boolean mMutated;
 
+    // Animations.
+    private Runnable mAnimationRunnable;
+    private long mEnterAnimationEnd;
+    private long mExitAnimationEnd;
+    private Drawable mLastDrawable;
+
     // overrides from Drawable
 
     @Override
@@ -55,6 +64,9 @@
         if (mCurrDrawable != null) {
             mCurrDrawable.draw(canvas);
         }
+        if (mLastDrawable != null) {
+            mLastDrawable.draw(canvas);
+        }
     }
 
     @Override
@@ -83,7 +95,11 @@
         if (mAlpha != alpha) {
             mAlpha = alpha;
             if (mCurrDrawable != null) {
-                mCurrDrawable.setAlpha(alpha);
+                if (mEnterAnimationEnd == 0) {
+                    mCurrDrawable.setAlpha(alpha);
+                } else {
+                    animate(false);
+                }
             }
         }
     }
@@ -108,8 +124,29 @@
         }
     }
     
+    /**
+     * Change the global fade duration when a new drawable is entering
+     * the scene.
+     * @param ms The amount of time to fade in milliseconds.
+     */
+    public void setEnterFadeDuration(int ms) {
+        mDrawableContainerState.mEnterFadeDuration = ms;
+    }
+    
+    /**
+     * Change the global fade duration when a new drawable is leaving
+     * the scene.
+     * @param ms The amount of time to fade in milliseconds.
+     */
+    public void setExitFadeDuration(int ms) {
+        mDrawableContainerState.mExitFadeDuration = ms;
+    }
+    
     @Override
     protected void onBoundsChange(Rect bounds) {
+        if (mLastDrawable != null) {
+            mLastDrawable.setBounds(bounds);
+        }
         if (mCurrDrawable != null) {
             mCurrDrawable.setBounds(bounds);
         }
@@ -121,7 +158,34 @@
     }
     
     @Override
+    public void jumpToCurrentState() {
+        boolean changed = false;
+        if (mLastDrawable != null) {
+            mLastDrawable.jumpToCurrentState();
+            mLastDrawable = null;
+            changed = true;
+        }
+        if (mCurrDrawable != null) {
+            mCurrDrawable.jumpToCurrentState();
+        }
+        if (mExitAnimationEnd != 0) {
+            mExitAnimationEnd = 0;
+            changed = true;
+        }
+        if (mEnterAnimationEnd != 0) {
+            mEnterAnimationEnd = 0;
+            changed = true;
+        }
+        if (changed) {
+            invalidateSelf();
+        }
+    }
+
+    @Override
     protected boolean onStateChange(int[] state) {
+        if (mLastDrawable != null) {
+            return mLastDrawable.setState(state);
+        }
         if (mCurrDrawable != null) {
             return mCurrDrawable.setState(state);
         }
@@ -130,6 +194,9 @@
 
     @Override
     protected boolean onLevelChange(int level) {
+        if (mLastDrawable != null) {
+            return mLastDrawable.setLevel(level);
+        }
         if (mCurrDrawable != null) {
             return mCurrDrawable.setLevel(level);
         }
@@ -168,22 +235,19 @@
         return mCurrDrawable != null ? mCurrDrawable.getMinimumHeight() : 0;
     }
 
-    public void invalidateDrawable(Drawable who)
-    {
+    public void invalidateDrawable(Drawable who) {
         if (who == mCurrDrawable && mCallback != null) {
             mCallback.invalidateDrawable(this);
         }
     }
 
-    public void scheduleDrawable(Drawable who, Runnable what, long when)
-    {
+    public void scheduleDrawable(Drawable who, Runnable what, long when) {
         if (who == mCurrDrawable && mCallback != null) {
             mCallback.scheduleDrawable(this, what, when);
         }
     }
 
-    public void unscheduleDrawable(Drawable who, Runnable what)
-    {
+    public void unscheduleDrawable(Drawable who, Runnable what) {
         if (who == mCurrDrawable && mCallback != null) {
             mCallback.unscheduleDrawable(this, what);
         }
@@ -192,6 +256,9 @@
     @Override
     public boolean setVisible(boolean visible, boolean restart) {
         boolean changed = super.setVisible(visible, restart);
+        if (mLastDrawable != null) {
+            mLastDrawable.setVisible(visible, restart);
+        }
         if (mCurrDrawable != null) {
             mCurrDrawable.setVisible(visible, restart);
         }
@@ -208,16 +275,39 @@
         if (idx == mCurIndex) {
             return false;
         }
+
+        final long now = SystemClock.uptimeMillis();
+
+        if (DEBUG) android.util.Log.i(TAG, toString() + " from " + mCurIndex + " to " + idx
+                + ": exit=" + mDrawableContainerState.mExitFadeDuration
+                + " enter=" + mDrawableContainerState.mEnterFadeDuration);
+
+        if (mDrawableContainerState.mExitFadeDuration > 0) {
+            if (mLastDrawable != null) {
+                mLastDrawable.setVisible(false, false);
+            }
+            if (mCurrDrawable != null) {
+                mLastDrawable = mCurrDrawable;
+                mExitAnimationEnd = now + mDrawableContainerState.mExitFadeDuration;
+            } else {
+                mLastDrawable = null;
+                mExitAnimationEnd = 0;
+            }
+        } else if (mCurrDrawable != null) {
+            mCurrDrawable.setVisible(false, false);
+        }
+
         if (idx >= 0 && idx < mDrawableContainerState.mNumChildren) {
             Drawable d = mDrawableContainerState.mDrawables[idx];
-            if (mCurrDrawable != null) {
-                mCurrDrawable.setVisible(false, false);
-            }
             mCurrDrawable = d;
             mCurIndex = idx;
             if (d != null) {
+                if (mDrawableContainerState.mEnterFadeDuration > 0) {
+                    mEnterAnimationEnd = now + mDrawableContainerState.mEnterFadeDuration;
+                } else {
+                    d.setAlpha(mAlpha);
+                }
                 d.setVisible(isVisible(), true);
-                d.setAlpha(mAlpha);
                 d.setDither(mDrawableContainerState.mDither);
                 d.setColorFilter(mColorFilter);
                 d.setState(getState());
@@ -225,16 +315,72 @@
                 d.setBounds(getBounds());
             }
         } else {
-            if (mCurrDrawable != null) {
-                mCurrDrawable.setVisible(false, false);
-            }
             mCurrDrawable = null;
             mCurIndex = -1;
         }
+
+        if (mEnterAnimationEnd != 0 || mExitAnimationEnd != 0) {
+            if (mAnimationRunnable == null) {
+                mAnimationRunnable = new Runnable() {
+                    @Override public void run() {
+                        animate(true);
+                        invalidateSelf();
+                    }
+                };
+            } else {
+                unscheduleSelf(mAnimationRunnable);
+            }
+            // Compute first frame and schedule next animation.
+            animate(true);
+        }
+
         invalidateSelf();
+
         return true;
     }
     
+    void animate(boolean schedule) {
+        final long now = SystemClock.uptimeMillis();
+        boolean animating = false;
+        if (mCurrDrawable != null) {
+            if (mEnterAnimationEnd != 0) {
+                if (mEnterAnimationEnd <= now) {
+                    mCurrDrawable.setAlpha(mAlpha);
+                    mEnterAnimationEnd = 0;
+                } else {
+                    int animAlpha = (int)((mEnterAnimationEnd-now)*255)
+                            / mDrawableContainerState.mEnterFadeDuration;
+                    if (DEBUG) android.util.Log.i(TAG, toString() + " cur alpha " + animAlpha);
+                    mCurrDrawable.setAlpha(((255-animAlpha)*mAlpha)/255);
+                    animating = true;
+                }
+            }
+        } else {
+            mEnterAnimationEnd = 0;
+        }
+        if (mLastDrawable != null) {
+            if (mExitAnimationEnd != 0) {
+                if (mExitAnimationEnd <= now) {
+                    mLastDrawable.setVisible(false, false);
+                    mLastDrawable = null;
+                    mExitAnimationEnd = 0;
+                } else {
+                    int animAlpha = (int)((mExitAnimationEnd-now)*255)
+                            / mDrawableContainerState.mExitFadeDuration;
+                    if (DEBUG) android.util.Log.i(TAG, toString() + " last alpha " + animAlpha);
+                    mLastDrawable.setAlpha((animAlpha*mAlpha)/255);
+                    animating = true;
+                }
+            }
+        } else {
+            mExitAnimationEnd = 0;
+        }
+
+        if (schedule && animating) {
+            scheduleSelf(mAnimationRunnable, now + 1000/60);
+        }
+    }
+
     @Override
     public Drawable getCurrent() {
         return mCurrDrawable;
@@ -300,6 +446,9 @@
         
         boolean     mDither = DEFAULT_DITHER;        
 
+        int         mEnterFadeDuration;
+        int         mExitFadeDuration;
+
         DrawableContainerState(DrawableContainerState orig, DrawableContainer owner,
                 Resources res) {
             mOwner = owner;
@@ -340,6 +489,9 @@
                 
                 mDither = orig.mDither;
 
+                mEnterFadeDuration = orig.mEnterFadeDuration;
+                mExitFadeDuration = orig.mExitFadeDuration;
+
             } else {
                 mDrawables = new Drawable[10];
                 mNumChildren = 0;
@@ -476,6 +628,22 @@
             }
         }
 
+        public final void setEnterFadeDuration(int duration) {
+            mEnterFadeDuration = duration;
+        }
+
+        public final int getEnterFadeDuration() {
+            return mEnterFadeDuration;
+        }
+
+        public final void setExitFadeDuration(int duration) {
+            mExitFadeDuration = duration;
+        }
+
+        public final int getExitFadeDuration() {
+            return mExitFadeDuration;
+        }
+
         public final int getOpacity() {
             if (mHaveOpacity) {
                 return mOpacity;
diff --git a/graphics/java/android/graphics/drawable/StateListDrawable.java b/graphics/java/android/graphics/drawable/StateListDrawable.java
index 239be40..384ca81 100644
--- a/graphics/java/android/graphics/drawable/StateListDrawable.java
+++ b/graphics/java/android/graphics/drawable/StateListDrawable.java
@@ -20,6 +20,7 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
+import java.util.Arrays;
 
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -44,6 +45,7 @@
  * @attr ref android.R.styleable#DrawableStates_state_checkable
  * @attr ref android.R.styleable#DrawableStates_state_checked
  * @attr ref android.R.styleable#DrawableStates_state_selected
+ * @attr ref android.R.styleable#DrawableStates_state_activated
  * @attr ref android.R.styleable#DrawableStates_state_active
  * @attr ref android.R.styleable#DrawableStates_state_single
  * @attr ref android.R.styleable#DrawableStates_state_first
@@ -52,6 +54,9 @@
  * @attr ref android.R.styleable#DrawableStates_state_pressed
  */
 public class StateListDrawable extends DrawableContainer {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "StateListDrawable";
+
     /**
      * To be proper, we should have a getter for dither (and alpha, etc.)
      * so that proxy classes like this can save/restore their delegates'
@@ -93,6 +98,8 @@
     @Override
     protected boolean onStateChange(int[] stateSet) {
         int idx = mStateListState.indexOfStateSet(stateSet);
+        if (DEBUG) android.util.Log.i(TAG, "onStateChange " + this + " states "
+                + Arrays.toString(stateSet) + " found " + idx);
         if (idx < 0) {
             idx = mStateListState.indexOfStateSet(StateSet.WILD_CARD);
         }
@@ -117,6 +124,10 @@
                 com.android.internal.R.styleable.StateListDrawable_variablePadding, false));
         mStateListState.setConstantSize(a.getBoolean(
                 com.android.internal.R.styleable.StateListDrawable_constantSize, false));
+        mStateListState.setEnterFadeDuration(a.getInt(
+                com.android.internal.R.styleable.StateListDrawable_enterFadeDuration, 0));
+        mStateListState.setExitFadeDuration(a.getInt(
+                com.android.internal.R.styleable.StateListDrawable_exitFadeDuration, 0));
 
         setDither(a.getBoolean(com.android.internal.R.styleable.StateListDrawable_dither,
                                DEFAULT_DITHER));
@@ -251,7 +262,7 @@
     }
 
     static final class StateListState extends DrawableContainerState {
-        private int[][] mStateSets;
+        int[][] mStateSets;
 
         StateListState(StateListState orig, StateListDrawable owner, Resources res) {
             super(orig, owner, res);