Fix FastScroller regression for non-UI threads

Previously it was safe to call setFastScrollEnabled() and
setFastScrollAlwaysVisible() off the UI thread. After switching
FastScroller to use an Overlay, these methods stopped working.

This change ensures that all direct interaction with FastScroller
happens on the thread that created the host AbsListView.

BUG: 10210504
Change-Id: Ib6d9bd9212965420c0de39546652e3bc2d32ff8b
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 2d7637c..e7227e3 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -223,6 +223,11 @@
     public static final int CHOICE_MODE_MULTIPLE_MODAL = 3;
 
     /**
+     * The thread that created this view.
+     */
+    private final Thread mOwnerThread;
+
+    /**
      * Controls if/how the user may choose/check items in the list
      */
     int mChoiceMode = CHOICE_MODE_NONE;
@@ -438,6 +443,11 @@
     boolean mFastScrollEnabled;
 
     /**
+     * Whether or not to always show the fast scroll feature on this list
+     */
+    boolean mFastScrollAlwaysVisible;
+
+    /**
      * Optional callback to notify client when scroll position has changed
      */
     private OnScrollListener mOnScrollListener;
@@ -756,6 +766,8 @@
         super(context);
         initAbsListView();
 
+        mOwnerThread = Thread.currentThread();
+
         setVerticalScrollBarEnabled(true);
         TypedArray a = context.obtainStyledAttributes(R.styleable.View);
         initializeScrollbars(a);
@@ -770,6 +782,8 @@
         super(context, attrs, defStyle);
         initAbsListView();
 
+        mOwnerThread = Thread.currentThread();
+
         TypedArray a = context.obtainStyledAttributes(attrs,
                 com.android.internal.R.styleable.AbsListView, defStyle, 0);
 
@@ -1205,15 +1219,28 @@
      * @see #isFastScrollEnabled()
      * @param enabled whether or not to enable fast scrolling
      */
-    public void setFastScrollEnabled(boolean enabled) {
-        mFastScrollEnabled = enabled;
+    public void setFastScrollEnabled(final boolean enabled) {
+        if (mFastScrollEnabled != enabled) {
+            mFastScrollEnabled = enabled;
 
-        if (enabled && mFastScroller == null) {
-            mFastScroller = new FastScroller(getContext(), this);
+            if (isOwnerThread()) {
+                setFastScrollerEnabledUiThread(enabled);
+            } else {
+                post(new Runnable() {
+                    @Override
+                    public void run() {
+                        setFastScrollerEnabledUiThread(enabled);
+                    }
+                });
+            }
         }
+    }
 
+    private void setFastScrollerEnabledUiThread(boolean enabled) {
         if (mFastScroller != null) {
             mFastScroller.setEnabled(enabled);
+        } else if (enabled) {
+            mFastScroller = new FastScroller(this);
         }
     }
 
@@ -1228,17 +1255,38 @@
      * @see #setScrollBarStyle(int)
      * @see #setFastScrollEnabled(boolean)
      */
-    public void setFastScrollAlwaysVisible(boolean alwaysShow) {
-        if (alwaysShow && !mFastScrollEnabled) {
-            setFastScrollEnabled(true);
-        }
+    public void setFastScrollAlwaysVisible(final boolean alwaysShow) {
+        if (mFastScrollAlwaysVisible != alwaysShow) {
+            if (alwaysShow && !mFastScrollEnabled) {
+                setFastScrollEnabled(true);
+            }
 
+            mFastScrollAlwaysVisible = alwaysShow;
+
+            if (isOwnerThread()) {
+                setFastScrollerAlwaysVisibleUiThread(alwaysShow);
+            } else {
+                post(new Runnable() {
+                    @Override
+                    public void run() {
+                        setFastScrollerAlwaysVisibleUiThread(alwaysShow);
+                    }
+                });
+            }
+        }
+    }
+
+    private void setFastScrollerAlwaysVisibleUiThread(boolean alwaysShow) {
         if (mFastScroller != null) {
             mFastScroller.setAlwaysShow(alwaysShow);
         }
+    }
 
-        computeOpaqueFlags();
-        recomputePadding();
+    /**
+     * @return whether the current thread is the one that created the view
+     */
+    private boolean isOwnerThread() {
+        return mOwnerThread == Thread.currentThread();
     }
 
     /**
@@ -1249,12 +1297,12 @@
      * @see #setFastScrollAlwaysVisible(boolean)
      */
     public boolean isFastScrollAlwaysVisible() {
-        return mFastScrollEnabled && mFastScroller.isAlwaysShowEnabled();
+        return mFastScrollEnabled && mFastScrollAlwaysVisible;
     }
 
     @Override
     public int getVerticalScrollbarWidth() {
-        if (isFastScrollAlwaysVisible()) {
+        if (isFastScrollAlwaysVisible() && mFastScroller != null) {
             return Math.max(super.getVerticalScrollbarWidth(), mFastScroller.getWidth());
         }
         return super.getVerticalScrollbarWidth();
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index d08e38e..b3b00b1 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -231,10 +231,11 @@
         }
     };
 
-    public FastScroller(Context context, AbsListView listView) {
+    public FastScroller(AbsListView listView) {
         mList = listView;
         mOverlay = listView.getOverlay();
 
+        final Context context = listView.getContext();
         mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
 
         final Resources res = context.getResources();