11a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira/*
21a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira * Copyright (C) 2011 Google Inc.
31a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira * Licensed to The Android Open Source Project.
41a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira *
51a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira * Licensed under the Apache License, Version 2.0 (the "License");
61a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira * you may not use this file except in compliance with the License.
71a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira * You may obtain a copy of the License at
81a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira *
91a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira *      http://www.apache.org/licenses/LICENSE-2.0
101a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira *
111a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira * Unless required by applicable law or agreed to in writing, software
121a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira * distributed under the License is distributed on an "AS IS" BASIS,
131a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
141a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira * See the License for the specific language governing permissions and
151a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira * limitations under the License.
161a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira */
171a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
1830e2c24b056542f3b1b438aeb798305d1226d0c8Andy Huangpackage com.android.mail.browse;
191a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
201a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereiraimport android.database.DataSetObserver;
211a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereiraimport android.view.View;
221a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereiraimport android.view.ViewGroup;
231a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereiraimport android.widget.BaseAdapter;
241a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereiraimport android.widget.ListAdapter;
251a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
261a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereiraimport java.util.Arrays;
271a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereiraimport java.util.List;
281a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
291a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira/**
301a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira * An adapter that combines items from multiple provided adapters into a single list.
311a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira *
321a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira * @param <T> the class of each constituent adapter
331a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira */
34773f2e6e6994f38bf7d2679f2412d2f14f22c424Andrew Sappersteinpublic class MergedAdapter<T extends ListAdapter> extends BaseAdapter {
351a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
361a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    private List<T> mAdapters;
371a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    private final DataSetObserver mObserver;
381a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
39773f2e6e6994f38bf7d2679f2412d2f14f22c424Andrew Sapperstein    public static class LocalAdapterPosition<T extends ListAdapter> {
40773f2e6e6994f38bf7d2679f2412d2f14f22c424Andrew Sapperstein        private final T mAdapter;
41773f2e6e6994f38bf7d2679f2412d2f14f22c424Andrew Sapperstein        private final int mLocalPosition;
421a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
431a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        public LocalAdapterPosition(T adapter, int offset) {
441a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            mAdapter = adapter;
451a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            mLocalPosition = offset;
461a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        }
47773f2e6e6994f38bf7d2679f2412d2f14f22c424Andrew Sapperstein
48773f2e6e6994f38bf7d2679f2412d2f14f22c424Andrew Sapperstein        public T getAdapter() {
49773f2e6e6994f38bf7d2679f2412d2f14f22c424Andrew Sapperstein            return mAdapter;
50773f2e6e6994f38bf7d2679f2412d2f14f22c424Andrew Sapperstein        }
51773f2e6e6994f38bf7d2679f2412d2f14f22c424Andrew Sapperstein
52773f2e6e6994f38bf7d2679f2412d2f14f22c424Andrew Sapperstein        public int getLocalPosition() {
53773f2e6e6994f38bf7d2679f2412d2f14f22c424Andrew Sapperstein            return mLocalPosition;
54773f2e6e6994f38bf7d2679f2412d2f14f22c424Andrew Sapperstein        }
551a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    }
561a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
571a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    public MergedAdapter() {
581a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        mObserver = new DataSetObserver() {
591a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            @Override
601a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            public void onChanged() {
611a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira                notifyDataSetChanged();
621a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            }
631a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        };
641a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    }
651a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
661a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    public void setAdapters(T... adapters) {
671a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        if (mAdapters != null) {
681a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            for (T adapter : mAdapters) {
691a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira                adapter.unregisterDataSetObserver(mObserver);
701a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            }
711a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        }
721a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
731a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        mAdapters = Arrays.asList(adapters);
741a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
751a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        for (T adapter : mAdapters) {
761a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            adapter.registerDataSetObserver(mObserver);
771a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        }
781a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    }
791a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
801a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    public int getSubAdapterCount() {
811a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        return mAdapters.size();
821a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    }
831a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
841a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    public T getSubAdapter(int index) {
851a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        return mAdapters.get(index);
861a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    }
871a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
881a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    @Override
891a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    public int getCount() {
901a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        int count = 0;
911a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        for (T adapter : mAdapters) {
921a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            count += adapter.getCount();
931a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        }
941a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        return count;
951a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        // TODO: cache counts until next onChanged
961a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    }
971a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
981a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    /**
991a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira     * For a given merged position, find the corresponding Adapter and local position within that
1001a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira     * Adapter by iterating through Adapters and summing their counts until the merged position is
1011a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira     * found.
1021a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira     *
1031a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira     * @param position a merged (global) position
1041a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira     * @return the matching Adapter and local position, or null if not found
1051a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira     */
1061a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    public LocalAdapterPosition<T> getAdapterOffsetForItem(final int position) {
1071a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        final int adapterCount = mAdapters.size();
1081a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        int i = 0;
1091a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        int count = 0;
1101a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
1111a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        while (i < adapterCount) {
1121a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            T a = mAdapters.get(i);
1131a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            int newCount = count + a.getCount();
1141a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            if (position < newCount) {
1151a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira                return new LocalAdapterPosition<T>(a, position - count);
1161a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            }
1171a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            count = newCount;
1181a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            i++;
1191a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        }
1201a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        return null;
1211a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    }
1221a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
1231a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    @Override
1241a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    public Object getItem(int position) {
1251a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
1261a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        if (result == null) {
1271a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            return null;
1281a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        }
1291a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        return result.mAdapter.getItem(result.mLocalPosition);
1301a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    }
1311a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
1321a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    @Override
1331a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    public long getItemId(int position) {
1341a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        return position;
1351a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    }
1361a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
1371a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    @Override
1381a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    public int getViewTypeCount() {
1391a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        int count = 0;
1401a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        for (T adapter : mAdapters) {
1411a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            count += adapter.getViewTypeCount();
1421a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        }
1431a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        return count;
1441a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    }
1451a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
1461a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    @Override
1471a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    public int getItemViewType(int position) {
1481a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
1491a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        int otherViewTypeCount = 0;
1501a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        for (T adapter : mAdapters) {
1511a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            if (adapter == result.mAdapter) {
1521a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira                break;
1531a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            }
1541a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            otherViewTypeCount += adapter.getViewTypeCount();
1551a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        }
1561a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        int type = result.mAdapter.getItemViewType(result.mLocalPosition);
1571a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        // Headers (negative types) are in a separate global namespace and their values should not
1581a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        // be affected by preceding adapter view types.
1591a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        if (type >= 0) {
1601a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            type += otherViewTypeCount;
1611a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        }
1621a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        return type;
1631a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    }
1641a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
1651a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    @Override
1661a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    public View getView(int position, View convertView, ViewGroup parent) {
1671a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
1681a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        return result.mAdapter.getView(result.mLocalPosition, convertView, parent);
1691a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    }
1701a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
1711a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    @Override
1721a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    public boolean areAllItemsEnabled() {
1731a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        boolean enabled = true;
1741a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        for (T adapter : mAdapters) {
1751a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira            enabled &= adapter.areAllItemsEnabled();
1761a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        }
1771a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        return enabled;
1781a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    }
1791a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
1801a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    @Override
1811a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    public boolean isEnabled(int position) {
1821a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
1831a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira        return result.mAdapter.isEnabled(result.mLocalPosition);
1841a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira    }
1851a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira
1861a73ea8bc1b08ff4e7baac515ed54a15de13ffe2Mindy Pereira}