diff options
author | Mike Dodd <mdodd@google.com> | 2015-08-11 11:16:59 -0700 |
---|---|---|
committer | Mike Dodd <mdodd@google.com> | 2015-08-12 12:47:26 -0700 |
commit | d3b009ae55651f1e60950342468e3c37fdeb0796 (patch) | |
tree | bc4b489af52d0e2521e21167d2ad76a47256f348 /src/com/android/messaging/ui/contact/ContactSectionIndexer.java | |
parent | ef8c7abbcfc9c770385d6609a4b4bc70240ebdc4 (diff) | |
download | android_packages_apps_Messaging-d3b009ae55651f1e60950342468e3c37fdeb0796.tar.gz android_packages_apps_Messaging-d3b009ae55651f1e60950342468e3c37fdeb0796.tar.bz2 android_packages_apps_Messaging-d3b009ae55651f1e60950342468e3c37fdeb0796.zip |
Initial checkin of AOSP Messaging app.
b/23110861
Change-Id: I11db999bd10656801e618f78ab2b2ef74136fff1
Diffstat (limited to 'src/com/android/messaging/ui/contact/ContactSectionIndexer.java')
-rw-r--r-- | src/com/android/messaging/ui/contact/ContactSectionIndexer.java | 169 |
1 files changed, 169 insertions, 0 deletions
diff --git a/src/com/android/messaging/ui/contact/ContactSectionIndexer.java b/src/com/android/messaging/ui/contact/ContactSectionIndexer.java new file mode 100644 index 0000000..1d5abf3 --- /dev/null +++ b/src/com/android/messaging/ui/contact/ContactSectionIndexer.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2015 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.messaging.ui.contact; + +import android.database.Cursor; +import android.os.Bundle; +import android.provider.ContactsContract.Contacts; +import android.text.TextUtils; +import android.widget.SectionIndexer; + +import com.android.messaging.util.Assert; +import com.android.messaging.util.ContactUtil; +import com.android.messaging.util.LogUtil; + +import java.util.ArrayList; + +/** + * Indexes contact alphabetical sections so we can report to the fast scrolling list view + * where we are in the list when the user scrolls through the contact list, allowing us to show + * alphabetical indicators for the fast scroller as well as list section headers. + */ +public class ContactSectionIndexer implements SectionIndexer { + private String[] mSections; + private ArrayList<Integer> mSectionStartingPositions; + private static final String BLANK_HEADER_STRING = " "; + + public ContactSectionIndexer(final Cursor contactsCursor) { + buildIndexer(contactsCursor); + } + + @Override + public Object[] getSections() { + return mSections; + } + + @Override + public int getPositionForSection(final int sectionIndex) { + if (mSectionStartingPositions.isEmpty()) { + return 0; + } + // Clamp to the bounds of the section position array per Android API doc. + return mSectionStartingPositions.get( + Math.max(Math.min(sectionIndex, mSectionStartingPositions.size() - 1), 0)); + } + + @Override + public int getSectionForPosition(final int position) { + if (mSectionStartingPositions.isEmpty()) { + return 0; + } + + // Perform a binary search on the starting positions of the sections to the find the + // section for the position. + int left = 0; + int right = mSectionStartingPositions.size() - 1; + + // According to getSectionForPosition()'s doc, we should always clamp the value when the + // position is out of bound. + if (position <= mSectionStartingPositions.get(left)) { + return left; + } else if (position >= mSectionStartingPositions.get(right)) { + return right; + } + + while (left <= right) { + final int mid = (left + right) / 2; + final int startingPos = mSectionStartingPositions.get(mid); + final int nextStartingPos = mSectionStartingPositions.get(mid + 1); + if (position >= startingPos && position < nextStartingPos) { + return mid; + } else if (position < startingPos) { + right = mid - 1; + } else if (position >= nextStartingPos) { + left = mid + 1; + } + } + Assert.fail("Invalid section indexer state: couldn't find section for pos " + position); + return -1; + } + + private boolean buildIndexerFromCursorExtras(final Cursor cursor) { + if (cursor == null) { + return false; + } + final Bundle cursorExtras = cursor.getExtras(); + if (cursorExtras == null) { + return false; + } + final String[] sections = cursorExtras.getStringArray( + Contacts.EXTRA_ADDRESS_BOOK_INDEX_TITLES); + final int[] counts = cursorExtras.getIntArray(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS); + if (sections == null || counts == null) { + return false; + } + + if (sections.length != counts.length) { + return false; + } + + this.mSections = sections; + mSectionStartingPositions = new ArrayList<Integer>(counts.length); + int position = 0; + for (int i = 0; i < counts.length; i++) { + if (TextUtils.isEmpty(mSections[i])) { + mSections[i] = BLANK_HEADER_STRING; + } else if (!mSections[i].equals(BLANK_HEADER_STRING)) { + mSections[i] = mSections[i].trim(); + } + + mSectionStartingPositions.add(position); + position += counts[i]; + } + return true; + } + + private void buildIndexerFromDisplayNames(final Cursor cursor) { + // Loop through the contact cursor and get the starting position for each first character. + // The result is stored into two arrays, one for the section header (i.e. the first + // character), and one for the starting position, which is guaranteed to be sorted in + // ascending order. + final ArrayList<String> sections = new ArrayList<String>(); + mSectionStartingPositions = new ArrayList<Integer>(); + if (cursor != null) { + cursor.moveToPosition(-1); + int currentPosition = 0; + while (cursor.moveToNext()) { + // The sort key is typically the contact's display name, so for example, a contact + // named "Bob" will go into section "B". The Contacts provider generally uses a + // a slightly more sophisticated heuristic, but as a fallback this is good enough. + final String sortKey = cursor.getString(ContactUtil.INDEX_SORT_KEY); + final String section = TextUtils.isEmpty(sortKey) ? BLANK_HEADER_STRING : + sortKey.substring(0, 1).toUpperCase(); + + final int lastIndex = sections.size() - 1; + final String currentSection = lastIndex >= 0 ? sections.get(lastIndex) : null; + if (!TextUtils.equals(currentSection, section)) { + sections.add(section); + mSectionStartingPositions.add(currentPosition); + } + currentPosition++; + } + } + mSections = new String[sections.size()]; + sections.toArray(mSections); + } + + private void buildIndexer(final Cursor cursor) { + // First check if we get indexer label extras from the contact provider; if not, fall back + // to building from display names. + if (!buildIndexerFromCursorExtras(cursor)) { + LogUtil.w(LogUtil.BUGLE_TAG, "contact provider didn't provide contact label " + + "information, fall back to using display name!"); + buildIndexerFromDisplayNames(cursor); + } + } +} |