Home | History | Annotate | Download | only in mailcommon
      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 
     19 package com.android.mailcommon;
     20 
     21 import android.database.DataSetObserver;
     22 import android.view.View;
     23 import android.view.ViewGroup;
     24 import android.widget.BaseAdapter;
     25 import android.widget.ListAdapter;
     26 import android.widget.SpinnerAdapter;
     27 
     28 import java.util.Arrays;
     29 import java.util.List;
     30 
     31 /**
     32  * An adapter that combines items from multiple provided adapters into a single list.
     33  *
     34  * @param <T> the class of each constituent adapter
     35  */
     36 public class MergedAdapter<T extends MergedAdapter.ListSpinnerAdapter> extends BaseAdapter {
     37 
     38     private List<T> mAdapters;
     39     private final DataSetObserver mObserver;
     40 
     41     /**
     42      * A Mergeable adapter must implement both ListAdapter and SpinnerAdapter to be useful in lists
     43      * and spinners.
     44      */
     45     public interface ListSpinnerAdapter extends ListAdapter, SpinnerAdapter {
     46     }
     47 
     48     public static class LocalAdapterPosition<T extends ListSpinnerAdapter> {
     49         public final T mAdapter;
     50         public final int mLocalPosition;
     51 
     52         public LocalAdapterPosition(T adapter, int offset) {
     53             mAdapter = adapter;
     54             mLocalPosition = offset;
     55         }
     56     }
     57 
     58     public MergedAdapter() {
     59         mObserver = new DataSetObserver() {
     60             @Override
     61             public void onChanged() {
     62                 notifyDataSetChanged();
     63             }
     64         };
     65     }
     66 
     67     public void setAdapters(T... adapters) {
     68         if (mAdapters != null) {
     69             for (T adapter : mAdapters) {
     70                 adapter.unregisterDataSetObserver(mObserver);
     71             }
     72         }
     73 
     74         mAdapters = Arrays.asList(adapters);
     75 
     76         for (T adapter : mAdapters) {
     77             adapter.registerDataSetObserver(mObserver);
     78         }
     79     }
     80 
     81     public int getSubAdapterCount() {
     82         return mAdapters.size();
     83     }
     84 
     85     public T getSubAdapter(int index) {
     86         return mAdapters.get(index);
     87     }
     88 
     89     @Override
     90     public int getCount() {
     91         int count = 0;
     92         for (T adapter : mAdapters) {
     93             count += adapter.getCount();
     94         }
     95         return count;
     96         // TODO: cache counts until next onChanged
     97     }
     98 
     99     /**
    100      * For a given merged position, find the corresponding Adapter and local position within that
    101      * Adapter by iterating through Adapters and summing their counts until the merged position is
    102      * found.
    103      *
    104      * @param position a merged (global) position
    105      * @return the matching Adapter and local position, or null if not found
    106      */
    107     public LocalAdapterPosition<T> getAdapterOffsetForItem(final int position) {
    108         final int adapterCount = mAdapters.size();
    109         int i = 0;
    110         int count = 0;
    111 
    112         while (i < adapterCount) {
    113             T a = mAdapters.get(i);
    114             int newCount = count + a.getCount();
    115             if (position < newCount) {
    116                 return new LocalAdapterPosition<T>(a, position - count);
    117             }
    118             count = newCount;
    119             i++;
    120         }
    121         return null;
    122     }
    123 
    124     @Override
    125     public Object getItem(int position) {
    126         LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
    127         if (result == null) {
    128             return null;
    129         }
    130         return result.mAdapter.getItem(result.mLocalPosition);
    131     }
    132 
    133     @Override
    134     public long getItemId(int position) {
    135         return position;
    136     }
    137 
    138     @Override
    139     public int getViewTypeCount() {
    140         int count = 0;
    141         for (T adapter : mAdapters) {
    142             count += adapter.getViewTypeCount();
    143         }
    144         return count;
    145     }
    146 
    147     @Override
    148     public int getItemViewType(int position) {
    149         LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
    150         int otherViewTypeCount = 0;
    151         for (T adapter : mAdapters) {
    152             if (adapter == result.mAdapter) {
    153                 break;
    154             }
    155             otherViewTypeCount += adapter.getViewTypeCount();
    156         }
    157         int type = result.mAdapter.getItemViewType(result.mLocalPosition);
    158         // Headers (negative types) are in a separate global namespace and their values should not
    159         // be affected by preceding adapter view types.
    160         if (type >= 0) {
    161             type += otherViewTypeCount;
    162         }
    163         return type;
    164     }
    165 
    166     @Override
    167     public View getView(int position, View convertView, ViewGroup parent) {
    168         LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
    169         return result.mAdapter.getView(result.mLocalPosition, convertView, parent);
    170     }
    171 
    172     @Override
    173     public View getDropDownView(int position, View convertView, ViewGroup parent) {
    174         LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
    175         return result.mAdapter.getDropDownView(result.mLocalPosition, convertView, parent);
    176     }
    177 
    178     @Override
    179     public boolean areAllItemsEnabled() {
    180         boolean enabled = true;
    181         for (T adapter : mAdapters) {
    182             enabled &= adapter.areAllItemsEnabled();
    183         }
    184         return enabled;
    185     }
    186 
    187     @Override
    188     public boolean isEnabled(int position) {
    189         LocalAdapterPosition<T> result = getAdapterOffsetForItem(position);
    190         return result.mAdapter.isEnabled(result.mLocalPosition);
    191     }
    192 
    193 }