blob: 44ff291b507e0704b98a4c52cc6f9b31ba5b9b7f [file] [log] [blame]
Dmitri Plotnikov54d716f2010-04-21 19:50:03 -07001/*
2 * Copyright (C) 2010 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 */
16package com.android.contacts.widget;
17
18import android.database.DataSetObserver;
19import android.view.View;
20import android.view.ViewGroup;
21import android.widget.BaseAdapter;
22import android.widget.ListAdapter;
23
Chiao Chenge0b2f1e2012-06-12 13:07:56 -070024import com.google.common.annotations.VisibleForTesting;
25
Dmitri Plotnikov54d716f2010-04-21 19:50:03 -070026/**
27 * A general purpose adapter that is composed of multiple sub-adapters. It just
28 * appends them in the order they are added. It listens to changes from all
29 * sub-adapters and propagates them to its own listeners.
30 */
31public class CompositeListAdapter extends BaseAdapter {
32
33 private static final int INITIAL_CAPACITY = 2;
34
35 private ListAdapter[] mAdapters;
36 private int[] mCounts;
37 private int[] mViewTypeCounts;
38 private int mSize = 0;
39 private int mCount = 0;
40 private int mViewTypeCount = 0;
41 private boolean mAllItemsEnabled = true;
42 private boolean mCacheValid = true;
43
44 private DataSetObserver mDataSetObserver = new DataSetObserver() {
45
46 @Override
47 public void onChanged() {
48 invalidate();
49 notifyDataChanged();
50 }
51
52 @Override
53 public void onInvalidated() {
54 invalidate();
55 notifyDataChanged();
56 }
57 };
58
59 public CompositeListAdapter() {
60 this(INITIAL_CAPACITY);
61 }
62
63 public CompositeListAdapter(int initialCapacity) {
64 mAdapters = new ListAdapter[INITIAL_CAPACITY];
65 mCounts = new int[INITIAL_CAPACITY];
66 mViewTypeCounts = new int[INITIAL_CAPACITY];
67 }
68
Flavio Lerda37a26842011-06-27 11:36:52 +010069 @VisibleForTesting
70 /*package*/ void addAdapter(ListAdapter adapter) {
Dmitri Plotnikov54d716f2010-04-21 19:50:03 -070071 if (mSize >= mAdapters.length) {
72 int newCapacity = mSize + 2;
73 ListAdapter[] newAdapters = new ListAdapter[newCapacity];
74 System.arraycopy(mAdapters, 0, newAdapters, 0, mSize);
75 mAdapters = newAdapters;
76
77 int[] newCounts = new int[newCapacity];
78 System.arraycopy(mCounts, 0, newCounts, 0, mSize);
79 mCounts = newCounts;
80
81 int[] newViewTypeCounts = new int[newCapacity];
82 System.arraycopy(mViewTypeCounts, 0, newViewTypeCounts, 0, mSize);
83 mViewTypeCounts = newViewTypeCounts;
84 }
85
86 adapter.registerDataSetObserver(mDataSetObserver);
87
88 int count = adapter.getCount();
89 int viewTypeCount = adapter.getViewTypeCount();
90
91 mAdapters[mSize] = adapter;
92 mCounts[mSize] = count;
93 mCount += count;
94 mAllItemsEnabled &= adapter.areAllItemsEnabled();
95 mViewTypeCounts[mSize] = viewTypeCount;
96 mViewTypeCount += viewTypeCount;
97 mSize++;
98
99 notifyDataChanged();
100 }
101
102 protected void notifyDataChanged() {
103 if (getCount() > 0) {
104 notifyDataSetChanged();
105 } else {
106 notifyDataSetInvalidated();
107 }
108 }
109
110 protected void invalidate() {
111 mCacheValid = false;
112 }
113
114 protected void ensureCacheValid() {
115 if (mCacheValid) {
116 return;
117 }
118
119 mCount = 0;
120 mAllItemsEnabled = true;
121 mViewTypeCount = 0;
122 for (int i = 0; i < mSize; i++) {
123 int count = mAdapters[i].getCount();
124 int viewTypeCount = mAdapters[i].getViewTypeCount();
125 mCounts[i] = count;
126 mCount += count;
127 mAllItemsEnabled &= mAdapters[i].areAllItemsEnabled();
128 mViewTypeCount += viewTypeCount;
129 }
130
131 mCacheValid = true;
132 }
133
134 public int getCount() {
135 ensureCacheValid();
136 return mCount;
137 }
138
139 public Object getItem(int position) {
140 ensureCacheValid();
141 int start = 0;
142 for (int i = 0; i < mCounts.length; i++) {
143 int end = start + mCounts[i];
144 if (position >= start && position < end) {
145 return mAdapters[i].getItem(position - start);
146 }
147 start = end;
148 }
149
150 throw new ArrayIndexOutOfBoundsException(position);
151 }
152
153 public long getItemId(int position) {
154 ensureCacheValid();
155 int start = 0;
156 for (int i = 0; i < mCounts.length; i++) {
157 int end = start + mCounts[i];
158 if (position >= start && position < end) {
159 return mAdapters[i].getItemId(position - start);
160 }
161 start = end;
162 }
163
164 throw new ArrayIndexOutOfBoundsException(position);
165 }
166
167 @Override
168 public int getViewTypeCount() {
169 ensureCacheValid();
170 return mViewTypeCount;
171 }
172
173 @Override
174 public int getItemViewType(int position) {
175 ensureCacheValid();
176 int start = 0;
177 int viewTypeOffset = 0;
178 for (int i = 0; i < mCounts.length; i++) {
179 int end = start + mCounts[i];
180 if (position >= start && position < end) {
181 return viewTypeOffset + mAdapters[i].getItemViewType(position - start);
182 }
183 viewTypeOffset += mViewTypeCounts[i];
184 start = end;
185 }
186
187 throw new ArrayIndexOutOfBoundsException(position);
188 }
189
190 public View getView(int position, View convertView, ViewGroup parent) {
191 ensureCacheValid();
192 int start = 0;
193 for (int i = 0; i < mCounts.length; i++) {
194 int end = start + mCounts[i];
195 if (position >= start && position < end) {
196 return mAdapters[i].getView(position - start, convertView, parent);
197 }
198 start = end;
199 }
200
201 throw new ArrayIndexOutOfBoundsException(position);
202 }
203
204 @Override
205 public boolean areAllItemsEnabled() {
206 ensureCacheValid();
207 return mAllItemsEnabled;
208 }
209
210 @Override
211 public boolean isEnabled(int position) {
212 ensureCacheValid();
213 int start = 0;
214 for (int i = 0; i < mCounts.length; i++) {
215 int end = start + mCounts[i];
216 if (position >= start && position < end) {
217 return mAdapters[i].areAllItemsEnabled()
218 || mAdapters[i].isEnabled(position - start);
219 }
220 start = end;
221 }
222
223 throw new ArrayIndexOutOfBoundsException(position);
224 }
225}