MergedAdapter.java revision 773f2e6e6994f38bf7d2679f2412d2f14f22c424
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
18package com.android.mail.browse;
19
20import android.database.DataSetObserver;
21import android.view.View;
22import android.view.ViewGroup;
23import android.widget.BaseAdapter;
24import android.widget.ListAdapter;
25
26import java.util.Arrays;
27import 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 */
34public 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}