New HeterogeneousExpandableList interface.

This interface can be used in conjunction with ExpandableListAdapter to
declare more types of child and/or group views.

None of the ExpandableListAdapter implementations is heterogeneous in the
framework. BaseExpandableListAdapter was decalred to use this interface so that users
see the methods and can overload. DateSortedExpandableListAdapter was left unchanged.

This feature is related to http://b/issue?id=1459940

Change-Id: Ifc589b697913778b16abfdcaaa9f8f81e564add7
diff --git a/api/current.xml b/api/current.xml
index 64f0c23..cd11b1d 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -199698,6 +199698,8 @@
 >
 <implements name="android.widget.ExpandableListAdapter">
 </implements>
+<implements name="android.widget.HeterogeneousExpandableList">
+</implements>
 <constructor name="BaseExpandableListAdapter"
  type="android.widget.BaseExpandableListAdapter"
  static="false"
@@ -199717,6 +199719,32 @@
  visibility="public"
 >
 </method>
+<method name="getChildType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="groupPosition" type="int">
+</parameter>
+<parameter name="childPosition" type="int">
+</parameter>
+</method>
+<method name="getChildTypeCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="getCombinedChildId"
  return="long"
  abstract="false"
@@ -199745,6 +199773,30 @@
 <parameter name="groupId" type="long">
 </parameter>
 </method>
+<method name="getGroupType"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="groupPosition" type="int">
+</parameter>
+</method>
+<method name="getGroupTypeCount"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
 <method name="isEmpty"
  return="boolean"
  abstract="false"
@@ -203487,6 +203539,64 @@
 </parameter>
 </method>
 </class>
+<interface name="HeterogeneousExpandableList"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getChildType"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="groupPosition" type="int">
+</parameter>
+<parameter name="childPosition" type="int">
+</parameter>
+</method>
+<method name="getChildTypeCount"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="getGroupType"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="groupPosition" type="int">
+</parameter>
+</method>
+<method name="getGroupTypeCount"
+ return="int"
+ abstract="true"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</interface>
 <class name="HorizontalScrollView"
  extends="android.widget.FrameLayout"
  abstract="false"
diff --git a/core/java/android/widget/BaseExpandableListAdapter.java b/core/java/android/widget/BaseExpandableListAdapter.java
index 1bba7f0..396b7ae 100644
--- a/core/java/android/widget/BaseExpandableListAdapter.java
+++ b/core/java/android/widget/BaseExpandableListAdapter.java
@@ -18,7 +18,6 @@
 
 import android.database.DataSetObservable;
 import android.database.DataSetObserver;
-import android.view.KeyEvent;
 
 /**
  * Base class for a {@link ExpandableListAdapter} used to provide data and Views
@@ -31,7 +30,8 @@
  * @see SimpleExpandableListAdapter
  * @see SimpleCursorTreeAdapter
  */
