18b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen/*
28b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * Copyright (C) 2015 The Android Open Source Project
38b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen *
48b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * Licensed under the Apache License, Version 2.0 (the "License");
58b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * you may not use this file except in compliance with the License.
68b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * You may obtain a copy of the License at
78b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen *
88b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen *      http://www.apache.org/licenses/LICENSE-2.0
98b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen *
108b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * Unless required by applicable law or agreed to in writing, software
118b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * distributed under the License is distributed on an "AS IS" BASIS,
128b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * See the License for the specific language governing permissions and
148b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * limitations under the License.
158b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen */
168b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenpackage android.support.car.ui;
178b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
188b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport android.content.Context;
198b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport android.support.v7.widget.RecyclerView;
208b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport android.util.Log;
218b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport android.view.ViewGroup;
228b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
238b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport java.util.ArrayList;
248b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenimport java.util.List;
258b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
268b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen/**
278b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * Maintains a list that groups adjacent items sharing the same value of
288b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * a "group-by" field.  The list has three types of elements: stand-alone, group header and group
298b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen * child. Groups are collapsible and collapsed by default.
308b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen */
318b7af7188228ca88838e31e070f0fca0d1f90581Yao Chenpublic abstract class GroupingRecyclerViewAdapter<E, VH extends RecyclerView.ViewHolder>
328b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        extends RecyclerView.Adapter<VH> {
338b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private static final String TAG = "CAR.UI.GroupingRecyclerViewAdapter";
348b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
358b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public static final int VIEW_TYPE_STANDALONE = 0;
368b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public static final int VIEW_TYPE_GROUP_HEADER = 1;
378b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public static final int VIEW_TYPE_IN_GROUP = 2;
388b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
398b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
408b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Build all groups based on grouping rules given cursor and calls {@link #addGroup} for
418b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * each of them.
428b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
438b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    protected abstract void buildGroups(List<E> data);
448b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
458b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    protected abstract VH onCreateStandAloneViewHolder(Context context, ViewGroup parent);
468b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    protected abstract void onBindStandAloneViewHolder(
478b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            VH holder, Context context, int positionInData);
488b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
498b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    protected abstract VH onCreateGroupViewHolder(Context context, ViewGroup parent);
508b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    protected abstract void onBindGroupViewHolder(VH holder, Context context, int positionInData,
518b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                                                  int groupSize, boolean expanded);
528b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
538b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    protected abstract VH onCreateChildViewHolder(Context context, ViewGroup parent);
548b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    protected abstract void onBindChildViewHolder(VH holder, Context context, int positionInData);
558b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
568b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    protected Context mContext;
578b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    protected List<E> mData;
588b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
598b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private int mCount;
608b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private List<GroupMetadata> mGroupMetadata;
618b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
628b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public GroupingRecyclerViewAdapter(Context context) {
638b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mContext = context;
648b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mGroupMetadata = new ArrayList<>();
658b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        resetGroup();
668b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
678b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
688b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public void setData(List<E> data) {
698b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mData = data;
708b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        resetGroup();
718b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (mData != null) {
728b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            buildGroups(mData);
738b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            rebuildGroupMetadata();
748b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
758b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        notifyDataSetChanged();
768b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
778b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
788b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Override
798b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public int getItemCount() {
808b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (mData != null && mCount != -1) {
818b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return mCount;
828b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
838b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return 0;
848b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
858b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
868b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Override
878b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public long getItemId(int position) {
888b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        E item = getItem(position);
898b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (item != null) {
908b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return item.hashCode();
918b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
928b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return 0;
938b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
948b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
958b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Override
968b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public int getItemViewType(int position) {
978b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return getPositionMetadata(position).itemType;
988b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
998b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1008b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public E getItem(int position) {
1018b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (mData == null) {
1028b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return null;
1038b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
1048b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1058b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        PositionMetadata pMetadata = getPositionMetadata(position);
1068b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return mData.get(pMetadata.positionInData);
1078b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
1088b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1098b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Override
1108b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public VH onCreateViewHolder(ViewGroup parent, int viewType) {
1118b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        switch (viewType) {
1128b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            case VIEW_TYPE_STANDALONE:
1138b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return onCreateStandAloneViewHolder(mContext, parent);
1148b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            case VIEW_TYPE_GROUP_HEADER:
1158b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return onCreateGroupViewHolder(mContext, parent);
1168b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            case VIEW_TYPE_IN_GROUP:
1178b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return onCreateChildViewHolder(mContext, parent);
1188b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
1198b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        Log.e(TAG, "Unknown viewType. Returning null ViewHolder");
1208b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return null;
1218b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
1228b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1238b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    @Override
1248b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public void onBindViewHolder(VH holder, int position) {
1258b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        PositionMetadata pMetadata = getPositionMetadata(position);
1268b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        switch (holder.getItemViewType()) {
1278b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            case VIEW_TYPE_STANDALONE:
1288b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                onBindStandAloneViewHolder(holder, mContext, pMetadata.positionInData);
1298b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                break;
1308b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            case VIEW_TYPE_GROUP_HEADER:
1318b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                onBindGroupViewHolder(holder, mContext, pMetadata.positionInData,
1328b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                        pMetadata.gMetadata.itemNumber, pMetadata.gMetadata.isExpanded());
1338b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                break;
1348b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            case VIEW_TYPE_IN_GROUP:
1358b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                onBindChildViewHolder(holder, mContext, pMetadata.positionInData);
1368b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                break;
1378b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
1388b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
1398b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1408b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public boolean toggleGroup(int positionInData, int positionOnUI) {
1418b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        PositionMetadata pMetadata = getPositionMetadata(positionInData);
1428b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (pMetadata.itemType != VIEW_TYPE_GROUP_HEADER) {
1438b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return false;
1448b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
1458b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1468b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        pMetadata.gMetadata.isExpanded = !pMetadata.gMetadata.isExpanded;
1478b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        rebuildGroupMetadata();
1488b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (pMetadata.gMetadata.isExpanded) {
1498b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            notifyItemRangeInserted(positionOnUI + 1, pMetadata.gMetadata.itemNumber);
1508b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        } else {
1518b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            notifyItemRangeRemoved(positionOnUI + 1, pMetadata.gMetadata.itemNumber);
1528b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
1538b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return true;
1548b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
1558b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1568b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
1578b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Return True if the item on the given position is a group header and the group is expanded,
1588b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * otherwise False.
1598b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
1608b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    public boolean isGroupExpanded(int position) {
1618b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        PositionMetadata pMetadata = getPositionMetadata(position);
1628b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        if (pMetadata.itemType != VIEW_TYPE_GROUP_HEADER) {
1638b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return false;
1648b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
1658b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1668b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        return pMetadata.gMetadata.isExpanded();
1678b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
1688b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1698b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
1708b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Records information about grouping in the list. Should only be called by the overridden
1718b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * {@link #buildGroups} method.
1728b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
1738b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    protected void addGroup(int offset, int size, boolean expanded) {
1748b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mGroupMetadata.add(GroupMetadata.obtain(offset, size, expanded));
1758b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
1768b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1778b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private void resetGroup() {
1788b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mCount = -1;
1798b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mGroupMetadata.clear();
1808b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
1818b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1828b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private void rebuildGroupMetadata() {
1838b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int currentPos = 0;
1848b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        for (int groupIndex = 0; groupIndex < mGroupMetadata.size(); groupIndex++) {
1858b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            GroupMetadata gMetadata = mGroupMetadata.get(groupIndex);
1868b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            gMetadata.offsetInDisplayList = currentPos;
1878b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            currentPos += gMetadata.getActualSize();
1888b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
1898b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        mCount = currentPos;
1908b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
1918b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1928b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    private PositionMetadata getPositionMetadata(int position) {
1938b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int left = 0;
1948b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int right = mGroupMetadata.size() - 1;
1958b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int mid;
1968b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        GroupMetadata midItem;
1978b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
1988b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        while (left <= right) {
1998b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            mid = (right - left) / 2 + left;
2008b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            midItem = mGroupMetadata.get(mid);
2018b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
2028b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (position > midItem.offsetInDisplayList + midItem.getActualSize() - 1) {
2038b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                left = mid + 1;
2048b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                continue;
2058b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
2068b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
2078b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (position < midItem.offsetInDisplayList) {
2088b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                right = mid - 1;
2098b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                continue;
2108b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
2118b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
2128b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int cursorOffset = midItem.offsetInDataList + (position - midItem.offsetInDisplayList);
2138b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            int viewType;
2148b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (midItem.offsetInDisplayList == position) {
2158b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                if (midItem.isStandAlone()) {
2168b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    viewType = VIEW_TYPE_STANDALONE;
2178b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                } else {
2188b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                    viewType = VIEW_TYPE_GROUP_HEADER;
2198b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                }
2208b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            } else {
2218b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                viewType = VIEW_TYPE_IN_GROUP;
2228b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                // Offset cursorOffset by 1, because the group_header and the first child
2238b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                // will share the same cursor.
2248b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                cursorOffset--;
2258b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
2268b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return new PositionMetadata(viewType, cursorOffset, midItem);
2278b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
2288b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
2298b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        throw new IllegalStateException(
2308b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                "illegal position " + position + ", total size is " + mCount);
2318b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
2328b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
2338b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    /**
2348b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * Information about where groups are located in the list, how large they are
2358b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     * and whether they are expanded.
2368b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen     */
2378b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    protected static class GroupMetadata {
2388b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        private int offsetInDisplayList;
2398b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        private int offsetInDataList;
2408b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        private int itemNumber;
2418b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        private boolean isExpanded;
2428b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
2438b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        static GroupMetadata obtain(int offset, int itemNumber, boolean isExpanded) {
2448b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            GroupMetadata gm = new GroupMetadata();
2458b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            gm.offsetInDataList = offset;
2468b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            gm.itemNumber = itemNumber;
2478b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            gm.isExpanded = isExpanded;
2488b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return gm;
2498b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
2508b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
2518b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        public boolean isExpanded() {
2528b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return !isStandAlone() && isExpanded;
2538b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
2548b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
2558b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        public boolean isStandAlone() {
2568b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            return itemNumber == 1;
2578b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
2588b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
2598b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        public int getActualSize() {
2608b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            if (!isExpanded()) {
2618b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return 1;
2628b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            } else {
2638b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen                return itemNumber + 1;
2648b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            }
2658b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
2668b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
2678b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
2688b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    protected static class PositionMetadata {
2698b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int itemType;
2708b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        int positionInData;
2718b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        GroupMetadata gMetadata;
2728b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen
2738b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        public PositionMetadata(int itemType, int positionInData, GroupMetadata gMetadata) {
2748b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            this.itemType = itemType;
2758b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            this.positionInData = positionInData;
2768b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen            this.gMetadata = gMetadata;
2778b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen        }
2788b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen    }
2798b7af7188228ca88838e31e070f0fca0d1f90581Yao Chen}
280