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