Back to project page fiction.
The source code is released under:
GNU General Public License
If you think the Android project fiction listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
/* * Copyright (C) 2008 The Android Open Source Project */*from ww w .ja v a 2 s .c om*/ * 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.lithiumli.fiction.ui; import android.database.Cursor; import android.database.DataSetObserver; import android.provider.MediaStore; import android.util.SparseIntArray; import android.widget.SectionIndexer; /** * Modified to remove "the", "an", "a", etc. */ public class SongsAlphabetIndexer extends DataSetObserver implements SectionIndexer { /** * Cursor that is used by the adapter of the list view. */ protected Cursor mDataCursor; /** * The index of the cursor column that this list is sorted on. */ protected int mColumnIndex; /** * The string of characters that make up the indexing sections. */ protected CharSequence mAlphabet; /** * Cached length of the alphabet array. */ private int mAlphabetLength; /** * This contains a cache of the computed indices so far. It will get reset whenever * the dataset changes or the cursor changes. */ private SparseIntArray mAlphaMap; /** * Use a collator to compare strings in a localized manner. */ private java.text.Collator mCollator; /** * The section array converted from the alphabet string. */ private String[] mAlphabetArray; private String[] mAlphabetSections; /** * Constructs the indexer. * @param cursor the cursor containing the data set * @param sortedColumnIndex the column number in the cursor that is sorted * alphabetically * @param alphabet string containing the alphabet, with space as the first character. * For example, use the string " ABCDEFGHIJKLMNOPQRSTUVWXYZ" for English indexing. * The characters must be uppercase and be sorted in ascii/unicode order. Basically * characters in the alphabet will show up as preview letters. */ public SongsAlphabetIndexer(Cursor cursor, int sortedColumnIndex, CharSequence alphabet) { mDataCursor = cursor; mColumnIndex = sortedColumnIndex; mAlphabet = alphabet; mAlphabetLength = alphabet.length(); mAlphabetArray = new String[mAlphabetLength]; for (int i = 0; i < mAlphabetLength; i++) { mAlphabetArray[i] = MediaStore.Audio.keyFor(Character.toString(mAlphabet.charAt(i))); } mAlphabetSections = ((String) alphabet).split("(?!^)"); mAlphaMap = new SparseIntArray(mAlphabetLength); if (cursor != null) { cursor.registerDataSetObserver(this); } // Get a Collator for the current locale for string comparisons. mCollator = java.text.Collator.getInstance(); mCollator.setStrength(java.text.Collator.PRIMARY); } /** * Returns the section array constructed from the alphabet provided in the constructor. * @return the section array */ public Object[] getSections() { return mAlphabetSections; } /** * Sets a new cursor as the data set and resets the cache of indices. * @param cursor the new cursor to use as the data set */ public void setCursor(Cursor cursor) { if (mDataCursor != null) { mDataCursor.unregisterDataSetObserver(this); } mDataCursor = cursor; if (cursor != null) { mDataCursor.registerDataSetObserver(this); } mAlphaMap.clear(); } /** * Default implementation compares the first character of word with letter. */ protected int compare(String word, String letter) { final String firstLetter; if (word.length() == 0) { firstLetter = " "; } else { firstLetter = word.substring(0, 3); } return firstLetter.compareTo(letter); } /** * Performs a binary search or cache lookup to find the first row that * matches a given section's starting letter. * @param sectionIndex the section to search for * @return the row index of the first occurrence, or the nearest next letter. * For instance, if searching for "T" and no "T" is found, then the first * row starting with "U" or any higher letter is returned. If there is no * data following "T" at all, then the list size is returned. */ public int getPositionForSection(int sectionIndex) { final SparseIntArray alphaMap = mAlphaMap; final Cursor cursor = mDataCursor; if (cursor == null || mAlphabet == null) { return 0; } // Check bounds if (sectionIndex <= 0) { return 0; } if (sectionIndex >= mAlphabetLength) { sectionIndex = mAlphabetLength - 1; } int savedCursorPos = cursor.getPosition(); int count = cursor.getCount(); int start = 0; int end = count; int pos; char letter = mAlphabet.charAt(sectionIndex); String targetLetter = mAlphabetArray[sectionIndex]; int key = letter; // Check map if (Integer.MIN_VALUE != (pos = alphaMap.get(key, Integer.MIN_VALUE))) { // Is it approximate? Using negative value to indicate that it's // an approximation and positive value when it is the accurate // position. if (pos < 0) { pos = -pos; end = pos; } else { // Not approximate, this is the confirmed start of section, return it return pos; } } // Do we have the position of the previous section? if (sectionIndex > 0) { int prevLetter = mAlphabet.charAt(sectionIndex - 1); int prevLetterPos = alphaMap.get(prevLetter, Integer.MIN_VALUE); if (prevLetterPos != Integer.MIN_VALUE) { start = Math.abs(prevLetterPos); } } // Now that we have a possibly optimized start and end, let's binary search pos = (end + start) / 2; while (pos < end) { // Get letter at pos cursor.moveToPosition(pos); String curName = cursor.getString(mColumnIndex); if (curName == null) { if (pos == 0) { break; } else { pos--; continue; } } curName = MediaStore.Audio.keyFor(curName); int diff = compare(curName, targetLetter); if (diff != 0) { if (diff < 0) { start = pos + 1; if (start >= count) { pos = count; break; } } else { end = pos; } } else { // They're the same, but that doesn't mean it's the start if (start == pos) { // This is it break; } else { // Need to go further lower to find the starting row end = pos; } } pos = (start + end) / 2; } alphaMap.put(key, pos); cursor.moveToPosition(savedCursorPos); return pos; } /** * Returns the section index for a given position in the list by querying the item * and comparing it with all items in the section array. */ public int getSectionForPosition(int position) { int savedCursorPos = mDataCursor.getPosition(); mDataCursor.moveToPosition(position); String curName = mDataCursor.getString(mColumnIndex); curName = MediaStore.Audio.keyFor(curName); mDataCursor.moveToPosition(savedCursorPos); // Linear search, as there are only a few items in the section index // Could speed this up later if it actually gets used. for (int i = 0; i < mAlphabetLength; i++) { if (curName.startsWith(mAlphabetArray[i])) { return i; } } return 0; // Don't recognize the letter - falls under zero'th section } /* * @hide */ @Override public void onChanged() { super.onChanged(); mAlphaMap.clear(); } /* * @hide */ @Override public void onInvalidated() { super.onInvalidated(); mAlphaMap.clear(); } }