-public abstract class BaseExpandableListAdapter implements ExpandableListAdapter {
+public abstract class BaseExpandableListAdapter implements ExpandableListAdapter, 
+        HeterogeneousExpandableList {
     private final DataSetObservable mDataSetObservable = new DataSetObservable();
     
     public void registerDataSetObserver(DataSetObserver observer) {
@@ -102,5 +102,37 @@
     public boolean isEmpty() {
         return getGroupCount() == 0;
     }
-    
+
+
+    /**
+     * {@inheritDoc}
+     * @return 0 for any group or child position, since only one child type count is declared.
+     */
+    public int getChildType(int groupPosition, int childPosition) {
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return 1 as a default value in BaseExpandableListAdapter.
+     */
+    public int getChildTypeCount() {
+        return 1;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return 0 for any groupPosition, since only one group type count is declared.
+     */
+    public int getGroupType(int groupPosition) {
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * @return 1 as a default value in BaseExpandableListAdapter.
+     */
+    public int getGroupTypeCount() {
+        return 1;
+    }
 }
diff --git a/core/java/android/widget/ExpandableListAdapter.java b/core/java/android/widget/ExpandableListAdapter.java
index b75983ce..7f6781b 100644
--- a/core/java/android/widget/ExpandableListAdapter.java
+++ b/core/java/android/widget/ExpandableListAdapter.java
@@ -17,7 +17,6 @@
 package android.widget;
 
 import android.database.DataSetObserver;
-import android.view.KeyEvent;
 import android.view.View;
 import android.view.ViewGroup;
 
@@ -108,7 +107,7 @@
     /**
      * Gets a View that displays the given group. This View is only for the
      * group--the Views for the group's children will be fetched using
-     * getChildrenView.
+     * {@link #getChildView(int, int, boolean, View, ViewGroup)}.
      * 
      * @param groupPosition the position of the group for which the View is
      *            returned
diff --git a/core/java/android/widget/ExpandableListConnector.java b/core/java/android/widget/ExpandableListConnector.java
index 01d3a4a..2ff6b70 100644
--- a/core/java/android/widget/ExpandableListConnector.java
+++ b/core/java/android/widget/ExpandableListConnector.java
@@ -442,8 +442,8 @@
 
         View retValue;
         if (posMetadata.position.type == ExpandableListPosition.GROUP) {
-            retValue = mExpandableListAdapter.getGroupView(posMetadata.position.groupPos, posMetadata
-                    .isExpanded(), convertView, parent);
+            retValue = mExpandableListAdapter.getGroupView(posMetadata.position.groupPos,
+                    posMetadata.isExpanded(), convertView, parent);
         } else if (posMetadata.position.type == ExpandableListPosition.CHILD) {
             final boolean isLastChild = posMetadata.groupMetadata.lastChildFlPos == flatListPos;
             
@@ -464,10 +464,21 @@
         final ExpandableListPosition pos = getUnflattenedPos(flatListPos).position;
 
         int retValue;
-        if (pos.type == ExpandableListPosition.GROUP) {
-            retValue = 0;
+        if (mExpandableListAdapter instanceof HeterogeneousExpandableList) {
+            HeterogeneousExpandableList adapter =
+                    (HeterogeneousExpandableList) mExpandableListAdapter;
+            if (pos.type == ExpandableListPosition.GROUP) {
+                retValue = adapter.getGroupType(pos.groupPos);
+            } else {
+                final int childType = adapter.getChildType(pos.groupPos, pos.childPos);
+                retValue = adapter.getGroupTypeCount() + childType;
+            }
         } else {
-            retValue = 1;
+            if (pos.type == ExpandableListPosition.GROUP) {
+                retValue = 0;
+            } else {
+                retValue = 1;
+            }
         }
         
         pos.recycle();
@@ -477,7 +488,13 @@
 
     @Override
     public int getViewTypeCount() {
-        return 2;
+        if (mExpandableListAdapter instanceof HeterogeneousExpandableList) {
+            HeterogeneousExpandableList adapter =
+                    (HeterogeneousExpandableList) mExpandableListAdapter;
+            return adapter.getGroupTypeCount() + adapter.getChildTypeCount();
+        } else {
+            return 2;
+        }
     }
     
     @Override
diff --git a/core/java/android/widget/HeterogeneousExpandableList.java b/core/java/android/widget/HeterogeneousExpandableList.java
new file mode 100644
index 0000000..1292733
--- /dev/null
+++ b/core/java/android/widget/HeterogeneousExpandableList.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.widget;
+
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Additional methods that when implemented make an
+ * {@link ExpandableListAdapter} take advantage of the {@link Adapter} view type
+ * mechanism.
+ * 
+ * An {@link ExpandableListAdapter} declares one view type for its group items
+ * and one view type for its child items. Although adapted for most {@link ExpandableListView}s,
+ * these values should be tuned heterogeneous {@link ExpandableListView}s. Lists that contain
+ * different types of group and/or child item views, should use an adapter that implements this
+ * interface. This way, the recycled views that will be provided to
+ * {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+ * and
+ * {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+ * will be of the appropriate group or child type, resulting in a more efficient reuse of the
+ * previously created views.
+ */
+public interface HeterogeneousExpandableList {
+    /**
+     * Get the type of group View that will be created by
+     * {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+     * . for the specified group item.
+     * 
+     * @param groupPosition the position of the group for which the type should be returned.
+     * @return An integer representing the type of group View. Two group views should share the same
+     *         type if one can be converted to the other in
+     *         {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+     *         . Note: Integers must be in the range 0 to {@link #getGroupTypeCount} - 1.
+     *         {@link android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE} can also be returned.
+     * @see android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE
+     * @see getGroupTypeCount()
+     */
+    int getGroupType(int groupPosition);
+
+    /**
+     * Get the type of child View that will be created by
+     * {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+     * for the specified child item.
+     * 
+     * @param groupPosition the position of the group that the child resides in
+     * @param childPosition the position of the child with respect to other children in the group
+     * @return An integer representing the type of child View. Two child views should share the same
+     *         type if one can be converted to the other in
+     *         {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+     *         Note: Integers must be in the range 0 to {@link #getChildTypeCount} - 1.
+     *         {@link android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE} can also be returned.
+     * @see android.widget.Adapter#IGNORE_ITEM_VIEW_TYPE
+     * @see getChildTypeCount()
+     */
+    int getChildType(int groupPosition, int childPosition);
+
+    /**
+     * <p>
+     * Returns the number of types of group Views that will be created by
+     * {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+     * . Each type represents a set of views that can be converted in
+     * {@link android.widget.ExpandableListAdapter#getGroupView(int, boolean, View, ViewGroup)}
+     * . If the adapter always returns the same type of View for all group items, this method should
+     * return 1.
+     * </p>
+     * <p>
+     * This method will only be called when the adapter is set on the {@link AdapterView}.
+     * </p>
+     * 
+     * @return The number of types of group Views that will be created by this adapter.
+     * @see getChildTypeCount()
+     * @see getGroupType()
+     */
+    int getGroupTypeCount();
+
+    /**
+     * <p>
+     * Returns the number of types of child Views that will be created by
+     * {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+     * . Each type represents a set of views that can be converted in
+     * {@link android.widget.ExpandableListAdapter#getChildView(int, int, boolean, View, ViewGroup)}
+     * , for any group. If the adapter always returns the same type of View for
+     * all child items, this method should return 1.
+     * </p>
+     * <p>
+     * This method will only be called when the adapter is set on the {@link AdapterView}.
+     * </p>
+     * 
+     * @return The total number of types of child Views that will be created by this adapter.
+     * @see getGroupTypeCount()
+     * @see getChildType()
+     */
+    int getChildTypeCount();
+}