/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.contacts.widget; import android.content.Context; import android.view.View; import android.view.ViewGroup; import android.widget.ListView; import android.widget.SectionIndexer; /** * A list adapter that supports section indexer and a pinned header. */ public abstract class IndexerListAdapter extends PinnedHeaderListAdapter implements SectionIndexer { protected Context mContext; private SectionIndexer mIndexer; private int mIndexedPartition = 0; private boolean mSectionHeaderDisplayEnabled; private View mHeader; /** * An item view is displayed differently depending on whether it is placed * at the beginning, middle or end of a section. It also needs to know the * section header when it is at the beginning of a section. This object * captures all this configuration. */ public static final class Placement { private int position = ListView.INVALID_POSITION; public boolean firstInSection; public boolean lastInSection; public String sectionHeader; public void invalidate() { position = ListView.INVALID_POSITION; } } private Placement mPlacementCache = new Placement(); /** * Constructor. */ public IndexerListAdapter(Context context) { super(context); mContext = context; } /** * Creates a section header view that will be pinned at the top of the list * as the user scrolls. */ protected abstract View createPinnedSectionHeaderView(Context context, ViewGroup parent); /** * Sets the title in the pinned header as the user scrolls. */ protected abstract void setPinnedSectionTitle(View pinnedHeaderView, String title); /** * Sets the contacts count in the pinned header. */ protected abstract void setPinnedHeaderContactsCount(View header); /** * clears the contacts count in the pinned header and makes the view invisible. */ protected abstract void clearPinnedHeaderContactsCount(View header); public boolean isSectionHeaderDisplayEnabled() { return mSectionHeaderDisplayEnabled; } public void setSectionHeaderDisplayEnabled(boolean flag) { this.mSectionHeaderDisplayEnabled = flag; } public int getIndexedPartition() { return mIndexedPartition; } public void setIndexedPartition(int partition) { this.mIndexedPartition = partition; } public SectionIndexer getIndexer() { return mIndexer; } public void setIndexer(SectionIndexer indexer) { mIndexer = indexer; mPlacementCache.invalidate(); } public Object[] getSections() { if (mIndexer == null) { return new String[] { " " }; } else { return mIndexer.getSections(); } } /** * @return relative position of the section in the indexed partition */ public int getPositionForSection(int sectionIndex) { if (mIndexer == null) { return -1; } return mIndexer.getPositionForSection(sectionIndex); } /** * @param position relative position in the indexed partition */ public int getSectionForPosition(int position) { if (mIndexer == null) { return -1; } return mIndexer.getSectionForPosition(position); } @Override public int getPinnedHeaderCount() { if (isSectionHeaderDisplayEnabled()) { return super.getPinnedHeaderCount() + 1; } else { return super.getPinnedHeaderCount(); } } @Override public View getPinnedHeaderView(int viewIndex, View convertView, ViewGroup parent) { if (isSectionHeaderDisplayEnabled() && viewIndex == getPinnedHeaderCount() - 1) { if (mHeader == null) { mHeader = createPinnedSectionHeaderView(mContext, parent); } return mHeader; } else { return super.getPinnedHeaderView(viewIndex, convertView, parent); } } @Override public void configurePinnedHeaders(PinnedHeaderListView listView) { super.configurePinnedHeaders(listView); if (!isSectionHeaderDisplayEnabled()) { return; } int index = getPinnedHeaderCount() - 1; if (mIndexer == null || getCount() == 0) { listView.setHeaderInvisible(index, false); } else { int listPosition = listView.getPositionAt(listView.getTotalTopPinnedHeaderHeight()); int position = listPosition - listView.getHeaderViewsCount(); int section = -1; int partition = getPartitionForPosition(position); if (partition == mIndexedPartition) { int offset = getOffsetInPartition(position); if (offset != -1) { section = getSectionForPosition(offset); } } if (section == -1) { listView.setHeaderInvisible(index, false); } else { setPinnedSectionTitle(mHeader, (String)mIndexer.getSections()[section]); if (section == 0) { setPinnedHeaderContactsCount(mHeader); } else { clearPinnedHeaderContactsCount(mHeader); } // Compute the item position where the current partition begins int partitionStart = getPositionForPartition(mIndexedPartition); if (hasHeader(mIndexedPartition)) { partitionStart++; } // Compute the item position where the next section begins int nextSectionPosition = partitionStart + getPositionForSection(section + 1); boolean isLastInSection = position == nextSectionPosition - 1; listView.setFadingHeader(index, listPosition, isLastInSection); } } } /** * Computes the item's placement within its section and populates the {@code placement} * object accordingly. Please note that the returned object is volatile and should be * copied if the result needs to be used later. */ public Placement getItemPlacementInSection(int position) { if (mPlacementCache.position == position) { return mPlacementCache; } mPlacementCache.position = position; if (isSectionHeaderDisplayEnabled()) { int section = getSectionForPosition(position); if (section != -1 && getPositionForSection(section) == position) { mPlacementCache.firstInSection = true; mPlacementCache.sectionHeader = (String)getSections()[section]; } else { mPlacementCache.firstInSection = false; mPlacementCache.sectionHeader = null; } mPlacementCache.lastInSection = (getPositionForSection(section + 1) - 1 == position); } else { mPlacementCache.firstInSection = false; mPlacementCache.lastInSection = false; mPlacementCache.sectionHeader = null; } return mPlacementCache; } }