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.android.contacts.common.testing.NeededForTesting; 25 import 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 36 public 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