Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3db4f241 authored by The Android Open Source Project's avatar The Android Open Source Project
Browse files

auto import from //branches/cupcake_rel/...@138607

parent e740e2e0
Loading
Loading
Loading
Loading
+78 −30
Original line number Diff line number Diff line
@@ -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.
@@ -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
@@ -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[] {
@@ -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;
@@ -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[] {
@@ -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);
        
@@ -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:
@@ -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;
            }
            
@@ -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());
@@ -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:
@@ -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;
        }
    }
@@ -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:
@@ -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: {
@@ -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);
@@ -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;
@@ -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
@@ -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);
                    }
                }
            }
        }
        
@@ -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;
        }        
    }
+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();
    }
}