Loading src/com/android/contacts/ContactsListActivity.java +78 −30 Original line number Diff line number Diff line Loading @@ -73,6 +73,7 @@ import android.widget.TextView; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Locale; /** * Displays a list of contacts. Usually is embedded into the ContactsActivity. Loading Loading @@ -169,6 +170,7 @@ public final class ContactsListActivity extends ListActivity static final String NAME_COLUMN = People.DISPLAY_NAME; static final String SORT_STRING = People.SORT_STRING; static final String[] CONTACTS_PROJECTION = new String[] { People._ID, // 0 Loading @@ -180,6 +182,7 @@ public final class ContactsListActivity extends ListActivity People.PRIMARY_PHONE_ID, // 6 People.PRIMARY_EMAIL_ID, // 7 People.PRESENCE_STATUS, // 8 SORT_STRING, // 9 }; static final String[] STREQUENT_PROJECTION = new String[] { Loading Loading @@ -227,6 +230,7 @@ public final class ContactsListActivity extends ListActivity static final int PRIMARY_EMAIL_ID_COLUMN_INDEX = 7; static final int SERVER_STATUS_COLUMN_INDEX = 8; static final int PHOTO_COLUMN_INDEX = 9; static final int SORT_STRING_INDEX = 9; static final int PHONES_PERSON_ID_INDEX = 6; static final int CONTACT_METHODS_PERSON_ID_INDEX = 6; Loading @@ -235,8 +239,6 @@ public final class ContactsListActivity extends ListActivity static final int DISPLAY_GROUP_INDEX_ALL_CONTACTS_WITH_PHONES = 1; static final int DISPLAY_GROUP_INDEX_MY_CONTACTS = 2; static final String SORT_ORDER = NAME_COLUMN + " COLLATE LOCALIZED ASC"; private static final int QUERY_TOKEN = 42; private static final String[] GROUPS_PROJECTION = new String[] { Loading Loading @@ -1137,6 +1139,15 @@ public final class ContactsListActivity extends ListActivity } } private static String getSortOrder(String[] projectionType) { if (Locale.getDefault().equals(Locale.JAPAN) && projectionType == CONTACTS_PROJECTION) { return SORT_STRING + " ASC"; } else { return NAME_COLUMN + " COLLATE LOCALIZED ASC"; } } void startQuery() { mAdapter.setLoading(true); Loading @@ -1147,7 +1158,8 @@ public final class ContactsListActivity extends ListActivity switch (mMode) { case MODE_GROUP: mQueryHandler.startQuery(QUERY_TOKEN, null, mGroupUri, CONTACTS_PROJECTION, null, null, SORT_ORDER); mGroupUri, CONTACTS_PROJECTION, null, null, getSortOrder(CONTACTS_PROJECTION)); break; case MODE_ALL_CONTACTS: Loading @@ -1155,18 +1167,20 @@ public final class ContactsListActivity extends ListActivity case MODE_PICK_OR_CREATE_CONTACT: case MODE_INSERT_OR_EDIT_CONTACT: mQueryHandler.startQuery(QUERY_TOKEN, null, People.CONTENT_URI, CONTACTS_PROJECTION, null, null, SORT_ORDER); null, null, getSortOrder(CONTACTS_PROJECTION)); break; case MODE_WITH_PHONES: mQueryHandler.startQuery(QUERY_TOKEN, null, People.CONTENT_URI, CONTACTS_PROJECTION, People.PRIMARY_PHONE_ID + " IS NOT NULL", null, SORT_ORDER); People.PRIMARY_PHONE_ID + " IS NOT NULL", null, getSortOrder(CONTACTS_PROJECTION)); break; case MODE_QUERY: { mQuery = getIntent().getStringExtra(SearchManager.QUERY); mQueryHandler.startQuery(QUERY_TOKEN, null, getPeopleFilterUri(mQuery), CONTACTS_PROJECTION, null, null, SORT_ORDER); CONTACTS_PROJECTION, null, null, getSortOrder(CONTACTS_PROJECTION)); break; } Loading @@ -1183,26 +1197,30 @@ public final class ContactsListActivity extends ListActivity mCreateData = ssp; } if ("mailto".equals(scheme)) { mCreateExtras = new Bundle(); mCreateExtras.putAll(getIntent().getExtras()); Bundle originalExtras = getIntent().getExtras(); if (originalExtras != null) { mCreateExtras.putAll(originalExtras); } if ("mailto".equals(scheme)) { mCreateExtras.putString(Intents.Insert.EMAIL, ssp); mCreatePersonIndex = CONTACT_METHODS_PERSON_ID_INDEX; mQueryHandler.startQuery(QUERY_TOKEN, null, ContactMethods.CONTENT_URI, CONTACT_METHODS_PROJECTION, ContactMethodsColumns.KIND + "=" + Contacts.KIND_EMAIL + " AND " + ContactMethods.DATA + "=?", new String[] { ssp }, SORT_ORDER); ContactMethods.DATA + "=?", new String[] { ssp }, getSortOrder(CONTACT_METHODS_PROJECTION)); } else if ("tel".equals(scheme)) { mCreateExtras = new Bundle(); mCreateExtras.putAll(getIntent().getExtras()); mCreateExtras.putString(Intents.Insert.PHONE, ssp); mCreatePersonIndex = PHONES_PERSON_ID_INDEX; mQueryHandler.startQuery(QUERY_TOKEN, null, Uri.withAppendedPath(Phones.CONTENT_FILTER_URL, ssp), PHONES_PROJECTION, null, null, SORT_ORDER); PHONES_PROJECTION, null, null, getSortOrder(PHONES_PROJECTION)); } else { Log.w(TAG, "Invalid intent:" + getIntent()); Loading @@ -1213,14 +1231,16 @@ public final class ContactsListActivity extends ListActivity } case MODE_STARRED: mQueryHandler.startQuery(QUERY_TOKEN, null, People.CONTENT_URI, CONTACTS_PROJECTION, People.STARRED + "=1", null, SORT_ORDER); mQueryHandler.startQuery(QUERY_TOKEN, null, People.CONTENT_URI, CONTACTS_PROJECTION, People.STARRED + "=1", null, getSortOrder(CONTACTS_PROJECTION)); break; case MODE_FREQUENT: mQueryHandler.startQuery(QUERY_TOKEN, null, People.CONTENT_URI, CONTACTS_PROJECTION, mQueryHandler.startQuery(QUERY_TOKEN, null, People.CONTENT_URI, CONTACTS_PROJECTION, People.TIMES_CONTACTED + " > 0", null, People.TIMES_CONTACTED + " DESC, " + NAME_COLUMN + " ASC"); People.TIMES_CONTACTED + " DESC, " + getSortOrder(CONTACTS_PROJECTION)); break; case MODE_STREQUENT: Loading @@ -1231,13 +1251,14 @@ public final class ContactsListActivity extends ListActivity case MODE_PICK_PHONE: mQueryHandler.startQuery(QUERY_TOKEN, null, Phones.CONTENT_URI, PHONES_PROJECTION, null, null, SORT_ORDER); null, null, getSortOrder(PHONES_PROJECTION)); break; case MODE_PICK_POSTAL: mQueryHandler.startQuery(QUERY_TOKEN, null, ContactMethods.CONTENT_URI, CONTACT_METHODS_PROJECTION, ContactMethods.KIND + "=" + Contacts.KIND_POSTAL, null, SORT_ORDER); ContactMethods.KIND + "=" + Contacts.KIND_POSTAL, null, getSortOrder(CONTACT_METHODS_PROJECTION)); break; } } Loading @@ -1259,7 +1280,8 @@ public final class ContactsListActivity extends ListActivity } else { uri = Uri.withAppendedPath(mGroupFilterUri, Uri.encode(filter)); } return resolver.query(uri, CONTACTS_PROJECTION, null, null, SORT_ORDER); return resolver.query(uri, CONTACTS_PROJECTION, null, null, getSortOrder(CONTACTS_PROJECTION)); } case MODE_ALL_CONTACTS: Loading @@ -1267,23 +1289,25 @@ public final class ContactsListActivity extends ListActivity case MODE_PICK_OR_CREATE_CONTACT: case MODE_INSERT_OR_EDIT_CONTACT: { return resolver.query(getPeopleFilterUri(filter), CONTACTS_PROJECTION, null, null, SORT_ORDER); getSortOrder(CONTACTS_PROJECTION)); } case MODE_WITH_PHONES: { return resolver.query(getPeopleFilterUri(filter), CONTACTS_PROJECTION, People.PRIMARY_PHONE_ID + " IS NOT NULL", null, SORT_ORDER); People.PRIMARY_PHONE_ID + " IS NOT NULL", null, getSortOrder(CONTACTS_PROJECTION)); } case MODE_STARRED: { return resolver.query(getPeopleFilterUri(filter), CONTACTS_PROJECTION, People.STARRED + "=1", null, SORT_ORDER); People.STARRED + "=1", null, getSortOrder(CONTACTS_PROJECTION)); } case MODE_FREQUENT: { return resolver.query(getPeopleFilterUri(filter), CONTACTS_PROJECTION, People.TIMES_CONTACTED + " > 0", null, People.TIMES_CONTACTED + " DESC, " + NAME_COLUMN + " ASC"); People.TIMES_CONTACTED + " DESC, " + getSortOrder(CONTACTS_PROJECTION)); } case MODE_STREQUENT: { Loading @@ -1305,7 +1329,8 @@ public final class ContactsListActivity extends ListActivity } else { uri = Phones.CONTENT_URI; } return resolver.query(uri, PHONES_PROJECTION, null, null, SORT_ORDER); return resolver.query(uri, PHONES_PROJECTION, null, null, getSortOrder(PHONES_PROJECTION)); } } throw new UnsupportedOperationException("filtering not allowed in mode " + mMode); Loading Loading @@ -1475,7 +1500,7 @@ public final class ContactsListActivity extends ListActivity private final class ContactItemListAdapter extends ResourceCursorAdapter implements SectionIndexer { private AlphabetIndexer mIndexer; private SectionIndexer mIndexer; private String mAlphabet; private boolean mLoading = true; private CharSequence mUnknownNameText; Loading Loading @@ -1507,6 +1532,14 @@ public final class ContactsListActivity extends ListActivity } } private SectionIndexer getNewIndexer(Cursor cursor) { if (Locale.getDefault().equals(Locale.JAPAN)) { return new JapaneseContactListIndexer(cursor, SORT_STRING_INDEX); } else { return new AlphabetIndexer(cursor, NAME_COLUMN_INDEX, mAlphabet); } } /** * Callback on the UI thread when the content observer on the backing cursor fires. * Instead of calling requery we need to do an async query so that the requery doesn't Loading Loading @@ -1682,9 +1715,21 @@ public final class ContactsListActivity extends ListActivity private void updateIndexer(Cursor cursor) { if (mIndexer == null) { mIndexer = new AlphabetIndexer(cursor, NAME_COLUMN_INDEX, mAlphabet); mIndexer = getNewIndexer(cursor); } else { mIndexer.setCursor(cursor); if (Locale.getDefault().equals(Locale.JAPAN)) { if (mIndexer instanceof JapaneseContactListIndexer) { ((JapaneseContactListIndexer)mIndexer).setCursor(cursor); } else { mIndexer = getNewIndexer(cursor); } } else { if (mIndexer instanceof AlphabetIndexer) { ((AlphabetIndexer)mIndexer).setCursor(cursor); } else { mIndexer = getNewIndexer(cursor); } } } } Loading Loading @@ -1716,13 +1761,16 @@ public final class ContactsListActivity extends ListActivity // No cursor, the section doesn't exist so just return 0 return 0; } mIndexer = new AlphabetIndexer(cursor, NAME_COLUMN_INDEX, mAlphabet); mIndexer = getNewIndexer(cursor); } return mIndexer.getPositionForSection(sectionIndex); } public int getSectionForPosition(int position) { // Note: JapaneseContactListIndexer depends on the fact // this method always returns 0. If you change this, // please care it too. return 0; } } Loading src/com/android/contacts/JapaneseContactListIndexer.java 0 → 100644 +187 −0 Original line number Diff line number Diff line /* * Copyright (C) 2009 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; import android.database.Cursor; import android.database.DataSetObserver; import android.util.Log; import android.util.SparseIntArray; import android.widget.SectionIndexer; /** * SectionIndexer which is for "phonetically sortable" String. This class heavily depends on the * algorithm of the SQL function "GET_PHONETICALLY_SORTABLE_STRING", whose implementation * is written in C++. */ public final class JapaneseContactListIndexer extends DataSetObserver implements SectionIndexer { private static String TAG = "JapaneseContactListIndexer"; static private final String[] sSections = { " ", // Sections of SectionIndexer should start with " " (some components assume it). "\u3042", "\u304B", "\u3055", "\u305F", "\u306A", // a, ka, sa, ta, na "\u306F", "\u307E", "\u3084", "\u3089", "\u308F", // ha, ma, ya, ra, wa "\uFF21", "\uFF22", "\uFF23", "\uFF24", "\uFF25", // full-width ABCDE "\uFF26", "\uFF27", "\uFF28", "\uFF29", "\uFF2A", // full-width FGHIJ "\uFF2B", "\uFF2C", "\uFF2D", "\uFF2E", "\uFF2F", // full-width KLMNO "\uFF30", "\uFF31", "\uFF32", "\uFF33", "\uFF34", // full-width PQRST "\uFF35", "\uFF36", "\uFF37", "\uFF38", "\uFF39", // full-width UVWXY "\uFF40", // full-width Z "\u6570", "\u8A18" // alphabets, numbers, symbols }; static private final int sSectionsLength = sSections.length; private int mColumnIndex; private Cursor mDataCursor; private SparseIntArray mStringMap; public JapaneseContactListIndexer(Cursor cursor, int columnIndex) { int len = sSections.length; mColumnIndex = columnIndex; mDataCursor = cursor; mStringMap = new SparseIntArray(sSectionsLength); if (cursor != null) { cursor.registerDataSetObserver(this); } } public void setCursor(Cursor cursor) { if (mDataCursor != null) { mDataCursor.unregisterDataSetObserver(this); } mDataCursor = cursor; if (cursor != null) { mDataCursor.registerDataSetObserver(this); } } private int getSectionCodePoint(int index) { if (index < sSections.length - 2) { return sSections[index].codePointAt(0); } else if (index == sSections.length - 2) { return 0xFF66; // Numbers are mapped from 0xFF66. } else { // index == mSections.length - 1 return 0xFF70; // Symbols are mapped from 0xFF70. } } public int getPositionForSection(int sectionIndex) { final SparseIntArray stringMap = mStringMap; final Cursor cursor = mDataCursor; if (cursor == null || sectionIndex <= 0) { return 0; } if (sectionIndex >= sSectionsLength) { sectionIndex = sSectionsLength - 1; } int savedCursorPos = cursor.getPosition(); String targetLetter = sSections[sectionIndex]; int key = targetLetter.codePointAt(0); // Check cache map { int tmp = stringMap.get(key, Integer.MIN_VALUE); if (Integer.MIN_VALUE != tmp) { return tmp; } } int end = cursor.getCount(); int pos = 0; { // Note that sectionIndex > 0. int prevLetter = sSections[sectionIndex - 1].codePointAt(0); int prevLetterPos = stringMap.get(prevLetter, Integer.MIN_VALUE); if (prevLetterPos != Integer.MIN_VALUE) { pos = prevLetterPos; } } // Do rough binary search if there are a lot of entries. while (end - pos > 100) { int tmp = (end + pos) / 2; cursor.moveToPosition(tmp); String sort_name; do { sort_name = cursor.getString(mColumnIndex); if (sort_name == null || sort_name.length() == 0) { // This should not happen, since sort_name field is created // automatically when syncing to a server, or creating/editing // the entry... Log.e(TAG, "sort_name is null or its length is 0. index: " + tmp); cursor.moveToNext(); tmp++; continue; } break; } while (tmp < end); if (tmp == end) { break; } int codePoint = sort_name.codePointAt(0); if (codePoint < getSectionCodePoint(sectionIndex)) { pos = tmp; } else { end = tmp; } } for (cursor.moveToPosition(pos); !cursor.isAfterLast(); ++pos, cursor.moveToNext()) { String sort_name = cursor.getString(mColumnIndex); if (sort_name == null || sort_name.length() == 0) { // This should not happen, since sort_name field is created // automatically when syncing to a server, or creating/editing // the entry... Log.e(TAG, "sort_name is null or its length is 0. index: " + pos); continue; } int codePoint = sort_name.codePointAt(0); if (codePoint >= getSectionCodePoint(sectionIndex)) { break; } } stringMap.put(key, pos); cursor.moveToPosition(savedCursorPos); return pos; } public int getSectionForPosition(int position) { // Not used in Contacts. Ignore for now. return 0; } public Object[] getSections() { return sSections; } @Override public void onChanged() { super.onChanged(); mStringMap.clear(); } @Override public void onInvalidated() { super.onInvalidated(); mStringMap.clear(); } } Loading
src/com/android/contacts/ContactsListActivity.java +78 −30 Original line number Diff line number Diff line Loading @@ -73,6 +73,7 @@ import android.widget.TextView; import java.lang.ref.SoftReference; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Locale; /** * Displays a list of contacts. Usually is embedded into the ContactsActivity. Loading Loading @@ -169,6 +170,7 @@ public final class ContactsListActivity extends ListActivity static final String NAME_COLUMN = People.DISPLAY_NAME; static final String SORT_STRING = People.SORT_STRING; static final String[] CONTACTS_PROJECTION = new String[] { People._ID, // 0 Loading @@ -180,6 +182,7 @@ public final class ContactsListActivity extends ListActivity People.PRIMARY_PHONE_ID, // 6 People.PRIMARY_EMAIL_ID, // 7 People.PRESENCE_STATUS, // 8 SORT_STRING, // 9 }; static final String[] STREQUENT_PROJECTION = new String[] { Loading Loading @@ -227,6 +230,7 @@ public final class ContactsListActivity extends ListActivity static final int PRIMARY_EMAIL_ID_COLUMN_INDEX = 7; static final int SERVER_STATUS_COLUMN_INDEX = 8; static final int PHOTO_COLUMN_INDEX = 9; static final int SORT_STRING_INDEX = 9; static final int PHONES_PERSON_ID_INDEX = 6; static final int CONTACT_METHODS_PERSON_ID_INDEX = 6; Loading @@ -235,8 +239,6 @@ public final class ContactsListActivity extends ListActivity static final int DISPLAY_GROUP_INDEX_ALL_CONTACTS_WITH_PHONES = 1; static final int DISPLAY_GROUP_INDEX_MY_CONTACTS = 2; static final String SORT_ORDER = NAME_COLUMN + " COLLATE LOCALIZED ASC"; private static final int QUERY_TOKEN = 42; private static final String[] GROUPS_PROJECTION = new String[] { Loading Loading @@ -1137,6 +1139,15 @@ public final class ContactsListActivity extends ListActivity } } private static String getSortOrder(String[] projectionType) { if (Locale.getDefault().equals(Locale.JAPAN) && projectionType == CONTACTS_PROJECTION) { return SORT_STRING + " ASC"; } else { return NAME_COLUMN + " COLLATE LOCALIZED ASC"; } } void startQuery() { mAdapter.setLoading(true); Loading @@ -1147,7 +1158,8 @@ public final class ContactsListActivity extends ListActivity switch (mMode) { case MODE_GROUP: mQueryHandler.startQuery(QUERY_TOKEN, null, mGroupUri, CONTACTS_PROJECTION, null, null, SORT_ORDER); mGroupUri, CONTACTS_PROJECTION, null, null, getSortOrder(CONTACTS_PROJECTION)); break; case MODE_ALL_CONTACTS: Loading @@ -1155,18 +1167,20 @@ public final class ContactsListActivity extends ListActivity case MODE_PICK_OR_CREATE_CONTACT: case MODE_INSERT_OR_EDIT_CONTACT: mQueryHandler.startQuery(QUERY_TOKEN, null, People.CONTENT_URI, CONTACTS_PROJECTION, null, null, SORT_ORDER); null, null, getSortOrder(CONTACTS_PROJECTION)); break; case MODE_WITH_PHONES: mQueryHandler.startQuery(QUERY_TOKEN, null, People.CONTENT_URI, CONTACTS_PROJECTION, People.PRIMARY_PHONE_ID + " IS NOT NULL", null, SORT_ORDER); People.PRIMARY_PHONE_ID + " IS NOT NULL", null, getSortOrder(CONTACTS_PROJECTION)); break; case MODE_QUERY: { mQuery = getIntent().getStringExtra(SearchManager.QUERY); mQueryHandler.startQuery(QUERY_TOKEN, null, getPeopleFilterUri(mQuery), CONTACTS_PROJECTION, null, null, SORT_ORDER); CONTACTS_PROJECTION, null, null, getSortOrder(CONTACTS_PROJECTION)); break; } Loading @@ -1183,26 +1197,30 @@ public final class ContactsListActivity extends ListActivity mCreateData = ssp; } if ("mailto".equals(scheme)) { mCreateExtras = new Bundle(); mCreateExtras.putAll(getIntent().getExtras()); Bundle originalExtras = getIntent().getExtras(); if (originalExtras != null) { mCreateExtras.putAll(originalExtras); } if ("mailto".equals(scheme)) { mCreateExtras.putString(Intents.Insert.EMAIL, ssp); mCreatePersonIndex = CONTACT_METHODS_PERSON_ID_INDEX; mQueryHandler.startQuery(QUERY_TOKEN, null, ContactMethods.CONTENT_URI, CONTACT_METHODS_PROJECTION, ContactMethodsColumns.KIND + "=" + Contacts.KIND_EMAIL + " AND " + ContactMethods.DATA + "=?", new String[] { ssp }, SORT_ORDER); ContactMethods.DATA + "=?", new String[] { ssp }, getSortOrder(CONTACT_METHODS_PROJECTION)); } else if ("tel".equals(scheme)) { mCreateExtras = new Bundle(); mCreateExtras.putAll(getIntent().getExtras()); mCreateExtras.putString(Intents.Insert.PHONE, ssp); mCreatePersonIndex = PHONES_PERSON_ID_INDEX; mQueryHandler.startQuery(QUERY_TOKEN, null, Uri.withAppendedPath(Phones.CONTENT_FILTER_URL, ssp), PHONES_PROJECTION, null, null, SORT_ORDER); PHONES_PROJECTION, null, null, getSortOrder(PHONES_PROJECTION)); } else { Log.w(TAG, "Invalid intent:" + getIntent()); Loading @@ -1213,14 +1231,16 @@ public final class ContactsListActivity extends ListActivity } case MODE_STARRED: mQueryHandler.startQuery(QUERY_TOKEN, null, People.CONTENT_URI, CONTACTS_PROJECTION, People.STARRED + "=1", null, SORT_ORDER); mQueryHandler.startQuery(QUERY_TOKEN, null, People.CONTENT_URI, CONTACTS_PROJECTION, People.STARRED + "=1", null, getSortOrder(CONTACTS_PROJECTION)); break; case MODE_FREQUENT: mQueryHandler.startQuery(QUERY_TOKEN, null, People.CONTENT_URI, CONTACTS_PROJECTION, mQueryHandler.startQuery(QUERY_TOKEN, null, People.CONTENT_URI, CONTACTS_PROJECTION, People.TIMES_CONTACTED + " > 0", null, People.TIMES_CONTACTED + " DESC, " + NAME_COLUMN + " ASC"); People.TIMES_CONTACTED + " DESC, " + getSortOrder(CONTACTS_PROJECTION)); break; case MODE_STREQUENT: Loading @@ -1231,13 +1251,14 @@ public final class ContactsListActivity extends ListActivity case MODE_PICK_PHONE: mQueryHandler.startQuery(QUERY_TOKEN, null, Phones.CONTENT_URI, PHONES_PROJECTION, null, null, SORT_ORDER); null, null, getSortOrder(PHONES_PROJECTION)); break; case MODE_PICK_POSTAL: mQueryHandler.startQuery(QUERY_TOKEN, null, ContactMethods.CONTENT_URI, CONTACT_METHODS_PROJECTION, ContactMethods.KIND + "=" + Contacts.KIND_POSTAL, null, SORT_ORDER); ContactMethods.KIND + "=" + Contacts.KIND_POSTAL, null, getSortOrder(CONTACT_METHODS_PROJECTION)); break; } } Loading @@ -1259,7 +1280,8 @@ public final class ContactsListActivity extends ListActivity } else { uri = Uri.withAppendedPath(mGroupFilterUri, Uri.encode(filter)); } return resolver.query(uri, CONTACTS_PROJECTION, null, null, SORT_ORDER); return resolver.query(uri, CONTACTS_PROJECTION, null, null, getSortOrder(CONTACTS_PROJECTION)); } case MODE_ALL_CONTACTS: Loading @@ -1267,23 +1289,25 @@ public final class ContactsListActivity extends ListActivity case MODE_PICK_OR_CREATE_CONTACT: case MODE_INSERT_OR_EDIT_CONTACT: { return resolver.query(getPeopleFilterUri(filter), CONTACTS_PROJECTION, null, null, SORT_ORDER); getSortOrder(CONTACTS_PROJECTION)); } case MODE_WITH_PHONES: { return resolver.query(getPeopleFilterUri(filter), CONTACTS_PROJECTION, People.PRIMARY_PHONE_ID + " IS NOT NULL", null, SORT_ORDER); People.PRIMARY_PHONE_ID + " IS NOT NULL", null, getSortOrder(CONTACTS_PROJECTION)); } case MODE_STARRED: { return resolver.query(getPeopleFilterUri(filter), CONTACTS_PROJECTION, People.STARRED + "=1", null, SORT_ORDER); People.STARRED + "=1", null, getSortOrder(CONTACTS_PROJECTION)); } case MODE_FREQUENT: { return resolver.query(getPeopleFilterUri(filter), CONTACTS_PROJECTION, People.TIMES_CONTACTED + " > 0", null, People.TIMES_CONTACTED + " DESC, " + NAME_COLUMN + " ASC"); People.TIMES_CONTACTED + " DESC, " + getSortOrder(CONTACTS_PROJECTION)); } case MODE_STREQUENT: { Loading @@ -1305,7 +1329,8 @@ public final class ContactsListActivity extends ListActivity } else { uri = Phones.CONTENT_URI; } return resolver.query(uri, PHONES_PROJECTION, null, null, SORT_ORDER); return resolver.query(uri, PHONES_PROJECTION, null, null, getSortOrder(PHONES_PROJECTION)); } } throw new UnsupportedOperationException("filtering not allowed in mode " + mMode); Loading Loading @@ -1475,7 +1500,7 @@ public final class ContactsListActivity extends ListActivity private final class ContactItemListAdapter extends ResourceCursorAdapter implements SectionIndexer { private AlphabetIndexer mIndexer; private SectionIndexer mIndexer; private String mAlphabet; private boolean mLoading = true; private CharSequence mUnknownNameText; Loading Loading @@ -1507,6 +1532,14 @@ public final class ContactsListActivity extends ListActivity } } private SectionIndexer getNewIndexer(Cursor cursor) { if (Locale.getDefault().equals(Locale.JAPAN)) { return new JapaneseContactListIndexer(cursor, SORT_STRING_INDEX); } else { return new AlphabetIndexer(cursor, NAME_COLUMN_INDEX, mAlphabet); } } /** * Callback on the UI thread when the content observer on the backing cursor fires. * Instead of calling requery we need to do an async query so that the requery doesn't Loading Loading @@ -1682,9 +1715,21 @@ public final class ContactsListActivity extends ListActivity private void updateIndexer(Cursor cursor) { if (mIndexer == null) { mIndexer = new AlphabetIndexer(cursor, NAME_COLUMN_INDEX, mAlphabet); mIndexer = getNewIndexer(cursor); } else { mIndexer.setCursor(cursor); if (Locale.getDefault().equals(Locale.JAPAN)) { if (mIndexer instanceof JapaneseContactListIndexer) { ((JapaneseContactListIndexer)mIndexer).setCursor(cursor); } else { mIndexer = getNewIndexer(cursor); } } else { if (mIndexer instanceof AlphabetIndexer) { ((AlphabetIndexer)mIndexer).setCursor(cursor); } else { mIndexer = getNewIndexer(cursor); } } } } Loading Loading @@ -1716,13 +1761,16 @@ public final class ContactsListActivity extends ListActivity // No cursor, the section doesn't exist so just return 0 return 0; } mIndexer = new AlphabetIndexer(cursor, NAME_COLUMN_INDEX, mAlphabet); mIndexer = getNewIndexer(cursor); } return mIndexer.getPositionForSection(sectionIndex); } public int getSectionForPosition(int position) { // Note: JapaneseContactListIndexer depends on the fact // this method always returns 0. If you change this, // please care it too. return 0; } } Loading
src/com/android/contacts/JapaneseContactListIndexer.java 0 → 100644 +187 −0 Original line number Diff line number Diff line /* * Copyright (C) 2009 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; import android.database.Cursor; import android.database.DataSetObserver; import android.util.Log; import android.util.SparseIntArray; import android.widget.SectionIndexer; /** * SectionIndexer which is for "phonetically sortable" String. This class heavily depends on the * algorithm of the SQL function "GET_PHONETICALLY_SORTABLE_STRING", whose implementation * is written in C++. */ public final class JapaneseContactListIndexer extends DataSetObserver implements SectionIndexer { private static String TAG = "JapaneseContactListIndexer"; static private final String[] sSections = { " ", // Sections of SectionIndexer should start with " " (some components assume it). "\u3042", "\u304B", "\u3055", "\u305F", "\u306A", // a, ka, sa, ta, na "\u306F", "\u307E", "\u3084", "\u3089", "\u308F", // ha, ma, ya, ra, wa "\uFF21", "\uFF22", "\uFF23", "\uFF24", "\uFF25", // full-width ABCDE "\uFF26", "\uFF27", "\uFF28", "\uFF29", "\uFF2A", // full-width FGHIJ "\uFF2B", "\uFF2C", "\uFF2D", "\uFF2E", "\uFF2F", // full-width KLMNO "\uFF30", "\uFF31", "\uFF32", "\uFF33", "\uFF34", // full-width PQRST "\uFF35", "\uFF36", "\uFF37", "\uFF38", "\uFF39", // full-width UVWXY "\uFF40", // full-width Z "\u6570", "\u8A18" // alphabets, numbers, symbols }; static private final int sSectionsLength = sSections.length; private int mColumnIndex; private Cursor mDataCursor; private SparseIntArray mStringMap; public JapaneseContactListIndexer(Cursor cursor, int columnIndex) { int len = sSections.length; mColumnIndex = columnIndex; mDataCursor = cursor; mStringMap = new SparseIntArray(sSectionsLength); if (cursor != null) { cursor.registerDataSetObserver(this); } } public void setCursor(Cursor cursor) { if (mDataCursor != null) { mDataCursor.unregisterDataSetObserver(this); } mDataCursor = cursor; if (cursor != null) { mDataCursor.registerDataSetObserver(this); } } private int getSectionCodePoint(int index) { if (index < sSections.length - 2) { return sSections[index].codePointAt(0); } else if (index == sSections.length - 2) { return 0xFF66; // Numbers are mapped from 0xFF66. } else { // index == mSections.length - 1 return 0xFF70; // Symbols are mapped from 0xFF70. } } public int getPositionForSection(int sectionIndex) { final SparseIntArray stringMap = mStringMap; final Cursor cursor = mDataCursor; if (cursor == null || sectionIndex <= 0) { return 0; } if (sectionIndex >= sSectionsLength) { sectionIndex = sSectionsLength - 1; } int savedCursorPos = cursor.getPosition(); String targetLetter = sSections[sectionIndex]; int key = targetLetter.codePointAt(0); // Check cache map { int tmp = stringMap.get(key, Integer.MIN_VALUE); if (Integer.MIN_VALUE != tmp) { return tmp; } } int end = cursor.getCount(); int pos = 0; { // Note that sectionIndex > 0. int prevLetter = sSections[sectionIndex - 1].codePointAt(0); int prevLetterPos = stringMap.get(prevLetter, Integer.MIN_VALUE); if (prevLetterPos != Integer.MIN_VALUE) { pos = prevLetterPos; } } // Do rough binary search if there are a lot of entries. while (end - pos > 100) { int tmp = (end + pos) / 2; cursor.moveToPosition(tmp); String sort_name; do { sort_name = cursor.getString(mColumnIndex); if (sort_name == null || sort_name.length() == 0) { // This should not happen, since sort_name field is created // automatically when syncing to a server, or creating/editing // the entry... Log.e(TAG, "sort_name is null or its length is 0. index: " + tmp); cursor.moveToNext(); tmp++; continue; } break; } while (tmp < end); if (tmp == end) { break; } int codePoint = sort_name.codePointAt(0); if (codePoint < getSectionCodePoint(sectionIndex)) { pos = tmp; } else { end = tmp; } } for (cursor.moveToPosition(pos); !cursor.isAfterLast(); ++pos, cursor.moveToNext()) { String sort_name = cursor.getString(mColumnIndex); if (sort_name == null || sort_name.length() == 0) { // This should not happen, since sort_name field is created // automatically when syncing to a server, or creating/editing // the entry... Log.e(TAG, "sort_name is null or its length is 0. index: " + pos); continue; } int codePoint = sort_name.codePointAt(0); if (codePoint >= getSectionCodePoint(sectionIndex)) { break; } } stringMap.put(key, pos); cursor.moveToPosition(savedCursorPos); return pos; } public int getSectionForPosition(int position) { // Not used in Contacts. Ignore for now. return 0; } public Object[] getSections() { return sSections; } @Override public void onChanged() { super.onChanged(); mStringMap.clear(); } @Override public void onInvalidated() { super.onInvalidated(); mStringMap.clear(); } }