Home | History | Annotate | Download | only in list
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.contacts.common.list;
     17 
     18 import android.content.Context;
     19 import android.view.View;
     20 import android.view.ViewGroup;
     21 import android.widget.ListView;
     22 import android.widget.SectionIndexer;
     23 
     24 /**
     25  * A list adapter that supports section indexer and a pinned header.
     26  */
     27 public abstract class IndexerListAdapter extends PinnedHeaderListAdapter implements SectionIndexer {
     28 
     29     protected Context mContext;
     30     private SectionIndexer mIndexer;
     31     private int mIndexedPartition = 0;
     32     private boolean mSectionHeaderDisplayEnabled;
     33     private View mHeader;
     34 
     35     /**
     36      * An item view is displayed differently depending on whether it is placed
     37      * at the beginning, middle or end of a section. It also needs to know the
     38      * section header when it is at the beginning of a section. This object
     39      * captures all this configuration.
     40      */
     41     public static final class Placement {
     42         private int position = ListView.INVALID_POSITION;
     43         public boolean firstInSection;
     44         public boolean lastInSection;
     45         public String sectionHeader;
     46 
     47         public void invalidate() {
     48             position = ListView.INVALID_POSITION;
     49         }
     50     }
     51 
     52     private Placement mPlacementCache = new Placement();
     53 
     54     /**
     55      * Constructor.
     56      */
     57     public IndexerListAdapter(Context context) {
     58         super(context);
     59         mContext = context;
     60     }
     61 
     62     /**
     63      * Creates a section header view that will be pinned at the top of the list
     64      * as the user scrolls.
     65      */
     66     protected abstract View createPinnedSectionHeaderView(Context context, ViewGroup parent);
     67 
     68     /**
     69      * Sets the title in the pinned header as the user scrolls.
     70      */
     71     protected abstract void setPinnedSectionTitle(View pinnedHeaderView, String title);
     72 
     73     /**
     74      * Sets the contacts count in the pinned header.
     75      */
     76     protected abstract void setPinnedHeaderContactsCount(View header);
     77 
     78     /**
     79      * clears the contacts count in the pinned header and makes the view invisible.
     80      */
     81     protected abstract void clearPinnedHeaderContactsCount(View header);
     82 
     83     public boolean isSectionHeaderDisplayEnabled() {
     84         return mSectionHeaderDisplayEnabled;
     85     }
     86 
     87     public void setSectionHeaderDisplayEnabled(boolean flag) {
     88         this.mSectionHeaderDisplayEnabled = flag;
     89     }
     90 
     91     public int getIndexedPartition() {
     92         return mIndexedPartition;
     93     }
     94 
     95     public void setIndexedPartition(int partition) {
     96         this.mIndexedPartition = partition;
     97     }
     98 
     99     public SectionIndexer getIndexer() {
    100         return mIndexer;
    101     }
    102 
    103     public void setIndexer(SectionIndexer indexer) {
    104         mIndexer = indexer;
    105         mPlacementCache.invalidate();
    106     }
    107 
    108     public Object[] getSections() {
    109         if (mIndexer == null) {
    110             return new String[] { " " };
    111         } else {
    112             return mIndexer.getSections();
    113         }
    114     }
    115 
    116     /**
    117      * @return relative position of the section in the indexed partition
    118      */
    119     public int getPositionForSection(int sectionIndex) {
    120         if (mIndexer == null) {
    121             return -1;
    122         }
    123 
    124         return mIndexer.getPositionForSection(sectionIndex);
    125     }
    126 
    127     /**
    128      * @param position relative position in the indexed partition
    129      */
    130     public int getSectionForPosition(int position) {
    131         if (mIndexer == null) {
    132             return -1;
    133         }
    134 
    135         return mIndexer.getSectionForPosition(position);
    136     }
    137 
    138     @Override
    139     public int getPinnedHeaderCount() {
    140         if (isSectionHeaderDisplayEnabled()) {
    141             return super.getPinnedHeaderCount() + 1;
    142         } else {
    143             return super.getPinnedHeaderCount();
    144         }
    145     }
    146 
    147     @Override
    148     public View getPinnedHeaderView(int viewIndex, View convertView, ViewGroup parent) {
    149         if (isSectionHeaderDisplayEnabled() && viewIndex == getPinnedHeaderCount() - 1) {
    150             if (mHeader == null) {
    151                 mHeader = createPinnedSectionHeaderView(mContext, parent);
    152                 mHeader.setLayoutDirection(parent.getLayoutDirection());
    153             }
    154             return mHeader;
    155         } else {
    156             return super.getPinnedHeaderView(viewIndex, convertView, parent);
    157         }
    158     }
    159 
    160     @Override
    161     public void configurePinnedHeaders(PinnedHeaderListView listView) {
    162         super.configurePinnedHeaders(listView);
    163 
    164         if (!isSectionHeaderDisplayEnabled()) {
    165             return;
    166         }
    167 
    168         int index = getPinnedHeaderCount() - 1;
    169         if (mIndexer == null || getCount() == 0) {
    170             listView.setHeaderInvisible(index, false);
    171         } else {
    172             int listPosition = listView.getPositionAt(listView.getTotalTopPinnedHeaderHeight());
    173             int position = listPosition - listView.getHeaderViewsCount();
    174 
    175             int section = -1;
    176             int partition = getPartitionForPosition(position);
    177             if (partition == mIndexedPartition) {
    178                 int offset = getOffsetInPartition(position);
    179                 if (offset != -1) {
    180                     section = getSectionForPosition(offset);
    181                 }
    182             }
    183 
    184             if (section == -1) {
    185                 listView.setHeaderInvisible(index, false);
    186             } else {
    187                 setPinnedSectionTitle(mHeader, (String)mIndexer.getSections()[section]);
    188                 if (section == 0) {
    189                     setPinnedHeaderContactsCount(mHeader);
    190                 } else {
    191                     clearPinnedHeaderContactsCount(mHeader);
    192                 }
    193                 // Compute the item position where the current partition begins
    194                 int partitionStart = getPositionForPartition(mIndexedPartition);
    195                 if (hasHeader(mIndexedPartition)) {
    196                     partitionStart++;
    197                 }
    198 
    199                 // Compute the item position where the next section begins
    200                 int nextSectionPosition = partitionStart + getPositionForSection(section + 1);
    201                 boolean isLastInSection = position == nextSectionPosition - 1;
    202                 listView.setFadingHeader(index, listPosition, isLastInSection);
    203             }
    204         }
    205     }
    206 
    207     /**
    208      * Computes the item's placement within its section and populates the {@code placement}
    209      * object accordingly.  Please note that the returned object is volatile and should be
    210      * copied if the result needs to be used later.
    211      */
    212     public Placement getItemPlacementInSection(int position) {
    213         if (mPlacementCache.position == position) {
    214             return mPlacementCache;
    215         }
    216 
    217         mPlacementCache.position = position;
    218         if (isSectionHeaderDisplayEnabled()) {
    219             int section = getSectionForPosition(position);
    220             if (section != -1 && getPositionForSection(section) == position) {
    221                 mPlacementCache.firstInSection = true;
    222                 mPlacementCache.sectionHeader = (String)getSections()[section];
    223             } else {
    224                 mPlacementCache.firstInSection = false;
    225                 mPlacementCache.sectionHeader = null;
    226             }
    227 
    228             mPlacementCache.lastInSection = (getPositionForSection(section + 1) - 1 == position);
    229         } else {
    230             mPlacementCache.firstInSection = false;
    231             mPlacementCache.lastInSection = false;
    232             mPlacementCache.sectionHeader = null;
    233         }
    234         return mPlacementCache;
    235     }
    236 }
    237