blob: b4ac2b4f2174743db0899c0f2c91ff33ff51f67f [file] [log] [blame]
Chiao Cheng89437e82012-11-01 13:41:51 -07001/*
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 */
Gary Mai69c182a2016-12-05 13:07:03 -080016package com.android.contacts.list;
Chiao Cheng89437e82012-11-01 13:41:51 -070017
18import android.content.Context;
19import android.view.View;
20import android.view.ViewGroup;
21import android.widget.ListView;
22import android.widget.SectionIndexer;
23
24/**
25 * A list adapter that supports section indexer and a pinned header.
26 */
27public 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
Chiao Cheng89437e82012-11-01 13:41:51 -070073 public boolean isSectionHeaderDisplayEnabled() {
74 return mSectionHeaderDisplayEnabled;
75 }
76
77 public void setSectionHeaderDisplayEnabled(boolean flag) {
78 this.mSectionHeaderDisplayEnabled = flag;
79 }
80
81 public int getIndexedPartition() {
82 return mIndexedPartition;
83 }
84
85 public void setIndexedPartition(int partition) {
86 this.mIndexedPartition = partition;
87 }
88
89 public SectionIndexer getIndexer() {
90 return mIndexer;
91 }
92
93 public void setIndexer(SectionIndexer indexer) {
94 mIndexer = indexer;
95 mPlacementCache.invalidate();
96 }
97
98 public Object[] getSections() {
99 if (mIndexer == null) {
100 return new String[] { " " };
101 } else {
102 return mIndexer.getSections();
103 }
104 }
105
106 /**
107 * @return relative position of the section in the indexed partition
108 */
109 public int getPositionForSection(int sectionIndex) {
110 if (mIndexer == null) {
111 return -1;
112 }
113
114 return mIndexer.getPositionForSection(sectionIndex);
115 }
116
117 /**
118 * @param position relative position in the indexed partition
119 */
120 public int getSectionForPosition(int position) {
121 if (mIndexer == null) {
122 return -1;
123 }
124
125 return mIndexer.getSectionForPosition(position);
126 }
127
128 @Override
129 public int getPinnedHeaderCount() {
130 if (isSectionHeaderDisplayEnabled()) {
131 return super.getPinnedHeaderCount() + 1;
132 } else {
133 return super.getPinnedHeaderCount();
134 }
135 }
136
137 @Override
138 public View getPinnedHeaderView(int viewIndex, View convertView, ViewGroup parent) {
139 if (isSectionHeaderDisplayEnabled() && viewIndex == getPinnedHeaderCount() - 1) {
140 if (mHeader == null) {
141 mHeader = createPinnedSectionHeaderView(mContext, parent);
Chiao Cheng89437e82012-11-01 13:41:51 -0700142 }
143 return mHeader;
144 } else {
145 return super.getPinnedHeaderView(viewIndex, convertView, parent);
146 }
147 }
148
149 @Override
150 public void configurePinnedHeaders(PinnedHeaderListView listView) {
151 super.configurePinnedHeaders(listView);
152
153 if (!isSectionHeaderDisplayEnabled()) {
154 return;
155 }
156
157 int index = getPinnedHeaderCount() - 1;
158 if (mIndexer == null || getCount() == 0) {
159 listView.setHeaderInvisible(index, false);
160 } else {
161 int listPosition = listView.getPositionAt(listView.getTotalTopPinnedHeaderHeight());
162 int position = listPosition - listView.getHeaderViewsCount();
163
164 int section = -1;
165 int partition = getPartitionForPosition(position);
166 if (partition == mIndexedPartition) {
167 int offset = getOffsetInPartition(position);
168 if (offset != -1) {
169 section = getSectionForPosition(offset);
170 }
171 }
172
173 if (section == -1) {
174 listView.setHeaderInvisible(index, false);
175 } else {
Andrew Lee020ba622014-04-25 15:26:35 -0700176 View topChild = listView.getChildAt(listPosition);
177 if (topChild != null) {
178 // Match the pinned header's height to the height of the list item.
179 mHeader.setMinimumHeight(topChild.getMeasuredHeight());
180 }
Chiao Cheng89437e82012-11-01 13:41:51 -0700181 setPinnedSectionTitle(mHeader, (String)mIndexer.getSections()[section]);
Andrew Leeb44032f2014-04-24 18:09:54 -0700182
Chiao Cheng89437e82012-11-01 13:41:51 -0700183 // Compute the item position where the current partition begins
184 int partitionStart = getPositionForPartition(mIndexedPartition);
185 if (hasHeader(mIndexedPartition)) {
186 partitionStart++;
187 }
188
189 // Compute the item position where the next section begins
190 int nextSectionPosition = partitionStart + getPositionForSection(section + 1);
191 boolean isLastInSection = position == nextSectionPosition - 1;
192 listView.setFadingHeader(index, listPosition, isLastInSection);
193 }
194 }
195 }
196
197 /**
198 * Computes the item's placement within its section and populates the {@code placement}
199 * object accordingly. Please note that the returned object is volatile and should be
200 * copied if the result needs to be used later.
201 */
202 public Placement getItemPlacementInSection(int position) {
203 if (mPlacementCache.position == position) {
204 return mPlacementCache;
205 }
206
207 mPlacementCache.position = position;
208 if (isSectionHeaderDisplayEnabled()) {
209 int section = getSectionForPosition(position);
210 if (section != -1 && getPositionForSection(section) == position) {
211 mPlacementCache.firstInSection = true;
212 mPlacementCache.sectionHeader = (String)getSections()[section];
213 } else {
214 mPlacementCache.firstInSection = false;
215 mPlacementCache.sectionHeader = null;
216 }
217
218 mPlacementCache.lastInSection = (getPositionForSection(section + 1) - 1 == position);
219 } else {
220 mPlacementCache.firstInSection = false;
221 mPlacementCache.lastInSection = false;
222 mPlacementCache.sectionHeader = null;
223 }
224 return mPlacementCache;
225 }
226}