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 */ 16 package com.android.contacts.widget; 17 18 import android.database.DataSetObserver; 19 import android.view.View; 20 import android.view.ViewGroup; 21 import android.widget.BaseAdapter; 22 import android.widget.ListAdapter; 23 24 import com.google.common.annotations.VisibleForTesting; 25 26 /** 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 */ 31 public 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 CompositeListAdapter()59 public CompositeListAdapter() { 60 this(INITIAL_CAPACITY); 61 } 62 CompositeListAdapter(int initialCapacity)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 69 @VisibleForTesting addAdapter(ListAdapter adapter)70 /*package*/ void addAdapter(ListAdapter adapter) { 71 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 notifyDataChanged()102 protected void notifyDataChanged() { 103 if (getCount() > 0) { 104 notifyDataSetChanged(); 105 } else { 106 notifyDataSetInvalidated(); 107 } 108 } 109 invalidate()110 protected void invalidate() { 111 mCacheValid = false; 112 } 113 ensureCacheValid()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 getCount()134 public int getCount() { 135 ensureCacheValid(); 136 return mCount; 137 } 138 getItem(int position)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 getItemId(int position)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 getViewTypeCount()168 public int getViewTypeCount() { 169 ensureCacheValid(); 170 return mViewTypeCount; 171 } 172 173 @Override getItemViewType(int position)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 getView(int position, View convertView, ViewGroup parent)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 areAllItemsEnabled()205 public boolean areAllItemsEnabled() { 206 ensureCacheValid(); 207 return mAllItemsEnabled; 208 } 209 210 @Override isEnabled(int position)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 } 226