Home | History | Annotate | Download | only in widget
      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.widget;
     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             }
    153             return mHeader;
    154         } else {
    155             return super.getPinnedHeaderView(viewIndex, convertView, parent);
    156         }
    157     }
    158 
    159     @Override
    160     public void configurePinnedHeaders(PinnedHeaderListView listView) {
    161         super.configurePinnedHeaders(listView);
    162 
    163         if (!isSectionHeaderDisplayEnabled()) {
    164             return;
    165         }
    166 
    167         int index = getPinnedHeaderCount() - 1;
    168         if (mIndexer == null || getCount() == 0) {
    169             listView.setHeaderInvisible(index, false);
    170         } else {
    171             int listPosition = listView.getPositionAt(listView.getTotalTopPinnedHeaderHeight());
    172             int position = listPosition - listView.getHeaderViewsCount();
    173 
    174             int section = -1;
    175             int partition = getPartitionForPosition(position);
    176             if (partition == mIndexedPartition) {
    177                 int offset = getOffsetInPartition(position);
    178                 if (offset != -1) {
    179                     section = getSectionForPosition(offset);
    180                 }
    181             }
    182 
    183             if (section == -1) {
    184                 listView.setHeaderInvisible(index, false);
    185             } else {
    186                 setPinnedSectionTitle(mHeader, (String)mIndexer.getSections()[section]);
    187                 if (section == 0) {
    188                     setPinnedHeaderContactsCount(mHeader);
    189                 } else {
    190                     clearPinnedHeaderContactsCount(mHeader);
    191                 }
    192                 // Compute the item position where the current partition begins
    193                 int partitionStart = getPositionForPartition(mIndexedPartition);
    194                 if (hasHeader(mIndexedPartition)) {
    195                     partitionStart++;
    196                 }
    197 
    198                 // Compute the item position where the next section begins
    199                 int nextSectionPosition = partitionStart + getPositionForSection(section + 1);
    200                 boolean isLastInSection = position == nextSectionPosition - 1;
    201                 listView.setFadingHeader(index, listPosition, isLastInSection);
    202             }
    203         }
    204     }
    205 
    206     /**
    207      * Computes the item's placement within its section and populates the {@code placement}
    208      * object accordingly.  Please note that the returned object is volatile and should be
    209      * copied if the result needs to be used later.
    210      */
    211     public Placement getItemPlacementInSection(int position) {
    212         if (mPlacementCache.position == position) {
    213             return mPlacementCache;
    214         }
    215 
    216         mPlacementCache.position = position;
    217         if (isSectionHeaderDisplayEnabled()) {
    218             int section = getSectionForPosition(position);
    219             if (section != -1 && getPositionForSection(section) == position) {
    220                 mPlacementCache.firstInSection = true;
    221                 mPlacementCache.sectionHeader = (String)getSections()[section];
    222             } else {
    223                 mPlacementCache.firstInSection = false;
    224                 mPlacementCache.sectionHeader = null;
    225             }
    226 
    227             mPlacementCache.lastInSection = (getPositionForSection(section + 1) - 1 == position);
    228         } else {
    229             mPlacementCache.firstInSection = false;
    230             mPlacementCache.lastInSection = false;
    231             mPlacementCache.sectionHeader = null;
    232         }
    233         return mPlacementCache;
    234     }
    235 }
    236