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