1587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo/*
2587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo * Copyright (C) 2011 Google Inc.
3587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo * Licensed to The Android Open Source Project.
4587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo *
5587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo * Licensed under the Apache License, Version 2.0 (the "License");
6587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo * you may not use this file except in compliance with the License.
7587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo * You may obtain a copy of the License at
8587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo *
9587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo *      http://www.apache.org/licenses/LICENSE-2.0
10587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo *
11587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo * Unless required by applicable law or agreed to in writing, software
12587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo * distributed under the License is distributed on an "AS IS" BASIS,
13587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo * See the License for the specific language governing permissions and
15587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo * limitations under the License.
16587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo */
17587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
18587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
19587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalopackage com.android.mailcommon;
20587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
21587722a561a8ad36a27134379cd1be82c13a13e8Ben Komaloimport android.database.DataSetObserver;
22587722a561a8ad36a27134379cd1be82c13a13e8Ben Komaloimport android.view.View;
23587722a561a8ad36a27134379cd1be82c13a13e8Ben Komaloimport android.view.ViewGroup;
24587722a561a8ad36a27134379cd1be82c13a13e8Ben Komaloimport android.widget.BaseAdapter;
25587722a561a8ad36a27134379cd1be82c13a13e8Ben Komaloimport android.widget.ListAdapter;
26587722a561a8ad36a27134379cd1be82c13a13e8Ben Komaloimport android.widget.SpinnerAdapter;
27587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
28587722a561a8ad36a27134379cd1be82c13a13e8Ben Komaloimport java.util.Arrays;
29587722a561a8ad36a27134379cd1be82c13a13e8Ben Komaloimport java.util.List;
30587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
31587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo/**
32587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo * An adapter that combines items from multiple provided adapters into a single list.
33587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo *
34587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo * @param <T> the class of each constituent adapter
35587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo */
36587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalopublic class MergedAdapter<T extends MergedAdapter.ListSpinnerAdapter> extends BaseAdapter {
37587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
38587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    private List<T> mAdapters;
39587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    private final DataSetObserver mObserver;
40587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
41587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    /**
42587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo     * A Mergeable adapter must implement both ListAdapter and SpinnerAdapter to be useful in lists
43587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo     * and spinners.
44587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo     */
45587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    public interface ListSpinnerAdapter extends ListAdapter, SpinnerAdapter {
46587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    }
47587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
48587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    public static class LocalAdapterPosition<T extends ListSpinnerAdapter> {
49587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        public final T mAdapter;
50587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        public final int mLocalPosition;
51587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
52587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        public LocalAdapterPosition(T adapter, int offset) {
53587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            mAdapter = adapter;
54587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            mLocalPosition = offset;
55587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        }
56587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    }
57587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
58587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    public MergedAdapter() {
59587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        mObserver = new DataSetObserver() {
60587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            @Override
61587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            public void onChanged() {
62587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo                notifyDataSetChanged();
63587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            }
64587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        };
65587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    }
66587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
67587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    public void setAdapters(T... adapters) {
68587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        if (mAdapters != null) {
69587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            for (T adapter : mAdapters) {
70587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo                adapter.unregisterDataSetObserver(mObserver);
71587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            }
72587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        }
73587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
74587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        mAdapters = Arrays.asList(adapters);
75587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
76587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        for (T adapter : mAdapters) {
77587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            adapter.registerDataSetObserver(mObserver);
78587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        }
79587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    }
80587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
81587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    public int getSubAdapterCount() {
82587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        return mAdapters.size();
83587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    }
84587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
85587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    public T getSubAdapter(int index) {
86587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        return mAdapters.get(index);
87587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    }
88587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
89587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    @Override
90587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    public int getCount() {
91587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        int count = 0;
92587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        for (T adapter : mAdapters) {
93587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            count += adapter.getCount();
94587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        }
95587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        return count;
96587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        // TODO: cache counts until next onChanged
97587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    }
98587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
99587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    /**
100587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo     * For a given merged position, find the corresponding Adapter and local position within that
101587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo     * Adapter by iterating through Adapters and summing their counts until the merged position is
102587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo     * found.
103587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo     *
104587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo     * @param position a merged (global) position
105587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo     * @return the matching Adapter and local position, or null if not found
106587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo     */
107587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    public LocalAdapterPosition<T> getAdapterOffsetForItem(final int position) {
108587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        final int adapterCount = mAdapters.size();
109587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        int i = 0;
110587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        int count = 0;
111587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
112587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        while (i < adapterCount) {
113587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            T a = mAdapters.get(i);
114587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            int newCount = count + a.getCount();
115587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            if (position < newCount) {
116587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo                return new LocalAdapterPosition<T>(a, position - count);
117587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            }
118587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            count = newCount;
119587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            i++;
120587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        }
121587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        return null;
122587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    }
123587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
124587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    @Override
125587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    public Object getItem(int position) {
126587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
127587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        if (result == null) {
128587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            return null;
129587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        }
130587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        return result.mAdapter.getItem(result.mLocalPosition);
131587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    }
132587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
133587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    @Override
134587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    public long getItemId(int position) {
135587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        return position;
136587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    }
137587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
138587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    @Override
139587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    public int getViewTypeCount() {
140587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        int count = 0;
141587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        for (T adapter : mAdapters) {
142587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            count += adapter.getViewTypeCount();
143587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        }
144587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        return count;
145587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    }
146587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
147587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    @Override
148587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    public int getItemViewType(int position) {
149587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
150587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        int otherViewTypeCount = 0;
151587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        for (T adapter : mAdapters) {
152587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            if (adapter == result.mAdapter) {
153587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo                break;
154587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            }
155587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            otherViewTypeCount += adapter.getViewTypeCount();
156587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        }
157587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        int type = result.mAdapter.getItemViewType(result.mLocalPosition);
158587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        // Headers (negative types) are in a separate global namespace and their values should not
159587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        // be affected by preceding adapter view types.
160587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        if (type >= 0) {
161587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            type += otherViewTypeCount;
162587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        }
163587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        return type;
164587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    }
165587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
166587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    @Override
167587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    public View getView(int position, View convertView, ViewGroup parent) {
168587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
169587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        return result.mAdapter.getView(result.mLocalPosition, convertView, parent);
170587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    }
171587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
172587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    @Override
173587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    public View getDropDownView(int position, View convertView, ViewGroup parent) {
174587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
175587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        return result.mAdapter.getDropDownView(result.mLocalPosition, convertView, parent);
176587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    }
177587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
178587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    @Override
179587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    public boolean areAllItemsEnabled() {
180587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        boolean enabled = true;
181587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        for (T adapter : mAdapters) {
182587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo            enabled &= adapter.areAllItemsEnabled();
183587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        }
184587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        return enabled;
185587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    }
186587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
187587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    @Override
188587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    public boolean isEnabled(int position) {
189587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
190587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo        return result.mAdapter.isEnabled(result.mLocalPosition);
191587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo    }
192587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo
193587722a561a8ad36a27134379cd1be82c13a13e8Ben Komalo}