• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
invalidate()47         public void invalidate() {
48             position = ListView.INVALID_POSITION;
49         }
50     }
51 
52     private Placement mPlacementCache = new Placement();
53 
54     /**
55      * Constructor.
56      */
IndexerListAdapter(Context context)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      */
createPinnedSectionHeaderView(Context context, ViewGroup parent)66     protected abstract View createPinnedSectionHeaderView(Context context, ViewGroup parent);
67 
68     /**
69      * Sets the title in the pinned header as the user scrolls.
70      */
setPinnedSectionTitle(View pinnedHeaderView, String title)71     protected abstract void setPinnedSectionTitle(View pinnedHeaderView, String title);
72 
73     /**
74      * Sets the contacts count in the pinned header.
75      */
setPinnedHeaderContactsCount(View header)76     protected abstract void setPinnedHeaderContactsCount(View header);
77 
78     /**
79      * clears the contacts count in the pinned header and makes the view invisible.
80      */
clearPinnedHeaderContactsCount(View header)81     protected abstract void clearPinnedHeaderContactsCount(View header);
82 
isSectionHeaderDisplayEnabled()83     public boolean isSectionHeaderDisplayEnabled() {
84         return mSectionHeaderDisplayEnabled;
85     }
86 
setSectionHeaderDisplayEnabled(boolean flag)87     public void setSectionHeaderDisplayEnabled(boolean flag) {
88         this.mSectionHeaderDisplayEnabled = flag;
89     }
90 
getIndexedPartition()91     public int getIndexedPartition() {
92         return mIndexedPartition;
93     }
94 
setIndexedPartition(int partition)95     public void setIndexedPartition(int partition) {
96         this.mIndexedPartition = partition;
97     }
98 
getIndexer()99     public SectionIndexer getIndexer() {
100         return mIndexer;
101     }
102 
setIndexer(SectionIndexer indexer)103     public void setIndexer(SectionIndexer indexer) {
104         mIndexer = indexer;
105         mPlacementCache.invalidate();
106     }
107 
getSections()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      */
getPositionForSection(int sectionIndex)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      */
getSectionForPosition(int position)130     public int getSectionForPosition(int position) {
131         if (mIndexer == null) {
132             return -1;
133         }
134 
135         return mIndexer.getSectionForPosition(position);
136     }
137 
138     @Override
getPinnedHeaderCount()139     public int getPinnedHeaderCount() {
140         if (isSectionHeaderDisplayEnabled()) {
141             return super.getPinnedHeaderCount() + 1;
142         } else {
143             return super.getPinnedHeaderCount();
144         }
145     }
146 
147     @Override
getPinnedHeaderView(int viewIndex, View convertView, ViewGroup parent)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
configurePinnedHeaders(PinnedHeaderListView listView)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      */
getItemPlacementInSection(int position)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