1 /* 2 * Copyright (C) 2011 Google Inc. 3 * Licensed to The Android Open Source Project. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package com.android.mail.browse; 19 20 import android.database.DataSetObserver; 21 import android.view.View; 22 import android.view.ViewGroup; 23 import android.widget.BaseAdapter; 24 import android.widget.ListAdapter; 25 26 import java.util.Arrays; 27 import java.util.List; 28 29 /** 30 * An adapter that combines items from multiple provided adapters into a single list. 31 * 32 * @param <T> the class of each constituent adapter 33 */ 34 public class MergedAdapter<T extends ListAdapter> extends BaseAdapter { 35 36 private List<T> mAdapters; 37 private final DataSetObserver mObserver; 38 39 public static class LocalAdapterPosition<T extends ListAdapter> { 40 private final T mAdapter; 41 private final int mLocalPosition; 42 43 public LocalAdapterPosition(T adapter, int offset) { 44 mAdapter = adapter; 45 mLocalPosition = offset; 46 } 47 48 public T getAdapter() { 49 return mAdapter; 50 } 51 52 public int getLocalPosition() { 53 return mLocalPosition; 54 } 55 } 56 57 public MergedAdapter() { 58 mObserver = new DataSetObserver() { 59 @Override 60 public void onChanged() { 61 notifyDataSetChanged(); 62 } 63 }; 64 } 65 66 public void setAdapters(T... adapters) { 67 if (mAdapters != null) { 68 for (T adapter : mAdapters) { 69 adapter.unregisterDataSetObserver(mObserver); 70 } 71 } 72 73 mAdapters = Arrays.asList(adapters); 74 75 for (T adapter : mAdapters) { 76 adapter.registerDataSetObserver(mObserver); 77 } 78 } 79 80 public int getSubAdapterCount() { 81 return mAdapters.size(); 82 } 83 84 public T getSubAdapter(int index) { 85 return mAdapters.get(index); 86 } 87 88 @Override 89 public int getCount() { 90 int count = 0; 91 for (T adapter : mAdapters) { 92 count += adapter.getCount(); 93 } 94 return count; 95 // TODO: cache counts until next onChanged 96 } 97 98 /** 99 * For a given merged position, find the corresponding Adapter and local position within that 100 * Adapter by iterating through Adapters and summing their counts until the merged position is 101 * found. 102 * 103 * @param position a merged (global) position 104 * @return the matching Adapter and local position, or null if not found 105 */ 106 public LocalAdapterPosition<T> getAdapterOffsetForItem(final int position) { 107 final int adapterCount = mAdapters.size(); 108 int i = 0; 109 int count = 0; 110 111 while (i < adapterCount) { 112 T a = mAdapters.get(i); 113 int newCount = count + a.getCount(); 114 if (position < newCount) { 115 return new LocalAdapterPosition<T>(a, position - count); 116 } 117 count = newCount; 118 i++; 119 } 120 return null; 121 } 122 123 @Override 124 public Object getItem(int position) { 125 LocalAdapterPosition<T> result = getAdapterOffsetForItem(position); 126 if (result == null) { 127 return null; 128 } 129 return result.mAdapter.getItem(result.mLocalPosition); 130 } 131 132 @Override 133 public long getItemId(int position) { 134 return position; 135 } 136 137 @Override 138 public int getViewTypeCount() { 139 int count = 0; 140 for (T adapter : mAdapters) { 141 count += adapter.getViewTypeCount(); 142 } 143 return count; 144 } 145 146 @Override 147 public int getItemViewType(int position) { 148 LocalAdapterPosition<T> result = getAdapterOffsetForItem(position); 149 int otherViewTypeCount = 0; 150 for (T adapter : mAdapters) { 151 if (adapter == result.mAdapter) { 152 break; 153 } 154 otherViewTypeCount += adapter.getViewTypeCount(); 155 } 156 int type = result.mAdapter.getItemViewType(result.mLocalPosition); 157 // Headers (negative types) are in a separate global namespace and their values should not 158 // be affected by preceding adapter view types. 159 if (type >= 0) { 160 type += otherViewTypeCount; 161 } 162 return type; 163 } 164 165 @Override 166 public View getView(int position, View convertView, ViewGroup parent) { 167 LocalAdapterPosition<T> result = getAdapterOffsetForItem(position); 168 return result.mAdapter.getView(result.mLocalPosition, convertView, parent); 169 } 170 171 @Override 172 public boolean areAllItemsEnabled() { 173 boolean enabled = true; 174 for (T adapter : mAdapters) { 175 enabled &= adapter.areAllItemsEnabled(); 176 } 177 return enabled; 178 } 179 180 @Override 181 public boolean isEnabled(int position) { 182 LocalAdapterPosition<T> result = getAdapterOffsetForItem(position); 183 return result.mAdapter.isEnabled(result.mLocalPosition); 184 } 185 186 }