2 * Copyright (C) 2009 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.contacts;
19 import android.database.Cursor;
20 import android.database.DataSetObserver;
21 import android.util.Log;
22 import android.util.SparseIntArray;
23 import android.widget.SectionIndexer;
26 * SectionIndexer which is for "phonetically sortable" String. This class heavily depends on the
27 * algorithm of the SQL function "GET_PHONETICALLY_SORTABLE_STRING", whose implementation
30 public final class JapaneseContactListIndexer extends DataSetObserver implements SectionIndexer {
31 private static String TAG = "JapaneseContactListIndexer";
33 static private final String[] sSections = {
34 " ", // Sections of SectionIndexer should start with " " (some components assume it).
35 "\u3042", "\u304B", "\u3055", "\u305F", "\u306A", // a, ka, sa, ta, na
36 "\u306F", "\u307E", "\u3084", "\u3089", "\u308F", // ha, ma, ya, ra, wa
37 "\uFF21", "\uFF22", "\uFF23", "\uFF24", "\uFF25", // full-width ABCDE
38 "\uFF26", "\uFF27", "\uFF28", "\uFF29", "\uFF2A", // full-width FGHIJ
39 "\uFF2B", "\uFF2C", "\uFF2D", "\uFF2E", "\uFF2F", // full-width KLMNO
40 "\uFF30", "\uFF31", "\uFF32", "\uFF33", "\uFF34", // full-width PQRST
41 "\uFF35", "\uFF36", "\uFF37", "\uFF38", "\uFF39", // full-width UVWXY
42 "\uFF40", // full-width Z
43 "\u6570", "\u8A18" // alphabets, numbers, symbols
45 static private final int sSectionsLength = sSections.length;
47 private int mColumnIndex;
48 private Cursor mDataCursor;
49 private SparseIntArray mStringMap;
51 public JapaneseContactListIndexer(Cursor cursor, int columnIndex) {
52 int len = sSections.length;
53 mColumnIndex = columnIndex;
55 mStringMap = new SparseIntArray(sSectionsLength);
57 cursor.registerDataSetObserver(this);
61 public void setCursor(Cursor cursor) {
62 if (mDataCursor != null) {
63 mDataCursor.unregisterDataSetObserver(this);
67 mDataCursor.registerDataSetObserver(this);
71 private int getSectionCodePoint(int index) {
72 if (index < sSections.length - 2) {
73 return sSections[index].codePointAt(0);
74 } else if (index == sSections.length - 2) {
75 return 0xFF66; // Numbers are mapped from 0xFF66.
76 } else { // index == mSections.length - 1
77 return 0xFF70; // Symbols are mapped from 0xFF70.
81 public int getPositionForSection(int sectionIndex) {
82 final SparseIntArray stringMap = mStringMap;
83 final Cursor cursor = mDataCursor;
85 if (cursor == null || sectionIndex <= 0) {
89 if (sectionIndex >= sSectionsLength) {
90 sectionIndex = sSectionsLength - 1;
93 int savedCursorPos = cursor.getPosition();
95 String targetLetter = sSections[sectionIndex];
96 int key = targetLetter.codePointAt(0);
100 int tmp = stringMap.get(key, Integer.MIN_VALUE);
101 if (Integer.MIN_VALUE != tmp) {
106 int end = cursor.getCount();
110 // Note that sectionIndex > 0.
111 int prevLetter = sSections[sectionIndex - 1].codePointAt(0);
112 int prevLetterPos = stringMap.get(prevLetter, Integer.MIN_VALUE);
113 if (prevLetterPos != Integer.MIN_VALUE) {
118 // Do rough binary search if there are a lot of entries.
119 while (end - pos > 100) {
120 int tmp = (end + pos) / 2;
121 cursor.moveToPosition(tmp);
124 sort_name = cursor.getString(mColumnIndex);
125 if (sort_name == null || sort_name.length() == 0) {
126 // This should not happen, since sort_name field is created
127 // automatically when syncing to a server, or creating/editing
129 Log.e(TAG, "sort_name is null or its length is 0. index: " + tmp);
139 int codePoint = sort_name.codePointAt(0);
140 if (codePoint < getSectionCodePoint(sectionIndex)) {
147 for (cursor.moveToPosition(pos); !cursor.isAfterLast(); ++pos, cursor.moveToNext()) {
148 String sort_name = cursor.getString(mColumnIndex);
149 if (sort_name == null || sort_name.length() == 0) {
150 // This should not happen, since sort_name field is created
151 // automatically when syncing to a server, or creating/editing
153 Log.e(TAG, "sort_name is null or its length is 0. index: " + pos);
156 int codePoint = sort_name.codePointAt(0);
157 if (codePoint >= getSectionCodePoint(sectionIndex)) {
162 stringMap.put(key, pos);
163 cursor.moveToPosition(savedCursorPos);
167 public int getSectionForPosition(int position) {
168 // Not used in Contacts. Ignore for now.
172 public Object[] getSections() {
177 public void onChanged() {
183 public void onInvalidated() {
184 super.onInvalidated();