1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.widget;
18
19import android.database.DataSetObserver;
20import android.view.View;
21import android.view.ViewGroup;
22
23import java.util.ArrayList;
24
25/**
26 * ListAdapter used when a ListView has header views. This ListAdapter
27 * wraps another one and also keeps track of the header views and their
28 * associated data objects.
29 *<p>This is intended as a base class; you will probably not need to
30 * use this class directly in your own code.
31 */
32public class HeaderViewListAdapter implements WrapperListAdapter, Filterable {
33
34    private final ListAdapter mAdapter;
35
36    // These two ArrayList are assumed to NOT be null.
37    // They are indeed created when declared in ListView and then shared.
38    ArrayList<ListView.FixedViewInfo> mHeaderViewInfos;
39    ArrayList<ListView.FixedViewInfo> mFooterViewInfos;
40
41    // Used as a placeholder in case the provided info views are indeed null.
42    // Currently only used by some CTS tests, which may be removed.
43    static final ArrayList<ListView.FixedViewInfo> EMPTY_INFO_LIST =
44        new ArrayList<ListView.FixedViewInfo>();
45
46    boolean mAreAllFixedViewsSelectable;
47
48    private final boolean mIsFilterable;
49
50    public HeaderViewListAdapter(ArrayList<ListView.FixedViewInfo> headerViewInfos,
51                                 ArrayList<ListView.FixedViewInfo> footerViewInfos,
52                                 ListAdapter adapter) {
53        mAdapter = adapter;
54        mIsFilterable = adapter instanceof Filterable;
55
56        if (headerViewInfos == null) {
57            mHeaderViewInfos = EMPTY_INFO_LIST;
58        } else {
59            mHeaderViewInfos = headerViewInfos;
60        }
61
62        if (footerViewInfos == null) {
63            mFooterViewInfos = EMPTY_INFO_LIST;
64        } else {
65            mFooterViewInfos = footerViewInfos;
66        }
67
68        mAreAllFixedViewsSelectable =
69                areAllListInfosSelectable(mHeaderViewInfos)
70                && areAllListInfosSelectable(mFooterViewInfos);
71    }
72
73    public int getHeadersCount() {
74        return mHeaderViewInfos.size();
75    }
76
77    public int getFootersCount() {
78        return mFooterViewInfos.size();
79    }
80
81    public boolean isEmpty() {
82        return mAdapter == null || mAdapter.isEmpty();
83    }
84
85    private boolean areAllListInfosSelectable(ArrayList<ListView.FixedViewInfo> infos) {
86        if (infos != null) {
87            for (ListView.FixedViewInfo info : infos) {
88                if (!info.isSelectable) {
89                    return false;
90                }
91            }
92        }
93        return true;
94    }
95
96    public boolean removeHeader(View v) {
97        for (int i = 0; i < mHeaderViewInfos.size(); i++) {
98            ListView.FixedViewInfo info = mHeaderViewInfos.get(i);
99            if (info.view == v) {
100                mHeaderViewInfos.remove(i);
101
102                mAreAllFixedViewsSelectable =
103                        areAllListInfosSelectable(mHeaderViewInfos)
104                        && areAllListInfosSelectable(mFooterViewInfos);
105
106                return true;
107            }
108        }
109
110        return false;
111    }
112
113    public boolean removeFooter(View v) {
114        for (int i = 0; i < mFooterViewInfos.size(); i++) {
115            ListView.FixedViewInfo info = mFooterViewInfos.get(i);
116            if (info.view == v) {
117                mFooterViewInfos.remove(i);
118
119                mAreAllFixedViewsSelectable =
120                        areAllListInfosSelectable(mHeaderViewInfos)
121                        && areAllListInfosSelectable(mFooterViewInfos);
122
123                return true;
124            }
125        }
126
127        return false;
128    }
129
130    public int getCount() {
131        if (mAdapter != null) {
132            return getFootersCount() + getHeadersCount() + mAdapter.getCount();
133        } else {
134            return getFootersCount() + getHeadersCount();
135        }
136    }
137
138    public boolean areAllItemsEnabled() {
139        if (mAdapter != null) {
140            return mAreAllFixedViewsSelectable && mAdapter.areAllItemsEnabled();
141        } else {
142            return true;
143        }
144    }
145
146    public boolean isEnabled(int position) {
147        // Header (negative positions will throw an IndexOutOfBoundsException)
148        int numHeaders = getHeadersCount();
149        if (position < numHeaders) {
150            return mHeaderViewInfos.get(position).isSelectable;
151        }
152
153        // Adapter
154        final int adjPosition = position - numHeaders;
155        int adapterCount = 0;
156        if (mAdapter != null) {
157            adapterCount = mAdapter.getCount();
158            if (adjPosition < adapterCount) {
159                return mAdapter.isEnabled(adjPosition);
160            }
161        }
162
163        // Footer (off-limits positions will throw an IndexOutOfBoundsException)
164        return mFooterViewInfos.get(adjPosition - adapterCount).isSelectable;
165    }
166
167    public Object getItem(int position) {
168        // Header (negative positions will throw an IndexOutOfBoundsException)
169        int numHeaders = getHeadersCount();
170        if (position < numHeaders) {
171            return mHeaderViewInfos.get(position).data;
172        }
173
174        // Adapter
175        final int adjPosition = position - numHeaders;
176        int adapterCount = 0;
177        if (mAdapter != null) {
178            adapterCount = mAdapter.getCount();
179            if (adjPosition < adapterCount) {
180                return mAdapter.getItem(adjPosition);
181            }
182        }
183
184        // Footer (off-limits positions will throw an IndexOutOfBoundsException)
185        return mFooterViewInfos.get(adjPosition - adapterCount).data;
186    }
187
188    public long getItemId(int position) {
189        int numHeaders = getHeadersCount();
190        if (mAdapter != null && position >= numHeaders) {
191            int adjPosition = position - numHeaders;
192            int adapterCount = mAdapter.getCount();
193            if (adjPosition < adapterCount) {
194                return mAdapter.getItemId(adjPosition);
195            }
196        }
197        return -1;
198    }
199
200    public boolean hasStableIds() {
201        if (mAdapter != null) {
202            return mAdapter.hasStableIds();
203        }
204        return false;
205    }
206
207    public View getView(int position, View convertView, ViewGroup parent) {
208        // Header (negative positions will throw an IndexOutOfBoundsException)
209        int numHeaders = getHeadersCount();
210        if (position < numHeaders) {
211            return mHeaderViewInfos.get(position).view;
212        }
213
214        // Adapter
215        final int adjPosition = position - numHeaders;
216        int adapterCount = 0;
217        if (mAdapter != null) {
218            adapterCount = mAdapter.getCount();
219            if (adjPosition < adapterCount) {
220                return mAdapter.getView(adjPosition, convertView, parent);
221            }
222        }
223
224        // Footer (off-limits positions will throw an IndexOutOfBoundsException)
225        return mFooterViewInfos.get(adjPosition - adapterCount).view;
226    }
227
228    public int getItemViewType(int position) {
229        int numHeaders = getHeadersCount();
230        if (mAdapter != null && position >= numHeaders) {
231            int adjPosition = position - numHeaders;
232            int adapterCount = mAdapter.getCount();
233            if (adjPosition < adapterCount) {
234                return mAdapter.getItemViewType(adjPosition);
235            }
236        }
237
238        return AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER;
239    }
240
241    public int getViewTypeCount() {
242        if (mAdapter != null) {
243            return mAdapter.getViewTypeCount();
244        }
245        return 1;
246    }
247
248    public void registerDataSetObserver(DataSetObserver observer) {
249        if (mAdapter != null) {
250            mAdapter.registerDataSetObserver(observer);
251        }
252    }
253
254    public void unregisterDataSetObserver(DataSetObserver observer) {
255        if (mAdapter != null) {
256            mAdapter.unregisterDataSetObserver(observer);
257        }
258    }
259
260    public Filter getFilter() {
261        if (mIsFilterable) {
262            return ((Filterable) mAdapter).getFilter();
263        }
264        return null;
265    }
266
267    public ListAdapter getWrappedAdapter() {
268        return mAdapter;
269    }
270}
271