Add ViewBinder & setText APIs to match SimpleCursorAdapter

There's nothing new here, just a clone of the ViewBinder API from
SimpleCursorAdapter.  This provides a much more lightweight way to
override data mapping on an item-by-item basis.

Possibly to-do, but I would rather leave for another CL:
(1) Better handling of expanded/contracted group & last child (footer).
(2) Cached info map (see SimpleCursorAdapter) for performance
diff --git a/api/current.xml b/api/current.xml
index aaaaeee..b19ac6b 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -66198,7 +66198,7 @@
  type="float"
  transient="false"
  volatile="false"
- value="0.001f"
+ value="0.0010f"
  static="true"
  final="true"
  deprecated="not deprecated"
@@ -176207,6 +176207,30 @@
 <parameter name="isExpanded" type="boolean">
 </parameter>
 </method>
+<method name="getViewBinder"
+ return="android.widget.SimpleCursorTreeAdapter.ViewBinder"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="setViewBinder"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="viewBinder" type="android.widget.SimpleCursorTreeAdapter.ViewBinder">
+</parameter>
+</method>
 <method name="setViewImage"
  return="void"
  abstract="false"
@@ -176222,7 +176246,47 @@
 <parameter name="value" type="java.lang.String">
 </parameter>
 </method>
+<method name="setViewText"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="v" type="android.widget.TextView">
+</parameter>
+<parameter name="text" type="java.lang.String">
+</parameter>
+</method>
 </class>
+<interface name="SimpleCursorTreeAdapter.ViewBinder"
+ abstract="true"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="setViewValue"
+ return="boolean"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="view" type="android.view.View">
+</parameter>
+<parameter name="cursor" type="android.database.Cursor">
+</parameter>
+<parameter name="columnIndex" type="int">
+</parameter>
+</method>
+</interface>
 <class name="SimpleExpandableListAdapter"
  extends="android.widget.BaseExpandableListAdapter"
  abstract="false"
diff --git a/core/java/android/widget/SimpleCursorTreeAdapter.java b/core/java/android/widget/SimpleCursorTreeAdapter.java
index c456f56..a1c65f0 100644
--- a/core/java/android/widget/SimpleCursorTreeAdapter.java
+++ b/core/java/android/widget/SimpleCursorTreeAdapter.java
@@ -26,9 +26,16 @@
  * defined in an XML file. You can specify which columns you want, which views
  * you want to display the columns, and the XML file that defines the appearance
  * of these views. Separate XML files for child and groups are possible.
- * TextViews bind the values to their text property (see
- * {@link TextView#setText(CharSequence)}). ImageViews bind the values to their
- * image's Uri property (see {@link ImageView#setImageURI(android.net.Uri)}).
+ *
+ * Binding occurs in two phases. First, if a
+ * {@link android.widget.SimpleCursorTreeAdapter.ViewBinder} is available,
+ * {@link ViewBinder#setViewValue(android.view.View, android.database.Cursor, int)}
+ * is invoked. If the returned value is true, binding has occurred. If the
+ * returned value is false and the view to bind is a TextView,
+ * {@link #setViewText(TextView, String)} is invoked. If the returned value
+ * is false and the view to bind is an ImageView,
+ * {@link #setViewImage(ImageView, String)} is invoked. If no appropriate
+ * binding can be found, an {@link IllegalStateException} is thrown.
  */
 public abstract class SimpleCursorTreeAdapter extends ResourceCursorTreeAdapter {
     /** The indices of columns that contain data to display for a group. */
@@ -48,6 +55,11 @@
     private int[] mChildTo;
     
     /**
+     * View binder, if supplied
+     */
+    private ViewBinder mViewBinder;
+
+    /**
      * Constructor.
      * 
      * @param context The context where the {@link ExpandableListView}
@@ -193,21 +205,53 @@
         initFromColumns(childCursor, childFromNames, mChildFrom);
     }
     
+    /**
+     * Returns the {@link ViewBinder} used to bind data to views.
+     *
+     * @return a ViewBinder or null if the binder does not exist
+     *
+     * @see #setViewBinder(android.widget.SimpleCursorTreeAdapter.ViewBinder)
+     */
+    public ViewBinder getViewBinder() {
+        return mViewBinder;
+    }
+
+    /**
+     * Sets the binder used to bind data to views.
+     *
+     * @param viewBinder the binder used to bind data to views, can be null to
+     *        remove the existing binder
+     *
+     * @see #getViewBinder()
+     */
+    public void setViewBinder(ViewBinder viewBinder) {
+        mViewBinder = viewBinder;
+    }
+
     private void bindView(View view, Context context, Cursor cursor, int[] from, int[] to) {
+        final ViewBinder binder = mViewBinder;
+        
         for (int i = 0; i < to.length; i++) {
             View v = view.findViewById(to[i]);
             if (v != null) {
-                String text = cursor.getString(from[i]);
-                if (text == null) {
-                    text = "";
+                boolean bound = false;
+                if (binder != null) {
+                    bound = binder.setViewValue(v, cursor, from[i]);
                 }
-                if (v instanceof TextView) {
-                    ((TextView) v).setText(text);
-                } else if (v instanceof ImageView) {
-                    setViewImage((ImageView) v, text);
-                } else {
-                    throw new IllegalStateException("SimpleCursorAdapter can bind values only to" +
-                            " TextView and ImageView!");
+                
+                if (!bound) {
+                    String text = cursor.getString(from[i]);
+                    if (text == null) {
+                        text = "";
+                    }
+                    if (v instanceof TextView) {
+                        setViewText((TextView) v, text);
+                    } else if (v instanceof ImageView) {
+                        setViewImage((ImageView) v, text);
+                    } else {
+                        throw new IllegalStateException("SimpleCursorTreeAdapter can bind values" +
+                                " only to TextView and ImageView!");
+                    }
                 }
             }
         }
@@ -238,4 +282,48 @@
             v.setImageURI(Uri.parse(value));
         }
     }
+
+    /**
+     * Called by bindView() to set the text for a TextView but only if
+     * there is no existing ViewBinder or if the existing ViewBinder cannot
+     * handle binding to an TextView.
+     *
+     * Intended to be overridden by Adapters that need to filter strings
+     * retrieved from the database.
+     * 
+     * @param v TextView to receive text
+     * @param text the text to be set for the TextView
+     */
+    public void setViewText(TextView v, String text) {
+        v.setText(text);
+    }
+
+    /**
+     * This class can be used by external clients of SimpleCursorTreeAdapter
+     * to bind values from the Cursor to views.
+     *
+     * You should use this class to bind values from the Cursor to views
+     * that are not directly supported by SimpleCursorTreeAdapter or to
+     * change the way binding occurs for views supported by
+     * SimpleCursorTreeAdapter.
+     *
+     * @see SimpleCursorTreeAdapter#setViewImage(ImageView, String) 
+     * @see SimpleCursorTreeAdapter#setViewText(TextView, String)
+     */
+    public static interface ViewBinder {
+        /**
+         * Binds the Cursor column defined by the specified index to the specified view.
+         *
+         * When binding is handled by this ViewBinder, this method must return true.
+         * If this method returns false, SimpleCursorTreeAdapter will attempts to handle
+         * the binding on its own.
+         *
+         * @param view the view to bind the data to
+         * @param cursor the cursor to get the data from
+         * @param columnIndex the column at which the data can be found in the cursor
+         *
+         * @return true if the data was bound to the view, false otherwise
+         */
+        boolean setViewValue(View view, Cursor cursor, int columnIndex);
+    }
 }