auto import from //branches/cupcake_rel/...@138607
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
index 39e127a..b9dc4f2 100644
--- a/src/com/android/contacts/ContactsListActivity.java
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -73,6 +73,7 @@
 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.
@@ -83,7 +84,7 @@
 
     private static final String LIST_STATE_KEY = "liststate";
     private static final String FOCUS_KEY = "focused";
-
+    
     static final int MENU_ITEM_VIEW_CONTACT = 1;
     static final int MENU_ITEM_CALL = 2;
     static final int MENU_ITEM_EDIT_BEFORE_CALL = 3;
@@ -169,6 +170,7 @@
 
     
     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
@@ -180,6 +182,7 @@
         People.PRIMARY_PHONE_ID, // 6
         People.PRIMARY_EMAIL_ID, // 7
         People.PRESENCE_STATUS, // 8
+        SORT_STRING, // 9
     };
 
     static final String[] STREQUENT_PROJECTION = new String[] {
@@ -227,6 +230,7 @@
     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;
@@ -234,9 +238,7 @@
     static final int DISPLAY_GROUP_INDEX_ALL_CONTACTS = 0;
     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[] {
@@ -1137,6 +1139,15 @@
         }
     }
 
+    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);
         
@@ -1147,7 +1158,8 @@
         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:
@@ -1155,18 +1167,20 @@
             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;
             }
             
@@ -1182,27 +1196,31 @@
                     ssp = data.getSchemeSpecificPart();
                     mCreateData = ssp;
                 }
+                
+                mCreateExtras = new Bundle();
+                Bundle originalExtras = getIntent().getExtras();
+                if (originalExtras != null) {
+                    mCreateExtras.putAll(originalExtras);
+                }
 
                 if ("mailto".equals(scheme)) {
-                    mCreateExtras = new Bundle();
-                    mCreateExtras.putAll(getIntent().getExtras());
                     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());
@@ -1213,14 +1231,16 @@
             }
 
             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:
@@ -1231,13 +1251,14 @@
 
             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;
         }
     }
@@ -1259,7 +1280,8 @@
                 } 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:
@@ -1267,23 +1289,25 @@
             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: {
@@ -1305,7 +1329,8 @@
                 } 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);
@@ -1475,7 +1500,7 @@
 
     private final class ContactItemListAdapter extends ResourceCursorAdapter 
             implements SectionIndexer {
-        private AlphabetIndexer mIndexer;
+        private SectionIndexer mIndexer;
         private String mAlphabet;
         private boolean mLoading = true;
         private CharSequence mUnknownNameText;
@@ -1507,6 +1532,14 @@
             }
         }
 
+        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
@@ -1682,9 +1715,21 @@
         
         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);
+                    }
+                }
             }
         }
         
@@ -1716,13 +1761,16 @@
                     // 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;
         }        
     }
diff --git a/src/com/android/contacts/JapaneseContactListIndexer.java b/src/com/android/contacts/JapaneseContactListIndexer.java
new file mode 100644
index 0000000..d5d6dcd
--- /dev/null
+++ b/src/com/android/contacts/JapaneseContactListIndexer.java
@@ -0,0 +1,187 @@
+/*
+ * 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();
+    }
+}