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