Home | History | Annotate | Download | only in contacts
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      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
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     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
     15  */
     16 package com.android.providers.contacts;
     17 
     18 import android.accounts.Account;
     19 import android.app.SearchManager;
     20 import android.content.ContentUris;
     21 import android.content.ContentValues;
     22 import android.content.Context;
     23 import android.content.UriMatcher;
     24 import android.database.Cursor;
     25 import android.database.DatabaseUtils;
     26 import android.database.SQLException;
     27 import android.database.sqlite.SQLiteDatabase;
     28 import android.database.sqlite.SQLiteDoneException;
     29 import android.database.sqlite.SQLiteQueryBuilder;
     30 import android.database.sqlite.SQLiteStatement;
     31 import android.net.Uri;
     32 import android.provider.BaseColumns;
     33 import android.provider.Contacts.ContactMethods;
     34 import android.provider.Contacts.Extensions;
     35 import android.provider.Contacts.People;
     36 import android.provider.ContactsContract;
     37 import android.provider.ContactsContract.CommonDataKinds.Email;
     38 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
     39 import android.provider.ContactsContract.CommonDataKinds.Im;
     40 import android.provider.ContactsContract.CommonDataKinds.Note;
     41 import android.provider.ContactsContract.CommonDataKinds.Organization;
     42 import android.provider.ContactsContract.CommonDataKinds.Phone;
     43 import android.provider.ContactsContract.CommonDataKinds.Photo;
     44 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
     45 import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
     46 import android.provider.ContactsContract.Contacts;
     47 import android.provider.ContactsContract.Data;
     48 import android.provider.ContactsContract.Groups;
     49 import android.provider.ContactsContract.RawContacts;
     50 import android.provider.ContactsContract.Settings;
     51 import android.provider.ContactsContract.StatusUpdates;
     52 import android.text.TextUtils;
     53 import android.util.Log;
     54 
     55 import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
     56 import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
     57 import com.android.providers.contacts.ContactsDatabaseHelper.ExtensionsColumns;
     58 import com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
     59 import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
     60 import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
     61 import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
     62 import com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
     63 import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
     64 import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
     65 import com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
     66 import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
     67 
     68 import java.util.HashMap;
     69 import java.util.Locale;
     70 
     71 @SuppressWarnings("deprecation")
     72 public class LegacyApiSupport {
     73 
     74     private static final String TAG = "ContactsProviderV1";
     75 
     76     private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
     77 
     78     private static final int PEOPLE = 1;
     79     private static final int PEOPLE_ID = 2;
     80     private static final int PEOPLE_UPDATE_CONTACT_TIME = 3;
     81     private static final int ORGANIZATIONS = 4;
     82     private static final int ORGANIZATIONS_ID = 5;
     83     private static final int PEOPLE_CONTACTMETHODS = 6;
     84     private static final int PEOPLE_CONTACTMETHODS_ID = 7;
     85     private static final int CONTACTMETHODS = 8;
     86     private static final int CONTACTMETHODS_ID = 9;
     87     private static final int PEOPLE_PHONES = 10;
     88     private static final int PEOPLE_PHONES_ID = 11;
     89     private static final int PHONES = 12;
     90     private static final int PHONES_ID = 13;
     91     private static final int EXTENSIONS = 14;
     92     private static final int EXTENSIONS_ID = 15;
     93     private static final int PEOPLE_EXTENSIONS = 16;
     94     private static final int PEOPLE_EXTENSIONS_ID = 17;
     95     private static final int GROUPS = 18;
     96     private static final int GROUPS_ID = 19;
     97     private static final int GROUPMEMBERSHIP = 20;
     98     private static final int GROUPMEMBERSHIP_ID = 21;
     99     private static final int PEOPLE_GROUPMEMBERSHIP = 22;
    100     private static final int PEOPLE_GROUPMEMBERSHIP_ID = 23;
    101     private static final int PEOPLE_PHOTO = 24;
    102     private static final int PHOTOS = 25;
    103     private static final int PHOTOS_ID = 26;
    104     private static final int PEOPLE_FILTER = 29;
    105     private static final int DELETED_PEOPLE = 30;
    106     private static final int DELETED_GROUPS = 31;
    107     private static final int SEARCH_SUGGESTIONS = 32;
    108     private static final int SEARCH_SHORTCUT = 33;
    109     private static final int PHONES_FILTER = 34;
    110     private static final int LIVE_FOLDERS_PEOPLE = 35;
    111     private static final int LIVE_FOLDERS_PEOPLE_GROUP_NAME = 36;
    112     private static final int LIVE_FOLDERS_PEOPLE_WITH_PHONES = 37;
    113     private static final int LIVE_FOLDERS_PEOPLE_FAVORITES = 38;
    114     private static final int CONTACTMETHODS_EMAIL = 39;
    115     private static final int GROUP_NAME_MEMBERS = 40;
    116     private static final int GROUP_SYSTEM_ID_MEMBERS = 41;
    117     private static final int PEOPLE_ORGANIZATIONS = 42;
    118     private static final int PEOPLE_ORGANIZATIONS_ID = 43;
    119     private static final int SETTINGS = 44;
    120 
    121     private static final String PEOPLE_JOINS =
    122             " JOIN " + Tables.ACCOUNTS + " ON ("
    123                 + RawContactsColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID + ")"
    124             + " LEFT OUTER JOIN data name ON (raw_contacts._id = name.raw_contact_id"
    125             + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = name.mimetype_id)"
    126                     + "='" + StructuredName.CONTENT_ITEM_TYPE + "')"
    127             + " LEFT OUTER JOIN data organization ON (raw_contacts._id = organization.raw_contact_id"
    128             + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = organization.mimetype_id)"
    129                     + "='" + Organization.CONTENT_ITEM_TYPE + "' AND organization.is_primary)"
    130             + " LEFT OUTER JOIN data email ON (raw_contacts._id = email.raw_contact_id"
    131             + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = email.mimetype_id)"
    132                     + "='" + Email.CONTENT_ITEM_TYPE + "' AND email.is_primary)"
    133             + " LEFT OUTER JOIN data note ON (raw_contacts._id = note.raw_contact_id"
    134             + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = note.mimetype_id)"
    135                     + "='" + Note.CONTENT_ITEM_TYPE + "')"
    136             + " LEFT OUTER JOIN data phone ON (raw_contacts._id = phone.raw_contact_id"
    137             + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = phone.mimetype_id)"
    138                     + "='" + Phone.CONTENT_ITEM_TYPE + "' AND phone.is_primary)";
    139 
    140     public static final String DATA_JOINS =
    141             " JOIN mimetypes ON (mimetypes._id = data.mimetype_id)"
    142             + " JOIN raw_contacts ON (raw_contacts._id = data.raw_contact_id)"
    143             + PEOPLE_JOINS;
    144 
    145     public static final String PRESENCE_JOINS =
    146             " LEFT OUTER JOIN " + Tables.PRESENCE +
    147             " ON (" + Tables.PRESENCE + "." + StatusUpdates.DATA_ID + "=" +
    148                     "(SELECT MAX(" + StatusUpdates.DATA_ID + ")" +
    149                     " FROM " + Tables.PRESENCE +
    150                     " WHERE people._id = " + PresenceColumns.RAW_CONTACT_ID + ")" +
    151             " )";
    152 
    153     private static final String PHONETIC_NAME_SQL = "trim(trim("
    154             + "ifnull(name." + StructuredName.PHONETIC_GIVEN_NAME + ",' ')||' '||"
    155             + "ifnull(name." + StructuredName.PHONETIC_MIDDLE_NAME + ",' '))||' '||"
    156             + "ifnull(name." + StructuredName.PHONETIC_FAMILY_NAME + ",' ')) ";
    157 
    158     private static final String CONTACT_METHOD_KIND_SQL =
    159             "CAST ((CASE WHEN mimetype='" + Email.CONTENT_ITEM_TYPE + "'"
    160                 + " THEN " + android.provider.Contacts.KIND_EMAIL
    161                 + " ELSE"
    162                     + " (CASE WHEN mimetype='" + Im.CONTENT_ITEM_TYPE +"'"
    163                         + " THEN " + android.provider.Contacts.KIND_IM
    164                         + " ELSE"
    165                         + " (CASE WHEN mimetype='" + StructuredPostal.CONTENT_ITEM_TYPE + "'"
    166                             + " THEN "  + android.provider.Contacts.KIND_POSTAL
    167                             + " ELSE"
    168                                 + " NULL"
    169                             + " END)"
    170                         + " END)"
    171                 + " END) AS INTEGER)";
    172 
    173     private static final String IM_PROTOCOL_SQL =
    174             "(CASE WHEN " + StatusUpdates.PROTOCOL + "=" + Im.PROTOCOL_CUSTOM
    175                 + " THEN 'custom:'||" + StatusUpdates.CUSTOM_PROTOCOL
    176                 + " ELSE 'pre:'||" + StatusUpdates.PROTOCOL
    177                 + " END)";
    178 
    179     private static String CONTACT_METHOD_DATA_SQL =
    180             "(CASE WHEN " + Data.MIMETYPE + "='" + Im.CONTENT_ITEM_TYPE + "'"
    181                 + " THEN (CASE WHEN " + Tables.DATA + "." + Im.PROTOCOL + "=" + Im.PROTOCOL_CUSTOM
    182                     + " THEN 'custom:'||" + Tables.DATA + "." + Im.CUSTOM_PROTOCOL
    183                     + " ELSE 'pre:'||" + Tables.DATA + "." + Im.PROTOCOL
    184                     + " END)"
    185                 + " ELSE " + Tables.DATA + "." + Email.DATA
    186                 + " END)";
    187 
    188     private static final Uri LIVE_FOLDERS_CONTACTS_URI = Uri.withAppendedPath(
    189             ContactsContract.AUTHORITY_URI, "live_folders/contacts");
    190 
    191     private static final Uri LIVE_FOLDERS_CONTACTS_WITH_PHONES_URI = Uri.withAppendedPath(
    192             ContactsContract.AUTHORITY_URI, "live_folders/contacts_with_phones");
    193 
    194     private static final Uri LIVE_FOLDERS_CONTACTS_FAVORITES_URI = Uri.withAppendedPath(
    195             ContactsContract.AUTHORITY_URI, "live_folders/favorites");
    196 
    197     private static final String CONTACTS_UPDATE_LASTTIMECONTACTED =
    198             "UPDATE " + Tables.CONTACTS +
    199             " SET " + Contacts.LAST_TIME_CONTACTED + "=? " +
    200             "WHERE " + Contacts._ID + "=?";
    201     private static final String RAWCONTACTS_UPDATE_LASTTIMECONTACTED =
    202             "UPDATE " + Tables.RAW_CONTACTS + " SET "
    203             + RawContacts.LAST_TIME_CONTACTED + "=? WHERE "
    204             + RawContacts._ID + "=?";
    205 
    206     private String[] mSelectionArgs1 = new String[1];
    207     private String[] mSelectionArgs2 = new String[2];
    208 
    209     public interface LegacyTables {
    210         public static final String PEOPLE = "view_v1_people";
    211         public static final String PEOPLE_JOIN_PRESENCE = "view_v1_people people " + PRESENCE_JOINS;
    212         public static final String GROUPS = "view_v1_groups";
    213         public static final String ORGANIZATIONS = "view_v1_organizations";
    214         public static final String CONTACT_METHODS = "view_v1_contact_methods";
    215         public static final String PHONES = "view_v1_phones";
    216         public static final String EXTENSIONS = "view_v1_extensions";
    217         public static final String GROUP_MEMBERSHIP = "view_v1_group_membership";
    218         public static final String PHOTOS = "view_v1_photos";
    219         public static final String SETTINGS = "v1_settings";
    220     }
    221 
    222     private static final String[] ORGANIZATION_MIME_TYPES = new String[] {
    223         Organization.CONTENT_ITEM_TYPE
    224     };
    225 
    226     private static final String[] CONTACT_METHOD_MIME_TYPES = new String[] {
    227         Email.CONTENT_ITEM_TYPE,
    228         Im.CONTENT_ITEM_TYPE,
    229         StructuredPostal.CONTENT_ITEM_TYPE,
    230     };
    231 
    232     private static final String[] PHONE_MIME_TYPES = new String[] {
    233         Phone.CONTENT_ITEM_TYPE
    234     };
    235 
    236     private static final String[] PHOTO_MIME_TYPES = new String[] {
    237         Photo.CONTENT_ITEM_TYPE
    238     };
    239 
    240     private static final String[] GROUP_MEMBERSHIP_MIME_TYPES = new String[] {
    241         GroupMembership.CONTENT_ITEM_TYPE
    242     };
    243 
    244     private static final String[] EXTENSION_MIME_TYPES = new String[] {
    245         android.provider.Contacts.Extensions.CONTENT_ITEM_TYPE
    246     };
    247 
    248     private interface IdQuery {
    249         String[] COLUMNS = { BaseColumns._ID };
    250 
    251         int _ID = 0;
    252     }
    253 
    254     /**
    255      * A custom data row that is used to store legacy photo data fields no
    256      * longer directly supported by the API.
    257      */
    258     private interface LegacyPhotoData {
    259         public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/photo_v1_extras";
    260 
    261         public static final String PHOTO_DATA_ID = Data.DATA1;
    262         public static final String LOCAL_VERSION = Data.DATA2;
    263         public static final String DOWNLOAD_REQUIRED = Data.DATA3;
    264         public static final String EXISTS_ON_SERVER = Data.DATA4;
    265         public static final String SYNC_ERROR = Data.DATA5;
    266     }
    267 
    268     public static final String LEGACY_PHOTO_JOIN =
    269             " LEFT OUTER JOIN data legacy_photo ON (raw_contacts._id = legacy_photo.raw_contact_id"
    270             + " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = legacy_photo.mimetype_id)"
    271                 + "='" + LegacyPhotoData.CONTENT_ITEM_TYPE + "'"
    272             + " AND " + DataColumns.CONCRETE_ID + " = legacy_photo." + LegacyPhotoData.PHOTO_DATA_ID
    273             + ")";
    274 
    275     private static final HashMap<String, String> sPeopleProjectionMap;
    276     private static final HashMap<String, String> sOrganizationProjectionMap;
    277     private static final HashMap<String, String> sContactMethodProjectionMap;
    278     private static final HashMap<String, String> sPhoneProjectionMap;
    279     private static final HashMap<String, String> sExtensionProjectionMap;
    280     private static final HashMap<String, String> sGroupProjectionMap;
    281     private static final HashMap<String, String> sGroupMembershipProjectionMap;
    282     private static final HashMap<String, String> sPhotoProjectionMap;
    283 
    284     static {
    285 
    286         // Contacts URI matching table
    287         UriMatcher matcher = sUriMatcher;
    288 
    289         String authority = android.provider.Contacts.AUTHORITY;
    290         matcher.addURI(authority, "extensions", EXTENSIONS);
    291         matcher.addURI(authority, "extensions/#", EXTENSIONS_ID);
    292         matcher.addURI(authority, "groups", GROUPS);
    293         matcher.addURI(authority, "groups/#", GROUPS_ID);
    294         matcher.addURI(authority, "groups/name/*/members", GROUP_NAME_MEMBERS);
    295 //        matcher.addURI(authority, "groups/name/*/members/filter/*",
    296 //                GROUP_NAME_MEMBERS_FILTER);
    297         matcher.addURI(authority, "groups/system_id/*/members", GROUP_SYSTEM_ID_MEMBERS);
    298 //        matcher.addURI(authority, "groups/system_id/*/members/filter/*",
    299 //                GROUP_SYSTEM_ID_MEMBERS_FILTER);
    300         matcher.addURI(authority, "groupmembership", GROUPMEMBERSHIP);
    301         matcher.addURI(authority, "groupmembership/#", GROUPMEMBERSHIP_ID);
    302 //        matcher.addURI(authority, "groupmembershipraw", GROUPMEMBERSHIP_RAW);
    303         matcher.addURI(authority, "people", PEOPLE);
    304 //        matcher.addURI(authority, "people/strequent", PEOPLE_STREQUENT);
    305 //        matcher.addURI(authority, "people/strequent/filter/*", PEOPLE_STREQUENT_FILTER);
    306         matcher.addURI(authority, "people/filter/*", PEOPLE_FILTER);
    307 //        matcher.addURI(authority, "people/with_phones_filter/*",
    308 //                PEOPLE_WITH_PHONES_FILTER);
    309 //        matcher.addURI(authority, "people/with_email_or_im_filter/*",
    310 //                PEOPLE_WITH_EMAIL_OR_IM_FILTER);
    311         matcher.addURI(authority, "people/#", PEOPLE_ID);
    312         matcher.addURI(authority, "people/#/extensions", PEOPLE_EXTENSIONS);
    313         matcher.addURI(authority, "people/#/extensions/#", PEOPLE_EXTENSIONS_ID);
    314         matcher.addURI(authority, "people/#/phones", PEOPLE_PHONES);
    315         matcher.addURI(authority, "people/#/phones/#", PEOPLE_PHONES_ID);
    316 //        matcher.addURI(authority, "people/#/phones_with_presence",
    317 //                PEOPLE_PHONES_WITH_PRESENCE);
    318         matcher.addURI(authority, "people/#/photo", PEOPLE_PHOTO);
    319 //        matcher.addURI(authority, "people/#/photo/data", PEOPLE_PHOTO_DATA);
    320         matcher.addURI(authority, "people/#/contact_methods", PEOPLE_CONTACTMETHODS);
    321 //        matcher.addURI(authority, "people/#/contact_methods_with_presence",
    322 //                PEOPLE_CONTACTMETHODS_WITH_PRESENCE);
    323         matcher.addURI(authority, "people/#/contact_methods/#", PEOPLE_CONTACTMETHODS_ID);
    324         matcher.addURI(authority, "people/#/organizations", PEOPLE_ORGANIZATIONS);
    325         matcher.addURI(authority, "people/#/organizations/#", PEOPLE_ORGANIZATIONS_ID);
    326         matcher.addURI(authority, "people/#/groupmembership", PEOPLE_GROUPMEMBERSHIP);
    327         matcher.addURI(authority, "people/#/groupmembership/#", PEOPLE_GROUPMEMBERSHIP_ID);
    328 //        matcher.addURI(authority, "people/raw", PEOPLE_RAW);
    329 //        matcher.addURI(authority, "people/owner", PEOPLE_OWNER);
    330         matcher.addURI(authority, "people/#/update_contact_time",
    331                 PEOPLE_UPDATE_CONTACT_TIME);
    332         matcher.addURI(authority, "deleted_people", DELETED_PEOPLE);
    333         matcher.addURI(authority, "deleted_groups", DELETED_GROUPS);
    334         matcher.addURI(authority, "phones", PHONES);
    335 //        matcher.addURI(authority, "phones_with_presence", PHONES_WITH_PRESENCE);
    336         matcher.addURI(authority, "phones/filter/*", PHONES_FILTER);
    337 //        matcher.addURI(authority, "phones/filter_name/*", PHONES_FILTER_NAME);
    338 //        matcher.addURI(authority, "phones/mobile_filter_name/*",
    339 //                PHONES_MOBILE_FILTER_NAME);
    340         matcher.addURI(authority, "phones/#", PHONES_ID);
    341         matcher.addURI(authority, "photos", PHOTOS);
    342         matcher.addURI(authority, "photos/#", PHOTOS_ID);
    343         matcher.addURI(authority, "contact_methods", CONTACTMETHODS);
    344         matcher.addURI(authority, "contact_methods/email", CONTACTMETHODS_EMAIL);
    345 //        matcher.addURI(authority, "contact_methods/email/*", CONTACTMETHODS_EMAIL_FILTER);
    346         matcher.addURI(authority, "contact_methods/#", CONTACTMETHODS_ID);
    347 //        matcher.addURI(authority, "contact_methods/with_presence",
    348 //                CONTACTMETHODS_WITH_PRESENCE);
    349         matcher.addURI(authority, "organizations", ORGANIZATIONS);
    350         matcher.addURI(authority, "organizations/#", ORGANIZATIONS_ID);
    351 //        matcher.addURI(authority, "voice_dialer_timestamp", VOICE_DIALER_TIMESTAMP);
    352         matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_QUERY,
    353                 SEARCH_SUGGESTIONS);
    354         matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
    355                 SEARCH_SUGGESTIONS);
    356         matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
    357                 SEARCH_SHORTCUT);
    358         matcher.addURI(authority, "settings", SETTINGS);
    359 
    360         matcher.addURI(authority, "live_folders/people", LIVE_FOLDERS_PEOPLE);
    361         matcher.addURI(authority, "live_folders/people/*",
    362                 LIVE_FOLDERS_PEOPLE_GROUP_NAME);
    363         matcher.addURI(authority, "live_folders/people_with_phones",
    364                 LIVE_FOLDERS_PEOPLE_WITH_PHONES);
    365         matcher.addURI(authority, "live_folders/favorites",
    366                 LIVE_FOLDERS_PEOPLE_FAVORITES);
    367 
    368 
    369         HashMap<String, String> peopleProjectionMap = new HashMap<String, String>();
    370         peopleProjectionMap.put(People.NAME, People.NAME);
    371         peopleProjectionMap.put(People.DISPLAY_NAME, People.DISPLAY_NAME);
    372         peopleProjectionMap.put(People.PHONETIC_NAME, People.PHONETIC_NAME);
    373         peopleProjectionMap.put(People.NOTES, People.NOTES);
    374         peopleProjectionMap.put(People.TIMES_CONTACTED, People.TIMES_CONTACTED);
    375         peopleProjectionMap.put(People.LAST_TIME_CONTACTED, People.LAST_TIME_CONTACTED);
    376         peopleProjectionMap.put(People.CUSTOM_RINGTONE, People.CUSTOM_RINGTONE);
    377         peopleProjectionMap.put(People.SEND_TO_VOICEMAIL, People.SEND_TO_VOICEMAIL);
    378         peopleProjectionMap.put(People.STARRED, People.STARRED);
    379         peopleProjectionMap.put(People.PRIMARY_ORGANIZATION_ID, People.PRIMARY_ORGANIZATION_ID);
    380         peopleProjectionMap.put(People.PRIMARY_EMAIL_ID, People.PRIMARY_EMAIL_ID);
    381         peopleProjectionMap.put(People.PRIMARY_PHONE_ID, People.PRIMARY_PHONE_ID);
    382 
    383         sPeopleProjectionMap = new HashMap<String, String>(peopleProjectionMap);
    384         sPeopleProjectionMap.put(People._ID, People._ID);
    385         sPeopleProjectionMap.put(People.NUMBER, People.NUMBER);
    386         sPeopleProjectionMap.put(People.TYPE, People.TYPE);
    387         sPeopleProjectionMap.put(People.LABEL, People.LABEL);
    388         sPeopleProjectionMap.put(People.NUMBER_KEY, People.NUMBER_KEY);
    389         sPeopleProjectionMap.put(People.IM_PROTOCOL, IM_PROTOCOL_SQL + " AS " + People.IM_PROTOCOL);
    390         sPeopleProjectionMap.put(People.IM_HANDLE, People.IM_HANDLE);
    391         sPeopleProjectionMap.put(People.IM_ACCOUNT, People.IM_ACCOUNT);
    392         sPeopleProjectionMap.put(People.PRESENCE_STATUS, People.PRESENCE_STATUS);
    393         sPeopleProjectionMap.put(People.PRESENCE_CUSTOM_STATUS,
    394                 "(SELECT " + StatusUpdates.STATUS +
    395                 " FROM " + Tables.STATUS_UPDATES +
    396                 " JOIN " + Tables.DATA +
    397                 "   ON(" + StatusUpdatesColumns.DATA_ID + "=" + DataColumns.CONCRETE_ID + ")" +
    398                 " WHERE " + DataColumns.CONCRETE_RAW_CONTACT_ID + "=people." + People._ID +
    399                 " ORDER BY " + StatusUpdates.STATUS_TIMESTAMP + " DESC " +
    400                 " LIMIT 1" +
    401                 ") AS " + People.PRESENCE_CUSTOM_STATUS);
    402 
    403         sOrganizationProjectionMap = new HashMap<String, String>();
    404         sOrganizationProjectionMap.put(android.provider.Contacts.Organizations._ID,
    405                 android.provider.Contacts.Organizations._ID);
    406         sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.PERSON_ID,
    407                 android.provider.Contacts.Organizations.PERSON_ID);
    408         sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.ISPRIMARY,
    409                 android.provider.Contacts.Organizations.ISPRIMARY);
    410         sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.COMPANY,
    411                 android.provider.Contacts.Organizations.COMPANY);
    412         sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.TYPE,
    413                 android.provider.Contacts.Organizations.TYPE);
    414         sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.LABEL,
    415                 android.provider.Contacts.Organizations.LABEL);
    416         sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.TITLE,
    417                 android.provider.Contacts.Organizations.TITLE);
    418 
    419         sContactMethodProjectionMap = new HashMap<String, String>(peopleProjectionMap);
    420         sContactMethodProjectionMap.put(ContactMethods._ID, ContactMethods._ID);
    421         sContactMethodProjectionMap.put(ContactMethods.PERSON_ID, ContactMethods.PERSON_ID);
    422         sContactMethodProjectionMap.put(ContactMethods.KIND, ContactMethods.KIND);
    423         sContactMethodProjectionMap.put(ContactMethods.ISPRIMARY, ContactMethods.ISPRIMARY);
    424         sContactMethodProjectionMap.put(ContactMethods.TYPE, ContactMethods.TYPE);
    425         sContactMethodProjectionMap.put(ContactMethods.DATA, ContactMethods.DATA);
    426         sContactMethodProjectionMap.put(ContactMethods.LABEL, ContactMethods.LABEL);
    427         sContactMethodProjectionMap.put(ContactMethods.AUX_DATA, ContactMethods.AUX_DATA);
    428 
    429         sPhoneProjectionMap = new HashMap<String, String>(peopleProjectionMap);
    430         sPhoneProjectionMap.put(android.provider.Contacts.Phones._ID,
    431                 android.provider.Contacts.Phones._ID);
    432         sPhoneProjectionMap.put(android.provider.Contacts.Phones.PERSON_ID,
    433                 android.provider.Contacts.Phones.PERSON_ID);
    434         sPhoneProjectionMap.put(android.provider.Contacts.Phones.ISPRIMARY,
    435                 android.provider.Contacts.Phones.ISPRIMARY);
    436         sPhoneProjectionMap.put(android.provider.Contacts.Phones.NUMBER,
    437                 android.provider.Contacts.Phones.NUMBER);
    438         sPhoneProjectionMap.put(android.provider.Contacts.Phones.TYPE,
    439                 android.provider.Contacts.Phones.TYPE);
    440         sPhoneProjectionMap.put(android.provider.Contacts.Phones.LABEL,
    441                 android.provider.Contacts.Phones.LABEL);
    442         sPhoneProjectionMap.put(android.provider.Contacts.Phones.NUMBER_KEY,
    443                 android.provider.Contacts.Phones.NUMBER_KEY);
    444 
    445         sExtensionProjectionMap = new HashMap<String, String>();
    446         sExtensionProjectionMap.put(android.provider.Contacts.Extensions._ID,
    447                 android.provider.Contacts.Extensions._ID);
    448         sExtensionProjectionMap.put(android.provider.Contacts.Extensions.PERSON_ID,
    449                 android.provider.Contacts.Extensions.PERSON_ID);
    450         sExtensionProjectionMap.put(android.provider.Contacts.Extensions.NAME,
    451                 android.provider.Contacts.Extensions.NAME);
    452         sExtensionProjectionMap.put(android.provider.Contacts.Extensions.VALUE,
    453                 android.provider.Contacts.Extensions.VALUE);
    454 
    455         sGroupProjectionMap = new HashMap<String, String>();
    456         sGroupProjectionMap.put(android.provider.Contacts.Groups._ID,
    457                 android.provider.Contacts.Groups._ID);
    458         sGroupProjectionMap.put(android.provider.Contacts.Groups.NAME,
    459                 android.provider.Contacts.Groups.NAME);
    460         sGroupProjectionMap.put(android.provider.Contacts.Groups.NOTES,
    461                 android.provider.Contacts.Groups.NOTES);
    462         sGroupProjectionMap.put(android.provider.Contacts.Groups.SYSTEM_ID,
    463                 android.provider.Contacts.Groups.SYSTEM_ID);
    464 
    465         sGroupMembershipProjectionMap = new HashMap<String, String>(sGroupProjectionMap);
    466         sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership._ID,
    467                 android.provider.Contacts.GroupMembership._ID);
    468         sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership.PERSON_ID,
    469                 android.provider.Contacts.GroupMembership.PERSON_ID);
    470         sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership.GROUP_ID,
    471                 android.provider.Contacts.GroupMembership.GROUP_ID);
    472         sGroupMembershipProjectionMap.put(
    473                 android.provider.Contacts.GroupMembership.GROUP_SYNC_ID,
    474                 android.provider.Contacts.GroupMembership.GROUP_SYNC_ID);
    475         sGroupMembershipProjectionMap.put(
    476                 android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT,
    477                 android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT);
    478         sGroupMembershipProjectionMap.put(
    479                 android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT_TYPE,
    480                 android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT_TYPE);
    481 
    482         sPhotoProjectionMap = new HashMap<String, String>();
    483         sPhotoProjectionMap.put(android.provider.Contacts.Photos._ID,
    484                 android.provider.Contacts.Photos._ID);
    485         sPhotoProjectionMap.put(android.provider.Contacts.Photos.PERSON_ID,
    486                 android.provider.Contacts.Photos.PERSON_ID);
    487         sPhotoProjectionMap.put(android.provider.Contacts.Photos.DATA,
    488                 android.provider.Contacts.Photos.DATA);
    489         sPhotoProjectionMap.put(android.provider.Contacts.Photos.LOCAL_VERSION,
    490                 android.provider.Contacts.Photos.LOCAL_VERSION);
    491         sPhotoProjectionMap.put(android.provider.Contacts.Photos.DOWNLOAD_REQUIRED,
    492                 android.provider.Contacts.Photos.DOWNLOAD_REQUIRED);
    493         sPhotoProjectionMap.put(android.provider.Contacts.Photos.EXISTS_ON_SERVER,
    494                 android.provider.Contacts.Photos.EXISTS_ON_SERVER);
    495         sPhotoProjectionMap.put(android.provider.Contacts.Photos.SYNC_ERROR,
    496                 android.provider.Contacts.Photos.SYNC_ERROR);
    497     }
    498 
    499     private final Context mContext;
    500     private final ContactsDatabaseHelper mDbHelper;
    501     private final ContactsProvider2 mContactsProvider;
    502     private final NameSplitter mPhoneticNameSplitter;
    503     private final GlobalSearchSupport mGlobalSearchSupport;
    504 
    505     private final SQLiteStatement mDataMimetypeQuery;
    506     private final SQLiteStatement mDataRawContactIdQuery;
    507 
    508     private final ContentValues mValues = new ContentValues();
    509     private final ContentValues mValues2 = new ContentValues();
    510     private final ContentValues mValues3 = new ContentValues();
    511     private boolean mDefaultAccountKnown;
    512     private Account mAccount;
    513 
    514     private final long mMimetypeEmail;
    515     private final long mMimetypeIm;
    516     private final long mMimetypePostal;
    517 
    518 
    519     public LegacyApiSupport(Context context, ContactsDatabaseHelper contactsDatabaseHelper,
    520             ContactsProvider2 contactsProvider, GlobalSearchSupport globalSearchSupport) {
    521         mContext = context;
    522         mContactsProvider = contactsProvider;
    523         mDbHelper = contactsDatabaseHelper;
    524         mGlobalSearchSupport = globalSearchSupport;
    525 
    526         mPhoneticNameSplitter = new NameSplitter("", "", "", context
    527                 .getString(com.android.internal.R.string.common_name_conjunctions), Locale
    528                 .getDefault());
    529 
    530         SQLiteDatabase db = mDbHelper.getReadableDatabase();
    531         mDataMimetypeQuery = db.compileStatement(
    532                 "SELECT " + DataColumns.MIMETYPE_ID +
    533                 " FROM " + Tables.DATA +
    534                 " WHERE " + Data._ID + "=?");
    535 
    536         mDataRawContactIdQuery = db.compileStatement(
    537                 "SELECT " + Data.RAW_CONTACT_ID +
    538                 " FROM " + Tables.DATA +
    539                 " WHERE " + Data._ID + "=?");
    540 
    541         mMimetypeEmail = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
    542         mMimetypeIm = mDbHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE);
    543         mMimetypePostal = mDbHelper.getMimeTypeId(StructuredPostal.CONTENT_ITEM_TYPE);
    544     }
    545 
    546     private void ensureDefaultAccount() {
    547         if (!mDefaultAccountKnown) {
    548             mAccount = mContactsProvider.getDefaultAccount();
    549             mDefaultAccountKnown = true;
    550         }
    551     }
    552 
    553     public static void createDatabase(SQLiteDatabase db) {
    554         Log.i(TAG, "Bootstrapping database legacy support");
    555         createViews(db);
    556         createSettingsTable(db);
    557     }
    558 
    559     public static void createViews(SQLiteDatabase db) {
    560 
    561         String peopleColumns = "name." + StructuredName.DISPLAY_NAME
    562                         + " AS " + People.NAME + ", " +
    563                 Tables.RAW_CONTACTS + "." + RawContactsColumns.DISPLAY_NAME
    564                         + " AS " + People.DISPLAY_NAME + ", " +
    565                 PHONETIC_NAME_SQL
    566                         + " AS " + People.PHONETIC_NAME + " , " +
    567                 "note." + Note.NOTE
    568                         + " AS " + People.NOTES + ", " +
    569                 AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
    570                 AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
    571                 Tables.RAW_CONTACTS + "." + RawContacts.TIMES_CONTACTED
    572                         + " AS " + People.TIMES_CONTACTED + ", " +
    573                 Tables.RAW_CONTACTS + "." + RawContacts.LAST_TIME_CONTACTED
    574                         + " AS " + People.LAST_TIME_CONTACTED + ", " +
    575                 Tables.RAW_CONTACTS + "." + RawContacts.CUSTOM_RINGTONE
    576                         + " AS " + People.CUSTOM_RINGTONE + ", " +
    577                 Tables.RAW_CONTACTS + "." + RawContacts.SEND_TO_VOICEMAIL
    578                         + " AS " + People.SEND_TO_VOICEMAIL + ", " +
    579                 Tables.RAW_CONTACTS + "." + RawContacts.STARRED
    580                         + " AS " + People.STARRED + ", " +
    581                 "organization." + Data._ID
    582                         + " AS " + People.PRIMARY_ORGANIZATION_ID + ", " +
    583                 "email." + Data._ID
    584                         + " AS " + People.PRIMARY_EMAIL_ID + ", " +
    585                 "phone." + Data._ID
    586                         + " AS " + People.PRIMARY_PHONE_ID + ", " +
    587                 "phone." + Phone.NUMBER
    588                         + " AS " + People.NUMBER + ", " +
    589                 "phone." + Phone.TYPE
    590                         + " AS " + People.TYPE + ", " +
    591                 "phone." + Phone.LABEL
    592                         + " AS " + People.LABEL + ", " +
    593                 "_PHONE_NUMBER_STRIPPED_REVERSED(phone." + Phone.NUMBER + ")"
    594                         + " AS " + People.NUMBER_KEY;
    595 
    596         db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.PEOPLE + ";");
    597         db.execSQL("CREATE VIEW " + LegacyTables.PEOPLE + " AS SELECT " +
    598                 RawContactsColumns.CONCRETE_ID
    599                         + " AS " + android.provider.Contacts.People._ID + ", " +
    600                 peopleColumns +
    601                 " FROM " + Tables.RAW_CONTACTS + PEOPLE_JOINS +
    602                 " WHERE " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0;");
    603 
    604         db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.ORGANIZATIONS + ";");
    605         db.execSQL("CREATE VIEW " + LegacyTables.ORGANIZATIONS + " AS SELECT " +
    606                 DataColumns.CONCRETE_ID
    607                         + " AS " + android.provider.Contacts.Organizations._ID + ", " +
    608                 Data.RAW_CONTACT_ID
    609                         + " AS " + android.provider.Contacts.Organizations.PERSON_ID + ", " +
    610                 Data.IS_PRIMARY
    611                         + " AS " + android.provider.Contacts.Organizations.ISPRIMARY + ", " +
    612                 AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
    613                 AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
    614                 Organization.COMPANY
    615                         + " AS " + android.provider.Contacts.Organizations.COMPANY + ", " +
    616                 Organization.TYPE
    617                         + " AS " + android.provider.Contacts.Organizations.TYPE + ", " +
    618                 Organization.LABEL
    619                         + " AS " + android.provider.Contacts.Organizations.LABEL + ", " +
    620                 Organization.TITLE
    621                         + " AS " + android.provider.Contacts.Organizations.TITLE +
    622                 " FROM " + Tables.DATA_JOIN_MIMETYPE_RAW_CONTACTS +
    623                 " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
    624                         + Organization.CONTENT_ITEM_TYPE + "'"
    625                         + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
    626         ";");
    627 
    628         db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.CONTACT_METHODS + ";");
    629         db.execSQL("CREATE VIEW " + LegacyTables.CONTACT_METHODS + " AS SELECT " +
    630                 DataColumns.CONCRETE_ID
    631                         + " AS " + ContactMethods._ID + ", " +
    632                 DataColumns.CONCRETE_RAW_CONTACT_ID
    633                         + " AS " + ContactMethods.PERSON_ID + ", " +
    634                 CONTACT_METHOD_KIND_SQL
    635                         + " AS " + ContactMethods.KIND + ", " +
    636                 DataColumns.CONCRETE_IS_PRIMARY
    637                         + " AS " + ContactMethods.ISPRIMARY + ", " +
    638                 Tables.DATA + "." + Email.TYPE
    639                         + " AS " + ContactMethods.TYPE + ", " +
    640                 CONTACT_METHOD_DATA_SQL
    641                         + " AS " + ContactMethods.DATA + ", " +
    642                 Tables.DATA + "." + Email.LABEL
    643                         + " AS " + ContactMethods.LABEL + ", " +
    644                 DataColumns.CONCRETE_DATA14
    645                         + " AS " + ContactMethods.AUX_DATA + ", " +
    646                 peopleColumns +
    647                 " FROM " + Tables.DATA + DATA_JOINS +
    648                 " WHERE " + ContactMethods.KIND + " IS NOT NULL"
    649                     + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
    650         ";");
    651 
    652 
    653         db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.PHONES + ";");
    654         db.execSQL("CREATE VIEW " + LegacyTables.PHONES + " AS SELECT DISTINCT " +
    655                 DataColumns.CONCRETE_ID
    656                         + " AS " + android.provider.Contacts.Phones._ID + ", " +
    657                 DataColumns.CONCRETE_RAW_CONTACT_ID
    658                         + " AS " + android.provider.Contacts.Phones.PERSON_ID + ", " +
    659                 DataColumns.CONCRETE_IS_PRIMARY
    660                         + " AS " + android.provider.Contacts.Phones.ISPRIMARY + ", " +
    661                 Tables.DATA + "." + Phone.NUMBER
    662                         + " AS " + android.provider.Contacts.Phones.NUMBER + ", " +
    663                 Tables.DATA + "." + Phone.TYPE
    664                         + " AS " + android.provider.Contacts.Phones.TYPE + ", " +
    665                 Tables.DATA + "." + Phone.LABEL
    666                         + " AS " + android.provider.Contacts.Phones.LABEL + ", " +
    667                 "_PHONE_NUMBER_STRIPPED_REVERSED(" + Tables.DATA + "." + Phone.NUMBER + ")"
    668                         + " AS " + android.provider.Contacts.Phones.NUMBER_KEY + ", " +
    669                 peopleColumns +
    670                 " FROM " + Tables.DATA
    671                         + " JOIN " + Tables.PHONE_LOOKUP
    672                         + " ON (" + Tables.DATA + "._id = "
    673                                 + Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.DATA_ID + ")"
    674                         + DATA_JOINS +
    675                 " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
    676                         + Phone.CONTENT_ITEM_TYPE + "'"
    677                         + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
    678         ";");
    679 
    680         db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.EXTENSIONS + ";");
    681         db.execSQL("CREATE VIEW " + LegacyTables.EXTENSIONS + " AS SELECT " +
    682                 DataColumns.CONCRETE_ID
    683                         + " AS " + android.provider.Contacts.Extensions._ID + ", " +
    684                 DataColumns.CONCRETE_RAW_CONTACT_ID
    685                         + " AS " + android.provider.Contacts.Extensions.PERSON_ID + ", " +
    686                 AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
    687                 AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
    688                 ExtensionsColumns.NAME
    689                         + " AS " + android.provider.Contacts.Extensions.NAME + ", " +
    690                 ExtensionsColumns.VALUE
    691                         + " AS " + android.provider.Contacts.Extensions.VALUE +
    692                 " FROM " + Tables.DATA_JOIN_MIMETYPE_RAW_CONTACTS +
    693                 " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
    694                         + android.provider.Contacts.Extensions.CONTENT_ITEM_TYPE + "'"
    695                         + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
    696         ";");
    697 
    698         db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.GROUPS + ";");
    699         db.execSQL("CREATE VIEW " + LegacyTables.GROUPS + " AS SELECT " +
    700                 GroupsColumns.CONCRETE_ID + " AS " + android.provider.Contacts.Groups._ID + ", " +
    701                 AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
    702                 AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
    703                 Groups.TITLE + " AS " + android.provider.Contacts.Groups.NAME + ", " +
    704                 Groups.NOTES + " AS " + android.provider.Contacts.Groups.NOTES + " , " +
    705                 Groups.SYSTEM_ID + " AS " + android.provider.Contacts.Groups.SYSTEM_ID +
    706                 " FROM " + Tables.GROUPS +
    707                 " JOIN " + Tables.ACCOUNTS + " ON (" +
    708                 GroupsColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID + ")" +
    709         ";");
    710 
    711         db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.GROUP_MEMBERSHIP + ";");
    712         db.execSQL("CREATE VIEW " + LegacyTables.GROUP_MEMBERSHIP + " AS SELECT " +
    713                 DataColumns.CONCRETE_ID
    714                         + " AS " + android.provider.Contacts.GroupMembership._ID + ", " +
    715                 DataColumns.CONCRETE_RAW_CONTACT_ID
    716                         + " AS " + android.provider.Contacts.GroupMembership.PERSON_ID + ", " +
    717                 AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
    718                 AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
    719                 GroupMembership.GROUP_ROW_ID
    720                         + " AS " + android.provider.Contacts.GroupMembership.GROUP_ID + ", " +
    721                 Groups.TITLE
    722                         + " AS " + android.provider.Contacts.GroupMembership.NAME + ", " +
    723                 Groups.NOTES
    724                         + " AS " + android.provider.Contacts.GroupMembership.NOTES + ", " +
    725                 Groups.SYSTEM_ID
    726                         + " AS " + android.provider.Contacts.GroupMembership.SYSTEM_ID + ", " +
    727                 GroupsColumns.CONCRETE_SOURCE_ID
    728                         + " AS "
    729                         + android.provider.Contacts.GroupMembership.GROUP_SYNC_ID + ", " +
    730                 AccountsColumns.CONCRETE_ACCOUNT_NAME
    731                         + " AS "
    732                         + android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT + ", " +
    733                 AccountsColumns.CONCRETE_ACCOUNT_TYPE
    734                         + " AS "
    735                         + android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT_TYPE +
    736                 " FROM " + Tables.DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_GROUPS +
    737                 " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
    738                         + GroupMembership.CONTENT_ITEM_TYPE + "'"
    739                         + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
    740         ";");
    741 
    742         db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.PHOTOS + ";");
    743         db.execSQL("CREATE VIEW " + LegacyTables.PHOTOS + " AS SELECT " +
    744                 DataColumns.CONCRETE_ID
    745                         + " AS " + android.provider.Contacts.Photos._ID + ", " +
    746                 DataColumns.CONCRETE_RAW_CONTACT_ID
    747                         + " AS " + android.provider.Contacts.Photos.PERSON_ID + ", " +
    748                 AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
    749                 AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
    750                 Tables.DATA + "." + Photo.PHOTO
    751                         + " AS " + android.provider.Contacts.Photos.DATA + ", " +
    752                 "legacy_photo." + LegacyPhotoData.EXISTS_ON_SERVER
    753                         + " AS " + android.provider.Contacts.Photos.EXISTS_ON_SERVER + ", " +
    754                 "legacy_photo." + LegacyPhotoData.DOWNLOAD_REQUIRED
    755                         + " AS " + android.provider.Contacts.Photos.DOWNLOAD_REQUIRED + ", " +
    756                 "legacy_photo." + LegacyPhotoData.LOCAL_VERSION
    757                         + " AS " + android.provider.Contacts.Photos.LOCAL_VERSION + ", " +
    758                 "legacy_photo." + LegacyPhotoData.SYNC_ERROR
    759                         + " AS " + android.provider.Contacts.Photos.SYNC_ERROR +
    760                 " FROM " + Tables.DATA + DATA_JOINS + LEGACY_PHOTO_JOIN +
    761                 " WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
    762                         + Photo.CONTENT_ITEM_TYPE + "'"
    763                         + " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
    764         ";");
    765 
    766     }
    767 
    768     public static void createSettingsTable(SQLiteDatabase db) {
    769         db.execSQL("DROP TABLE IF EXISTS " + LegacyTables.SETTINGS + ";");
    770         db.execSQL("CREATE TABLE " + LegacyTables.SETTINGS + " (" +
    771                 android.provider.Contacts.Settings._ID + " INTEGER PRIMARY KEY," +
    772                 android.provider.Contacts.Settings._SYNC_ACCOUNT + " TEXT," +
    773                 android.provider.Contacts.Settings._SYNC_ACCOUNT_TYPE + " TEXT," +
    774                 android.provider.Contacts.Settings.KEY + " STRING NOT NULL," +
    775                 android.provider.Contacts.Settings.VALUE + " STRING " +
    776         ");");
    777     }
    778 
    779     public Uri insert(Uri uri, ContentValues values) {
    780         ensureDefaultAccount();
    781         final int match = sUriMatcher.match(uri);
    782         long id = 0;
    783         switch (match) {
    784             case PEOPLE:
    785                 id = insertPeople(values);
    786                 break;
    787 
    788             case ORGANIZATIONS:
    789                 id = insertOrganization(values);
    790                 break;
    791 
    792             case PEOPLE_CONTACTMETHODS: {
    793                 long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
    794                 id = insertContactMethod(rawContactId, values);
    795                 break;
    796             }
    797 
    798             case CONTACTMETHODS: {
    799                 long rawContactId = getRequiredValue(values, ContactMethods.PERSON_ID);
    800                 id = insertContactMethod(rawContactId, values);
    801                 break;
    802             }
    803 
    804             case PHONES: {
    805                 long rawContactId = getRequiredValue(values,
    806                         android.provider.Contacts.Phones.PERSON_ID);
    807                 id = insertPhone(rawContactId, values);
    808                 break;
    809             }
    810 
    811             case PEOPLE_PHONES: {
    812                 long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
    813                 id = insertPhone(rawContactId, values);
    814                 break;
    815             }
    816 
    817             case EXTENSIONS: {
    818                 long rawContactId = getRequiredValue(values,
    819                         android.provider.Contacts.Extensions.PERSON_ID);
    820                 id = insertExtension(rawContactId, values);
    821                 break;
    822             }
    823 
    824             case GROUPS:
    825                 id = insertGroup(values);
    826                 break;
    827 
    828             case GROUPMEMBERSHIP: {
    829                 long rawContactId = getRequiredValue(values,
    830                         android.provider.Contacts.GroupMembership.PERSON_ID);
    831                 long groupId = getRequiredValue(values,
    832                         android.provider.Contacts.GroupMembership.GROUP_ID);
    833                 id = insertGroupMembership(rawContactId, groupId);
    834                 break;
    835             }
    836 
    837             default:
    838                 throw new UnsupportedOperationException(mDbHelper.exceptionMessage(uri));
    839         }
    840 
    841         if (id < 0) {
    842             return null;
    843         }
    844 
    845         final Uri result = ContentUris.withAppendedId(uri, id);
    846         onChange(result);
    847         return result;
    848     }
    849 
    850     private long getRequiredValue(ContentValues values, String column) {
    851         final Long value = values.getAsLong(column);
    852         if (value == null) {
    853             throw new RuntimeException("Required value: " + column);
    854         }
    855 
    856         return value;
    857     }
    858 
    859     private long insertPeople(ContentValues values) {
    860         parsePeopleValues(values);
    861 
    862         Uri contactUri = mContactsProvider.insertInTransaction(RawContacts.CONTENT_URI, mValues);
    863         long rawContactId = ContentUris.parseId(contactUri);
    864 
    865         if (mValues2.size() != 0) {
    866             mValues2.put(Data.RAW_CONTACT_ID, rawContactId);
    867             mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues2);
    868         }
    869         if (mValues3.size() != 0) {
    870             mValues3.put(Data.RAW_CONTACT_ID, rawContactId);
    871             mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues3);
    872         }
    873 
    874         return rawContactId;
    875     }
    876 
    877     private long insertOrganization(ContentValues values) {
    878         parseOrganizationValues(values);
    879         ContactsDatabaseHelper.copyLongValue(mValues, Data.RAW_CONTACT_ID,
    880                 values, android.provider.Contacts.Organizations.PERSON_ID);
    881 
    882         Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
    883 
    884         return ContentUris.parseId(uri);
    885     }
    886 
    887     private long insertPhone(long rawContactId, ContentValues values) {
    888         parsePhoneValues(values);
    889         mValues.put(Data.RAW_CONTACT_ID, rawContactId);
    890 
    891         Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
    892 
    893         return ContentUris.parseId(uri);
    894     }
    895 
    896     private long insertContactMethod(long rawContactId, ContentValues values) {
    897         Integer kind = values.getAsInteger(ContactMethods.KIND);
    898         if (kind == null) {
    899             throw new RuntimeException("Required value: " + ContactMethods.KIND);
    900         }
    901 
    902         parseContactMethodValues(kind, values);
    903 
    904         mValues.put(Data.RAW_CONTACT_ID, rawContactId);
    905         Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
    906         return ContentUris.parseId(uri);
    907     }
    908 
    909     private long insertExtension(long rawContactId, ContentValues values) {
    910         mValues.clear();
    911 
    912         mValues.put(Data.RAW_CONTACT_ID, rawContactId);
    913         mValues.put(Data.MIMETYPE, android.provider.Contacts.Extensions.CONTENT_ITEM_TYPE);
    914 
    915         parseExtensionValues(values);
    916 
    917         Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
    918         return ContentUris.parseId(uri);
    919     }
    920 
    921     private long insertGroup(ContentValues values) {
    922         parseGroupValues(values);
    923 
    924         if (mAccount != null) {
    925             mValues.put(Groups.ACCOUNT_NAME, mAccount.name);
    926             mValues.put(Groups.ACCOUNT_TYPE, mAccount.type);
    927         }
    928 
    929         Uri uri = mContactsProvider.insertInTransaction(Groups.CONTENT_URI, mValues);
    930         return ContentUris.parseId(uri);
    931     }
    932 
    933     private long insertGroupMembership(long rawContactId, long groupId) {
    934         mValues.clear();
    935 
    936         mValues.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
    937         mValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
    938         mValues.put(GroupMembership.GROUP_ROW_ID, groupId);
    939 
    940         Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
    941         return ContentUris.parseId(uri);
    942     }
    943 
    944     public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
    945         ensureDefaultAccount();
    946 
    947         int match = sUriMatcher.match(uri);
    948         int count = 0;
    949         switch(match) {
    950             case PEOPLE_UPDATE_CONTACT_TIME: {
    951                 count = updateContactTime(uri, values);
    952                 break;
    953             }
    954 
    955             case PEOPLE_PHOTO: {
    956                 long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
    957                 return updatePhoto(rawContactId, values);
    958             }
    959 
    960             case SETTINGS: {
    961                 return updateSettings(values);
    962             }
    963 
    964             case GROUPMEMBERSHIP:
    965             case GROUPMEMBERSHIP_ID:
    966             case -1: {
    967                 throw new UnsupportedOperationException(mDbHelper.exceptionMessage(uri));
    968             }
    969 
    970             default: {
    971                 count = updateAll(uri, match, values, selection, selectionArgs);
    972             }
    973         }
    974 
    975         if (count > 0) {
    976             mContext.getContentResolver().notifyChange(uri, null);
    977         }
    978 
    979         return count;
    980     }
    981 
    982     private int updateAll(Uri uri, final int match, ContentValues values, String selection,
    983             String[] selectionArgs) {
    984         Cursor c = query(uri, IdQuery.COLUMNS, selection, selectionArgs, null, null);
    985         if (c == null) {
    986             return 0;
    987         }
    988 
    989         int count = 0;
    990         try {
    991             while (c.moveToNext()) {
    992                 long id = c.getLong(IdQuery._ID);
    993                 count += update(match, id, values);
    994             }
    995         } finally {
    996             c.close();
    997         }
    998 
    999         return count;
   1000     }
   1001 
   1002     public int update(int match, long id, ContentValues values) {
   1003         int count = 0;
   1004         switch(match) {
   1005             case PEOPLE:
   1006             case PEOPLE_ID: {
   1007                 count = updatePeople(id, values);
   1008                 break;
   1009             }
   1010 
   1011             case ORGANIZATIONS:
   1012             case ORGANIZATIONS_ID: {
   1013                 count = updateOrganizations(id, values);
   1014                 break;
   1015             }
   1016 
   1017             case PHONES:
   1018             case PHONES_ID: {
   1019                 count = updatePhones(id, values);
   1020                 break;
   1021             }
   1022 
   1023             case CONTACTMETHODS:
   1024             case CONTACTMETHODS_ID: {
   1025                 count = updateContactMethods(id, values);
   1026                 break;
   1027             }
   1028 
   1029             case EXTENSIONS:
   1030             case EXTENSIONS_ID: {
   1031                 count = updateExtensions(id, values);
   1032                 break;
   1033             }
   1034 
   1035             case GROUPS:
   1036             case GROUPS_ID: {
   1037                 count = updateGroups(id, values);
   1038                 break;
   1039             }
   1040 
   1041             case PHOTOS:
   1042             case PHOTOS_ID:
   1043                 count = updatePhotoByDataId(id, values);
   1044                 break;
   1045         }
   1046 
   1047         return count;
   1048     }
   1049 
   1050     private int updatePeople(long rawContactId, ContentValues values) {
   1051         parsePeopleValues(values);
   1052 
   1053         int count = mContactsProvider.updateInTransaction(RawContacts.CONTENT_URI,
   1054                 mValues, RawContacts._ID + "=" + rawContactId, null);
   1055 
   1056         if (count == 0) {
   1057             return 0;
   1058         }
   1059 
   1060         if (mValues2.size() != 0) {
   1061             Uri dataUri = findFirstDataRow(rawContactId, StructuredName.CONTENT_ITEM_TYPE);
   1062             if (dataUri != null) {
   1063                 mContactsProvider.updateInTransaction(dataUri, mValues2, null, null);
   1064             } else {
   1065                 mValues2.put(Data.RAW_CONTACT_ID, rawContactId);
   1066                 mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues2);
   1067             }
   1068         }
   1069 
   1070         if (mValues3.size() != 0) {
   1071             Uri dataUri = findFirstDataRow(rawContactId, Note.CONTENT_ITEM_TYPE);
   1072             if (dataUri != null) {
   1073                 mContactsProvider.updateInTransaction(dataUri, mValues3, null, null);
   1074             } else {
   1075                 mValues3.put(Data.RAW_CONTACT_ID, rawContactId);
   1076                 mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues3);
   1077             }
   1078         }
   1079 
   1080         if (values.containsKey(People.LAST_TIME_CONTACTED) &&
   1081                 !values.containsKey(People.TIMES_CONTACTED)) {
   1082             updateContactTime(rawContactId, values);
   1083         }
   1084 
   1085         return count;
   1086     }
   1087 
   1088     private int updateOrganizations(long dataId, ContentValues values) {
   1089         parseOrganizationValues(values);
   1090 
   1091         return mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
   1092                 Data._ID + "=" + dataId, null);
   1093     }
   1094 
   1095     private int updatePhones(long dataId, ContentValues values) {
   1096         parsePhoneValues(values);
   1097 
   1098         return mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
   1099                 Data._ID + "=" + dataId, null);
   1100     }
   1101 
   1102     private int updateContactMethods(long dataId, ContentValues values) {
   1103         int kind;
   1104 
   1105         mDataMimetypeQuery.bindLong(1, dataId);
   1106         long mimetype_id;
   1107         try {
   1108             mimetype_id = mDataMimetypeQuery.simpleQueryForLong();
   1109         } catch (SQLiteDoneException e) {
   1110             // Data row not found
   1111             return 0;
   1112         }
   1113 
   1114         if (mimetype_id == mMimetypeEmail) {
   1115             kind = android.provider.Contacts.KIND_EMAIL;
   1116         } else if (mimetype_id == mMimetypeIm) {
   1117             kind = android.provider.Contacts.KIND_IM;
   1118         } else if (mimetype_id == mMimetypePostal) {
   1119             kind = android.provider.Contacts.KIND_POSTAL;
   1120         } else {
   1121 
   1122             // Non-legacy kind: return "Not found"
   1123             return 0;
   1124         }
   1125 
   1126         parseContactMethodValues(kind, values);
   1127 
   1128         return mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
   1129                 Data._ID + "=" + dataId, null);
   1130     }
   1131 
   1132     private int updateExtensions(long dataId, ContentValues values) {
   1133         parseExtensionValues(values);
   1134 
   1135         return mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
   1136                 Data._ID + "=" + dataId, null);
   1137     }
   1138 
   1139     private int updateGroups(long groupId, ContentValues values) {
   1140         parseGroupValues(values);
   1141 
   1142         return mContactsProvider.updateInTransaction(Groups.CONTENT_URI, mValues,
   1143                 Groups._ID + "=" + groupId, null);
   1144     }
   1145 
   1146     private int updateContactTime(Uri uri, ContentValues values) {
   1147         long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
   1148         updateContactTime(rawContactId, values);
   1149         return 1;
   1150     }
   1151 
   1152     private void updateContactTime(long rawContactId, ContentValues values) {
   1153         final Long storedTimeContacted = values.getAsLong(People.LAST_TIME_CONTACTED);
   1154         final long lastTimeContacted = storedTimeContacted != null ?
   1155             storedTimeContacted : System.currentTimeMillis();
   1156 
   1157         // TODO check sanctions
   1158         long contactId = mDbHelper.getContactId(rawContactId);
   1159         SQLiteDatabase mDb = mDbHelper.getWritableDatabase();
   1160         mSelectionArgs2[0] = String.valueOf(lastTimeContacted);
   1161         if (contactId != 0) {
   1162             mSelectionArgs2[1] = String.valueOf(contactId);
   1163             mDb.execSQL(CONTACTS_UPDATE_LASTTIMECONTACTED, mSelectionArgs2);
   1164             // increment times_contacted column
   1165             mSelectionArgs1[0] = String.valueOf(contactId);
   1166             mDb.execSQL(ContactsProvider2.UPDATE_TIMES_CONTACTED_CONTACTS_TABLE, mSelectionArgs1);
   1167         }
   1168         mSelectionArgs2[1] = String.valueOf(rawContactId);
   1169         mDb.execSQL(RAWCONTACTS_UPDATE_LASTTIMECONTACTED, mSelectionArgs2);
   1170         // increment times_contacted column
   1171         mSelectionArgs1[0] = String.valueOf(contactId);
   1172         mDb.execSQL(ContactsProvider2.UPDATE_TIMES_CONTACTED_RAWCONTACTS_TABLE, mSelectionArgs1);
   1173     }
   1174 
   1175     private int updatePhoto(long rawContactId, ContentValues values) {
   1176 
   1177         // TODO check sanctions
   1178 
   1179         int count;
   1180 
   1181         long dataId = findFirstDataId(rawContactId, Photo.CONTENT_ITEM_TYPE);
   1182 
   1183         mValues.clear();
   1184         byte[] bytes = values.getAsByteArray(android.provider.Contacts.Photos.DATA);
   1185         mValues.put(Photo.PHOTO, bytes);
   1186 
   1187         if (dataId == -1) {
   1188             mValues.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
   1189             mValues.put(Data.RAW_CONTACT_ID, rawContactId);
   1190             Uri dataUri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
   1191             dataId = ContentUris.parseId(dataUri);
   1192             count = 1;
   1193         } else {
   1194             Uri dataUri = ContentUris.withAppendedId(Data.CONTENT_URI, dataId);
   1195             count = mContactsProvider.updateInTransaction(dataUri, mValues, null, null);
   1196         }
   1197 
   1198         updateLegacyPhotoData(rawContactId, dataId, values);
   1199 
   1200         return count;
   1201     }
   1202 
   1203     private int updatePhotoByDataId(long dataId, ContentValues values) {
   1204 
   1205         mDataRawContactIdQuery.bindLong(1, dataId);
   1206         long rawContactId;
   1207 
   1208         try {
   1209             rawContactId = mDataRawContactIdQuery.simpleQueryForLong();
   1210         } catch (SQLiteDoneException e) {
   1211             // Data row not found
   1212             return 0;
   1213         }
   1214 
   1215         if (values.containsKey(android.provider.Contacts.Photos.DATA)) {
   1216             byte[] bytes = values.getAsByteArray(android.provider.Contacts.Photos.DATA);
   1217             mValues.clear();
   1218             mValues.put(Photo.PHOTO, bytes);
   1219             mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
   1220                     Data._ID + "=" + dataId, null);
   1221         }
   1222 
   1223         updateLegacyPhotoData(rawContactId, dataId, values);
   1224 
   1225         return 1;
   1226     }
   1227 
   1228     private void updateLegacyPhotoData(long rawContactId, long dataId, ContentValues values) {
   1229         mValues.clear();
   1230         ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.LOCAL_VERSION,
   1231                 values, android.provider.Contacts.Photos.LOCAL_VERSION);
   1232         ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.DOWNLOAD_REQUIRED,
   1233                 values, android.provider.Contacts.Photos.DOWNLOAD_REQUIRED);
   1234         ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.EXISTS_ON_SERVER,
   1235                 values, android.provider.Contacts.Photos.EXISTS_ON_SERVER);
   1236         ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.SYNC_ERROR,
   1237                 values, android.provider.Contacts.Photos.SYNC_ERROR);
   1238 
   1239         int updated = mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
   1240                 Data.MIMETYPE + "='" + LegacyPhotoData.CONTENT_ITEM_TYPE + "'"
   1241                         + " AND " + Data.RAW_CONTACT_ID + "=" + rawContactId
   1242                         + " AND " + LegacyPhotoData.PHOTO_DATA_ID + "=" + dataId, null);
   1243         if (updated == 0) {
   1244             mValues.put(Data.RAW_CONTACT_ID, rawContactId);
   1245             mValues.put(Data.MIMETYPE, LegacyPhotoData.CONTENT_ITEM_TYPE);
   1246             mValues.put(LegacyPhotoData.PHOTO_DATA_ID, dataId);
   1247             mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
   1248         }
   1249     }
   1250 
   1251     private int updateSettings(ContentValues values) {
   1252         SQLiteDatabase db = mDbHelper.getWritableDatabase();
   1253         String accountName = values.getAsString(android.provider.Contacts.Settings._SYNC_ACCOUNT);
   1254         String accountType =
   1255                 values.getAsString(android.provider.Contacts.Settings._SYNC_ACCOUNT_TYPE);
   1256         String key = values.getAsString(android.provider.Contacts.Settings.KEY);
   1257         if (key == null) {
   1258             throw new IllegalArgumentException("you must specify the key when updating settings");
   1259         }
   1260         updateSetting(db, accountName, accountType, values);
   1261         if (key.equals(android.provider.Contacts.Settings.SYNC_EVERYTHING)) {
   1262             mValues.clear();
   1263             mValues.put(Settings.SHOULD_SYNC,
   1264                     values.getAsInteger(android.provider.Contacts.Settings.VALUE));
   1265             String selection;
   1266             String[] selectionArgs;
   1267             if (accountName != null && accountType != null) {
   1268 
   1269                 selectionArgs = new String[]{accountName, accountType};
   1270                 selection = Settings.ACCOUNT_NAME + "=?"
   1271                         + " AND " + Settings.ACCOUNT_TYPE + "=?"
   1272                         + " AND " + Settings.DATA_SET + " IS NULL";
   1273             } else {
   1274                 selectionArgs = null;
   1275                 selection = Settings.ACCOUNT_NAME + " IS NULL"
   1276                         + " AND " + Settings.ACCOUNT_TYPE + " IS NULL"
   1277                         + " AND " + Settings.DATA_SET + " IS NULL";
   1278             }
   1279             int count = mContactsProvider.updateInTransaction(Settings.CONTENT_URI, mValues,
   1280                     selection, selectionArgs);
   1281             if (count == 0) {
   1282                 mValues.put(Settings.ACCOUNT_NAME, accountName);
   1283                 mValues.put(Settings.ACCOUNT_TYPE, accountType);
   1284                 mContactsProvider.insertInTransaction(Settings.CONTENT_URI, mValues);
   1285             }
   1286         }
   1287         return 1;
   1288     }
   1289 
   1290     private void updateSetting(SQLiteDatabase db, String accountName, String accountType,
   1291             ContentValues values) {
   1292         final String key = values.getAsString(android.provider.Contacts.Settings.KEY);
   1293         if (accountName == null || accountType == null) {
   1294             db.delete(LegacyTables.SETTINGS, "_sync_account IS NULL AND key=?", new String[]{key});
   1295         } else {
   1296             db.delete(LegacyTables.SETTINGS, "_sync_account=? AND _sync_account_type=? AND key=?",
   1297                     new String[]{accountName, accountType, key});
   1298         }
   1299         long rowId = db.insert(LegacyTables.SETTINGS,
   1300                 android.provider.Contacts.Settings.KEY, values);
   1301         if (rowId < 0) {
   1302             throw new SQLException("error updating settings with " + values);
   1303         }
   1304     }
   1305 
   1306     private interface SettingsMatchQuery {
   1307         String SQL =
   1308             "SELECT "
   1309                     + ContactsContract.Settings.ACCOUNT_NAME + ","
   1310                     + ContactsContract.Settings.ACCOUNT_TYPE + ","
   1311                     + ContactsContract.Settings.SHOULD_SYNC +
   1312             " FROM " + Tables.SETTINGS + " LEFT OUTER JOIN " + LegacyTables.SETTINGS +
   1313             " ON (" + ContactsContract.Settings.ACCOUNT_NAME + "="
   1314                               + android.provider.Contacts.Settings._SYNC_ACCOUNT +
   1315                       " AND " + ContactsContract.Settings.ACCOUNT_TYPE + "="
   1316                               + android.provider.Contacts.Settings._SYNC_ACCOUNT_TYPE +
   1317                       " AND " + ContactsContract.Settings.DATA_SET + " IS NULL" +
   1318                       " AND " + android.provider.Contacts.Settings.KEY + "='"
   1319                               + android.provider.Contacts.Settings.SYNC_EVERYTHING + "'" +
   1320             ")" +
   1321             " WHERE " + ContactsContract.Settings.SHOULD_SYNC + "<>"
   1322                             + android.provider.Contacts.Settings.VALUE;
   1323 
   1324         int ACCOUNT_NAME = 0;
   1325         int ACCOUNT_TYPE = 1;
   1326         int SHOULD_SYNC = 2;
   1327     }
   1328 
   1329     /**
   1330      * Brings legacy settings table in sync with the new settings.
   1331      */
   1332     public void copySettingsToLegacySettings() {
   1333         SQLiteDatabase db = mDbHelper.getWritableDatabase();
   1334         Cursor cursor = db.rawQuery(SettingsMatchQuery.SQL, null);
   1335         try {
   1336             while(cursor.moveToNext()) {
   1337                 String accountName = cursor.getString(SettingsMatchQuery.ACCOUNT_NAME);
   1338                 String accountType = cursor.getString(SettingsMatchQuery.ACCOUNT_TYPE);
   1339                 String value = cursor.getString(SettingsMatchQuery.SHOULD_SYNC);
   1340                 mValues.clear();
   1341                 mValues.put(android.provider.Contacts.Settings._SYNC_ACCOUNT, accountName);
   1342                 mValues.put(android.provider.Contacts.Settings._SYNC_ACCOUNT_TYPE, accountType);
   1343                 mValues.put(android.provider.Contacts.Settings.KEY,
   1344                         android.provider.Contacts.Settings.SYNC_EVERYTHING);
   1345                 mValues.put(android.provider.Contacts.Settings.VALUE, value);
   1346                 updateSetting(db, accountName, accountType, mValues);
   1347             }
   1348         } finally {
   1349             cursor.close();
   1350         }
   1351     }
   1352 
   1353     private void parsePeopleValues(ContentValues values) {
   1354         mValues.clear();
   1355         mValues2.clear();
   1356         mValues3.clear();
   1357 
   1358         ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
   1359                 values, People.CUSTOM_RINGTONE);
   1360         ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
   1361                 values, People.SEND_TO_VOICEMAIL);
   1362         ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
   1363                 values, People.LAST_TIME_CONTACTED);
   1364         ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
   1365                 values, People.TIMES_CONTACTED);
   1366         ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
   1367                 values, People.STARRED);
   1368         if (mAccount != null) {
   1369             mValues.put(RawContacts.ACCOUNT_NAME, mAccount.name);
   1370             mValues.put(RawContacts.ACCOUNT_TYPE, mAccount.type);
   1371         }
   1372 
   1373         if (values.containsKey(People.NAME) || values.containsKey(People.PHONETIC_NAME)) {
   1374             mValues2.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
   1375             ContactsDatabaseHelper.copyStringValue(mValues2, StructuredName.DISPLAY_NAME,
   1376                     values, People.NAME);
   1377             if (values.containsKey(People.PHONETIC_NAME)) {
   1378                 String phoneticName = values.getAsString(People.PHONETIC_NAME);
   1379                 NameSplitter.Name parsedName = new NameSplitter.Name();
   1380                 mPhoneticNameSplitter.split(parsedName, phoneticName);
   1381                 mValues2.put(StructuredName.PHONETIC_GIVEN_NAME, parsedName.getGivenNames());
   1382                 mValues2.put(StructuredName.PHONETIC_MIDDLE_NAME, parsedName.getMiddleName());
   1383                 mValues2.put(StructuredName.PHONETIC_FAMILY_NAME, parsedName.getFamilyName());
   1384             }
   1385         }
   1386 
   1387         if (values.containsKey(People.NOTES)) {
   1388             mValues3.put(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE);
   1389             ContactsDatabaseHelper.copyStringValue(mValues3, Note.NOTE, values, People.NOTES);
   1390         }
   1391     }
   1392 
   1393     private void parseOrganizationValues(ContentValues values) {
   1394         mValues.clear();
   1395 
   1396         mValues.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
   1397 
   1398         ContactsDatabaseHelper.copyLongValue(mValues, Data.IS_PRIMARY,
   1399                 values, android.provider.Contacts.Organizations.ISPRIMARY);
   1400 
   1401         ContactsDatabaseHelper.copyStringValue(mValues, Organization.COMPANY,
   1402                 values, android.provider.Contacts.Organizations.COMPANY);
   1403 
   1404         // TYPE values happen to remain the same between V1 and V2 - can just copy the value
   1405         ContactsDatabaseHelper.copyLongValue(mValues, Organization.TYPE,
   1406                 values, android.provider.Contacts.Organizations.TYPE);
   1407 
   1408         ContactsDatabaseHelper.copyStringValue(mValues, Organization.LABEL,
   1409                 values, android.provider.Contacts.Organizations.LABEL);
   1410         ContactsDatabaseHelper.copyStringValue(mValues, Organization.TITLE,
   1411                 values, android.provider.Contacts.Organizations.TITLE);
   1412     }
   1413 
   1414     private void parsePhoneValues(ContentValues values) {
   1415         mValues.clear();
   1416 
   1417         mValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
   1418 
   1419         ContactsDatabaseHelper.copyLongValue(mValues, Data.IS_PRIMARY,
   1420                 values, android.provider.Contacts.Phones.ISPRIMARY);
   1421 
   1422         ContactsDatabaseHelper.copyStringValue(mValues, Phone.NUMBER,
   1423                 values, android.provider.Contacts.Phones.NUMBER);
   1424 
   1425         // TYPE values happen to remain the same between V1 and V2 - can just copy the value
   1426         ContactsDatabaseHelper.copyLongValue(mValues, Phone.TYPE,
   1427                 values, android.provider.Contacts.Phones.TYPE);
   1428 
   1429         ContactsDatabaseHelper.copyStringValue(mValues, Phone.LABEL,
   1430                 values, android.provider.Contacts.Phones.LABEL);
   1431     }
   1432 
   1433     private void parseContactMethodValues(int kind, ContentValues values) {
   1434         mValues.clear();
   1435 
   1436         ContactsDatabaseHelper.copyLongValue(mValues, Data.IS_PRIMARY, values,
   1437                 ContactMethods.ISPRIMARY);
   1438 
   1439         switch (kind) {
   1440             case android.provider.Contacts.KIND_EMAIL: {
   1441                 copyCommonFields(values, Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL,
   1442                         Data.DATA14);
   1443                 ContactsDatabaseHelper.copyStringValue(mValues, Email.DATA, values,
   1444                         ContactMethods.DATA);
   1445                 break;
   1446             }
   1447 
   1448             case android.provider.Contacts.KIND_IM: {
   1449                 String protocol = values.getAsString(ContactMethods.DATA);
   1450                 if (protocol.startsWith("pre:")) {
   1451                     mValues.put(Im.PROTOCOL, Integer.parseInt(protocol.substring(4)));
   1452                 } else if (protocol.startsWith("custom:")) {
   1453                     mValues.put(Im.PROTOCOL, Im.PROTOCOL_CUSTOM);
   1454                     mValues.put(Im.CUSTOM_PROTOCOL, protocol.substring(7));
   1455                 }
   1456 
   1457                 copyCommonFields(values, Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL, Data.DATA14);
   1458                 break;
   1459             }
   1460 
   1461             case android.provider.Contacts.KIND_POSTAL: {
   1462                 copyCommonFields(values, StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE,
   1463                         StructuredPostal.LABEL, Data.DATA14);
   1464                 ContactsDatabaseHelper.copyStringValue(mValues, StructuredPostal.FORMATTED_ADDRESS,
   1465                         values, ContactMethods.DATA);
   1466                 break;
   1467             }
   1468         }
   1469     }
   1470 
   1471     private void copyCommonFields(ContentValues values, String mimeType, String typeColumn,
   1472             String labelColumn, String auxDataColumn) {
   1473         mValues.put(Data.MIMETYPE, mimeType);
   1474         ContactsDatabaseHelper.copyLongValue(mValues, typeColumn, values,
   1475                 ContactMethods.TYPE);
   1476         ContactsDatabaseHelper.copyStringValue(mValues, labelColumn, values,
   1477                 ContactMethods.LABEL);
   1478         ContactsDatabaseHelper.copyStringValue(mValues, auxDataColumn, values,
   1479                 ContactMethods.AUX_DATA);
   1480     }
   1481 
   1482     private void parseGroupValues(ContentValues values) {
   1483         mValues.clear();
   1484 
   1485         ContactsDatabaseHelper.copyStringValue(mValues, Groups.TITLE,
   1486                 values, android.provider.Contacts.Groups.NAME);
   1487         ContactsDatabaseHelper.copyStringValue(mValues, Groups.NOTES,
   1488                 values, android.provider.Contacts.Groups.NOTES);
   1489         ContactsDatabaseHelper.copyStringValue(mValues, Groups.SYSTEM_ID,
   1490                 values, android.provider.Contacts.Groups.SYSTEM_ID);
   1491     }
   1492 
   1493     private void parseExtensionValues(ContentValues values) {
   1494         ContactsDatabaseHelper.copyStringValue(mValues, ExtensionsColumns.NAME,
   1495                 values, android.provider.Contacts.People.Extensions.NAME);
   1496         ContactsDatabaseHelper.copyStringValue(mValues, ExtensionsColumns.VALUE,
   1497                 values, android.provider.Contacts.People.Extensions.VALUE);
   1498     }
   1499 
   1500     private Uri findFirstDataRow(long rawContactId, String contentItemType) {
   1501         long dataId = findFirstDataId(rawContactId, contentItemType);
   1502         if (dataId == -1) {
   1503             return null;
   1504         }
   1505 
   1506         return ContentUris.withAppendedId(Data.CONTENT_URI, dataId);
   1507     }
   1508 
   1509     private long findFirstDataId(long rawContactId, String mimeType) {
   1510         long dataId = -1;
   1511         Cursor c = mContactsProvider.query(Data.CONTENT_URI, IdQuery.COLUMNS,
   1512                 Data.RAW_CONTACT_ID + "=" + rawContactId + " AND "
   1513                         + Data.MIMETYPE + "='" + mimeType + "'",
   1514                 null, null);
   1515         try {
   1516             if (c.moveToFirst()) {
   1517                 dataId = c.getLong(IdQuery._ID);
   1518             }
   1519         } finally {
   1520             c.close();
   1521         }
   1522         return dataId;
   1523     }
   1524 
   1525 
   1526     public int delete(Uri uri, String selection, String[] selectionArgs) {
   1527         final int match = sUriMatcher.match(uri);
   1528         if (match == -1 || match == SETTINGS) {
   1529             throw new UnsupportedOperationException(mDbHelper.exceptionMessage(uri));
   1530         }
   1531 
   1532         Cursor c = query(uri, IdQuery.COLUMNS, selection, selectionArgs, null, null);
   1533         if (c == null) {
   1534             return 0;
   1535         }
   1536 
   1537         int count = 0;
   1538         try {
   1539             while (c.moveToNext()) {
   1540                 long id = c.getLong(IdQuery._ID);
   1541                 count += delete(uri, match, id);
   1542             }
   1543         } finally {
   1544             c.close();
   1545         }
   1546 
   1547         return count;
   1548     }
   1549 
   1550     public int delete(Uri uri, int match, long id) {
   1551         int count = 0;
   1552         switch (match) {
   1553             case PEOPLE:
   1554             case PEOPLE_ID:
   1555                 count = mContactsProvider.deleteRawContact(id, mDbHelper.getContactId(id), false);
   1556                 break;
   1557 
   1558             case PEOPLE_PHOTO:
   1559                 mValues.clear();
   1560                 mValues.putNull(android.provider.Contacts.Photos.DATA);
   1561                 updatePhoto(id, mValues);
   1562                 break;
   1563 
   1564             case ORGANIZATIONS:
   1565             case ORGANIZATIONS_ID:
   1566                 count = mContactsProvider.deleteData(id, ORGANIZATION_MIME_TYPES);
   1567                 break;
   1568 
   1569             case CONTACTMETHODS:
   1570             case CONTACTMETHODS_ID:
   1571                 count = mContactsProvider.deleteData(id, CONTACT_METHOD_MIME_TYPES);
   1572                 break;
   1573 
   1574             case PHONES:
   1575             case PHONES_ID:
   1576                 count = mContactsProvider.deleteData(id, PHONE_MIME_TYPES);
   1577                 break;
   1578 
   1579             case EXTENSIONS:
   1580             case EXTENSIONS_ID:
   1581                 count = mContactsProvider.deleteData(id, EXTENSION_MIME_TYPES);
   1582                 break;
   1583 
   1584             case PHOTOS:
   1585             case PHOTOS_ID:
   1586                 count = mContactsProvider.deleteData(id, PHOTO_MIME_TYPES);
   1587                 break;
   1588 
   1589             case GROUPMEMBERSHIP:
   1590             case GROUPMEMBERSHIP_ID:
   1591                 count = mContactsProvider.deleteData(id, GROUP_MEMBERSHIP_MIME_TYPES);
   1592                 break;
   1593 
   1594             case GROUPS:
   1595             case GROUPS_ID:
   1596                 count = mContactsProvider.deleteGroup(uri, id, false);
   1597                 break;
   1598 
   1599             default:
   1600                 throw new UnsupportedOperationException(mDbHelper.exceptionMessage(uri));
   1601         }
   1602 
   1603         return count;
   1604     }
   1605 
   1606     public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
   1607             String sortOrder, String limit) {
   1608         ensureDefaultAccount();
   1609 
   1610         final SQLiteDatabase db = mDbHelper.getReadableDatabase();
   1611         SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
   1612         String groupBy = null;
   1613 
   1614         final int match = sUriMatcher.match(uri);
   1615         switch (match) {
   1616             case PEOPLE: {
   1617                 qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
   1618                 qb.setProjectionMap(sPeopleProjectionMap);
   1619                 applyRawContactsAccount(qb);
   1620                 break;
   1621             }
   1622 
   1623             case PEOPLE_ID:
   1624                 qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
   1625                 qb.setProjectionMap(sPeopleProjectionMap);
   1626                 applyRawContactsAccount(qb);
   1627                 qb.appendWhere(" AND " + People._ID + "=");
   1628                 qb.appendWhere(uri.getPathSegments().get(1));
   1629                 break;
   1630 
   1631             case PEOPLE_FILTER: {
   1632                 qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
   1633                 qb.setProjectionMap(sPeopleProjectionMap);
   1634                 applyRawContactsAccount(qb);
   1635                 String filterParam = uri.getPathSegments().get(2);
   1636                 qb.appendWhere(" AND " + People._ID + " IN "
   1637                         + getRawContactsByFilterAsNestedQuery(filterParam));
   1638                 break;
   1639             }
   1640 
   1641             case GROUP_NAME_MEMBERS:
   1642                 qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
   1643                 qb.setProjectionMap(sPeopleProjectionMap);
   1644                 applyRawContactsAccount(qb);
   1645                 String group = uri.getPathSegments().get(2);
   1646                 qb.appendWhere(" AND " + buildGroupNameMatchWhereClause(group));
   1647                 break;
   1648 
   1649             case GROUP_SYSTEM_ID_MEMBERS:
   1650                 qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
   1651                 qb.setProjectionMap(sPeopleProjectionMap);
   1652                 applyRawContactsAccount(qb);
   1653                 String systemId = uri.getPathSegments().get(2);
   1654                 qb.appendWhere(" AND " + buildGroupSystemIdMatchWhereClause(systemId));
   1655                 break;
   1656 
   1657             case ORGANIZATIONS:
   1658                 qb.setTables(LegacyTables.ORGANIZATIONS + " organizations");
   1659                 qb.setProjectionMap(sOrganizationProjectionMap);
   1660                 applyRawContactsAccount(qb);
   1661                 break;
   1662 
   1663             case ORGANIZATIONS_ID:
   1664                 qb.setTables(LegacyTables.ORGANIZATIONS + " organizations");
   1665                 qb.setProjectionMap(sOrganizationProjectionMap);
   1666                 applyRawContactsAccount(qb);
   1667                 qb.appendWhere(" AND " + android.provider.Contacts.Organizations._ID + "=");
   1668                 qb.appendWhere(uri.getPathSegments().get(1));
   1669                 break;
   1670 
   1671             case PEOPLE_ORGANIZATIONS:
   1672                 qb.setTables(LegacyTables.ORGANIZATIONS + " organizations");
   1673                 qb.setProjectionMap(sOrganizationProjectionMap);
   1674                 applyRawContactsAccount(qb);
   1675                 qb.appendWhere(" AND " + android.provider.Contacts.Organizations.PERSON_ID + "=");
   1676                 qb.appendWhere(uri.getPathSegments().get(1));
   1677                 break;
   1678 
   1679             case PEOPLE_ORGANIZATIONS_ID:
   1680                 qb.setTables(LegacyTables.ORGANIZATIONS + " organizations");
   1681                 qb.setProjectionMap(sOrganizationProjectionMap);
   1682                 applyRawContactsAccount(qb);
   1683                 qb.appendWhere(" AND " + android.provider.Contacts.Organizations.PERSON_ID + "=");
   1684                 qb.appendWhere(uri.getPathSegments().get(1));
   1685                 qb.appendWhere(" AND " + android.provider.Contacts.Organizations._ID + "=");
   1686                 qb.appendWhere(uri.getPathSegments().get(3));
   1687                 break;
   1688 
   1689             case CONTACTMETHODS:
   1690                 qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
   1691                 qb.setProjectionMap(sContactMethodProjectionMap);
   1692                 applyRawContactsAccount(qb);
   1693                 break;
   1694 
   1695             case CONTACTMETHODS_ID:
   1696                 qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
   1697                 qb.setProjectionMap(sContactMethodProjectionMap);
   1698                 applyRawContactsAccount(qb);
   1699                 qb.appendWhere(" AND " + ContactMethods._ID + "=");
   1700                 qb.appendWhere(uri.getPathSegments().get(1));
   1701                 break;
   1702 
   1703             case CONTACTMETHODS_EMAIL:
   1704                 qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
   1705                 qb.setProjectionMap(sContactMethodProjectionMap);
   1706                 applyRawContactsAccount(qb);
   1707                 qb.appendWhere(" AND " + ContactMethods.KIND + "="
   1708                         + android.provider.Contacts.KIND_EMAIL);
   1709                 break;
   1710 
   1711             case PEOPLE_CONTACTMETHODS:
   1712                 qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
   1713                 qb.setProjectionMap(sContactMethodProjectionMap);
   1714                 applyRawContactsAccount(qb);
   1715                 qb.appendWhere(" AND " + ContactMethods.PERSON_ID + "=");
   1716                 qb.appendWhere(uri.getPathSegments().get(1));
   1717                 qb.appendWhere(" AND " + ContactMethods.KIND + " IS NOT NULL");
   1718                 break;
   1719 
   1720             case PEOPLE_CONTACTMETHODS_ID:
   1721                 qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
   1722                 qb.setProjectionMap(sContactMethodProjectionMap);
   1723                 applyRawContactsAccount(qb);
   1724                 qb.appendWhere(" AND " + ContactMethods.PERSON_ID + "=");
   1725                 qb.appendWhere(uri.getPathSegments().get(1));
   1726                 qb.appendWhere(" AND " + ContactMethods._ID + "=");
   1727                 qb.appendWhere(uri.getPathSegments().get(3));
   1728                 qb.appendWhere(" AND " + ContactMethods.KIND + " IS NOT NULL");
   1729                 break;
   1730 
   1731             case PHONES:
   1732                 qb.setTables(LegacyTables.PHONES + " phones");
   1733                 qb.setProjectionMap(sPhoneProjectionMap);
   1734                 applyRawContactsAccount(qb);
   1735                 break;
   1736 
   1737             case PHONES_ID:
   1738                 qb.setTables(LegacyTables.PHONES + " phones");
   1739                 qb.setProjectionMap(sPhoneProjectionMap);
   1740                 applyRawContactsAccount(qb);
   1741                 qb.appendWhere(" AND " + android.provider.Contacts.Phones._ID + "=");
   1742                 qb.appendWhere(uri.getPathSegments().get(1));
   1743                 break;
   1744 
   1745             case PHONES_FILTER:
   1746                 qb.setTables(LegacyTables.PHONES + " phones");
   1747                 qb.setProjectionMap(sPhoneProjectionMap);
   1748                 applyRawContactsAccount(qb);
   1749                 if (uri.getPathSegments().size() > 2) {
   1750                     String filterParam = uri.getLastPathSegment();
   1751                     qb.appendWhere(" AND person =");
   1752                     qb.appendWhere(mDbHelper.buildPhoneLookupAsNestedQuery(filterParam));
   1753                     qb.setDistinct(true);
   1754                 }
   1755                 break;
   1756 
   1757             case PEOPLE_PHONES:
   1758                 qb.setTables(LegacyTables.PHONES + " phones");
   1759                 qb.setProjectionMap(sPhoneProjectionMap);
   1760                 applyRawContactsAccount(qb);
   1761                 qb.appendWhere(" AND " + android.provider.Contacts.Phones.PERSON_ID + "=");
   1762                 qb.appendWhere(uri.getPathSegments().get(1));
   1763                 break;
   1764 
   1765             case PEOPLE_PHONES_ID:
   1766                 qb.setTables(LegacyTables.PHONES + " phones");
   1767                 qb.setProjectionMap(sPhoneProjectionMap);
   1768                 applyRawContactsAccount(qb);
   1769                 qb.appendWhere(" AND " + android.provider.Contacts.Phones.PERSON_ID + "=");
   1770                 qb.appendWhere(uri.getPathSegments().get(1));
   1771                 qb.appendWhere(" AND " + android.provider.Contacts.Phones._ID + "=");
   1772                 qb.appendWhere(uri.getPathSegments().get(3));
   1773                 break;
   1774 
   1775             case EXTENSIONS:
   1776                 qb.setTables(LegacyTables.EXTENSIONS + " extensions");
   1777                 qb.setProjectionMap(sExtensionProjectionMap);
   1778                 applyRawContactsAccount(qb);
   1779                 break;
   1780 
   1781             case EXTENSIONS_ID:
   1782                 qb.setTables(LegacyTables.EXTENSIONS + " extensions");
   1783                 qb.setProjectionMap(sExtensionProjectionMap);
   1784                 applyRawContactsAccount(qb);
   1785                 qb.appendWhere(" AND " + android.provider.Contacts.Extensions._ID + "=");
   1786                 qb.appendWhere(uri.getPathSegments().get(1));
   1787                 break;
   1788 
   1789             case PEOPLE_EXTENSIONS:
   1790                 qb.setTables(LegacyTables.EXTENSIONS + " extensions");
   1791                 qb.setProjectionMap(sExtensionProjectionMap);
   1792                 applyRawContactsAccount(qb);
   1793                 qb.appendWhere(" AND " + android.provider.Contacts.Extensions.PERSON_ID + "=");
   1794                 qb.appendWhere(uri.getPathSegments().get(1));
   1795                 break;
   1796 
   1797             case PEOPLE_EXTENSIONS_ID:
   1798                 qb.setTables(LegacyTables.EXTENSIONS + " extensions");
   1799                 qb.setProjectionMap(sExtensionProjectionMap);
   1800                 applyRawContactsAccount(qb);
   1801                 qb.appendWhere(" AND " + android.provider.Contacts.Extensions.PERSON_ID + "=");
   1802                 qb.appendWhere(uri.getPathSegments().get(1));
   1803                 qb.appendWhere(" AND " + android.provider.Contacts.Extensions._ID + "=");
   1804                 qb.appendWhere(uri.getPathSegments().get(3));
   1805                 break;
   1806 
   1807             case GROUPS:
   1808                 qb.setTables(LegacyTables.GROUPS + " groups");
   1809                 qb.setProjectionMap(sGroupProjectionMap);
   1810                 applyGroupAccount(qb);
   1811                 break;
   1812 
   1813             case GROUPS_ID:
   1814                 qb.setTables(LegacyTables.GROUPS + " groups");
   1815                 qb.setProjectionMap(sGroupProjectionMap);
   1816                 applyGroupAccount(qb);
   1817                 qb.appendWhere(" AND " + android.provider.Contacts.Groups._ID + "=");
   1818                 qb.appendWhere(uri.getPathSegments().get(1));
   1819                 break;
   1820 
   1821             case GROUPMEMBERSHIP:
   1822                 qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership");
   1823                 qb.setProjectionMap(sGroupMembershipProjectionMap);
   1824                 applyRawContactsAccount(qb);
   1825                 break;
   1826 
   1827             case GROUPMEMBERSHIP_ID:
   1828                 qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership");
   1829                 qb.setProjectionMap(sGroupMembershipProjectionMap);
   1830                 applyRawContactsAccount(qb);
   1831                 qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership._ID + "=");
   1832                 qb.appendWhere(uri.getPathSegments().get(1));
   1833                 break;
   1834 
   1835             case PEOPLE_GROUPMEMBERSHIP:
   1836                 qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership");
   1837                 qb.setProjectionMap(sGroupMembershipProjectionMap);
   1838                 applyRawContactsAccount(qb);
   1839                 qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership.PERSON_ID + "=");
   1840                 qb.appendWhere(uri.getPathSegments().get(1));
   1841                 break;
   1842 
   1843             case PEOPLE_GROUPMEMBERSHIP_ID:
   1844                 qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership");
   1845                 qb.setProjectionMap(sGroupMembershipProjectionMap);
   1846                 applyRawContactsAccount(qb);
   1847                 qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership.PERSON_ID + "=");
   1848                 qb.appendWhere(uri.getPathSegments().get(1));
   1849                 qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership._ID + "=");
   1850                 qb.appendWhere(uri.getPathSegments().get(3));
   1851                 break;
   1852 
   1853             case PEOPLE_PHOTO:
   1854                 qb.setTables(LegacyTables.PHOTOS + " photos");
   1855                 qb.setProjectionMap(sPhotoProjectionMap);
   1856                 applyRawContactsAccount(qb);
   1857                 qb.appendWhere(" AND " + android.provider.Contacts.Photos.PERSON_ID + "=");
   1858                 qb.appendWhere(uri.getPathSegments().get(1));
   1859                 limit = "1";
   1860                 break;
   1861 
   1862             case PHOTOS:
   1863                 qb.setTables(LegacyTables.PHOTOS + " photos");
   1864                 qb.setProjectionMap(sPhotoProjectionMap);
   1865                 applyRawContactsAccount(qb);
   1866                 break;
   1867 
   1868             case PHOTOS_ID:
   1869                 qb.setTables(LegacyTables.PHOTOS + " photos");
   1870                 qb.setProjectionMap(sPhotoProjectionMap);
   1871                 applyRawContactsAccount(qb);
   1872                 qb.appendWhere(" AND " + android.provider.Contacts.Photos._ID + "=");
   1873                 qb.appendWhere(uri.getPathSegments().get(1));
   1874                 break;
   1875 
   1876             case SEARCH_SUGGESTIONS:
   1877                 return mGlobalSearchSupport.handleSearchSuggestionsQuery(
   1878                         db, uri, projection, limit, null);
   1879 
   1880             case SEARCH_SHORTCUT: {
   1881                 String lookupKey = uri.getLastPathSegment();
   1882                 String filter = ContactsProvider2.getQueryParameter(uri, "filter");
   1883                 return mGlobalSearchSupport.handleSearchShortcutRefresh(
   1884                         db, projection, lookupKey, filter, null);
   1885             }
   1886 
   1887             case LIVE_FOLDERS_PEOPLE:
   1888                 return mContactsProvider.query(LIVE_FOLDERS_CONTACTS_URI,
   1889                         projection, selection, selectionArgs, sortOrder);
   1890 
   1891             case LIVE_FOLDERS_PEOPLE_WITH_PHONES:
   1892                 return mContactsProvider.query(LIVE_FOLDERS_CONTACTS_WITH_PHONES_URI,
   1893                         projection, selection, selectionArgs, sortOrder);
   1894 
   1895             case LIVE_FOLDERS_PEOPLE_FAVORITES:
   1896                 return mContactsProvider.query(LIVE_FOLDERS_CONTACTS_FAVORITES_URI,
   1897                         projection, selection, selectionArgs, sortOrder);
   1898 
   1899             case LIVE_FOLDERS_PEOPLE_GROUP_NAME:
   1900                 return mContactsProvider.query(Uri.withAppendedPath(LIVE_FOLDERS_CONTACTS_URI,
   1901                         Uri.encode(uri.getLastPathSegment())),
   1902                         projection, selection, selectionArgs, sortOrder);
   1903 
   1904             case DELETED_PEOPLE:
   1905             case DELETED_GROUPS:
   1906                 throw new UnsupportedOperationException(mDbHelper.exceptionMessage(uri));
   1907 
   1908             case SETTINGS:
   1909                 copySettingsToLegacySettings();
   1910                 qb.setTables(LegacyTables.SETTINGS);
   1911                 break;
   1912 
   1913             default:
   1914                 throw new IllegalArgumentException(mDbHelper.exceptionMessage(uri));
   1915         }
   1916 
   1917         // Perform the query and set the notification uri
   1918         final Cursor c = qb.query(db, projection, selection, selectionArgs,
   1919                 groupBy, null, sortOrder, limit);
   1920         if (c != null) {
   1921             c.setNotificationUri(mContext.getContentResolver(),
   1922                     android.provider.Contacts.CONTENT_URI);
   1923         }
   1924         return c;
   1925     }
   1926 
   1927     private void applyRawContactsAccount(SQLiteQueryBuilder qb) {
   1928         StringBuilder sb = new StringBuilder();
   1929         appendRawContactsAccount(sb);
   1930         qb.appendWhere(sb.toString());
   1931     }
   1932 
   1933     private void appendRawContactsAccount(StringBuilder sb) {
   1934         if (mAccount != null) {
   1935             sb.append(RawContacts.ACCOUNT_NAME + "=");
   1936             DatabaseUtils.appendEscapedSQLString(sb, mAccount.name);
   1937             sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
   1938             DatabaseUtils.appendEscapedSQLString(sb, mAccount.type);
   1939         } else {
   1940             sb.append(RawContacts.ACCOUNT_NAME + " IS NULL" +
   1941                     " AND " + RawContacts.ACCOUNT_TYPE + " IS NULL");
   1942         }
   1943     }
   1944 
   1945     private void applyGroupAccount(SQLiteQueryBuilder qb) {
   1946         StringBuilder sb = new StringBuilder();
   1947         appendGroupAccount(sb);
   1948         qb.appendWhere(sb.toString());
   1949     }
   1950 
   1951     private void appendGroupAccount(StringBuilder sb) {
   1952         if (mAccount != null) {
   1953             sb.append(Groups.ACCOUNT_NAME + "=");
   1954             DatabaseUtils.appendEscapedSQLString(sb, mAccount.name);
   1955             sb.append(" AND " + Groups.ACCOUNT_TYPE + "=");
   1956             DatabaseUtils.appendEscapedSQLString(sb, mAccount.type);
   1957         } else {
   1958             sb.append(Groups.ACCOUNT_NAME + " IS NULL" +
   1959                     " AND " + Groups.ACCOUNT_TYPE + " IS NULL");
   1960         }
   1961     }
   1962 
   1963     /**
   1964      * Build a WHERE clause that restricts the query to match people that are a member of
   1965      * a group with a particular name. The projection map of the query must include
   1966      * {@link People#_ID}.
   1967      *
   1968      * @param groupName The name of the group
   1969      * @return The where clause.
   1970      */
   1971     private String buildGroupNameMatchWhereClause(String groupName) {
   1972         return "people._id IN "
   1973                 + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
   1974                 + " FROM " + Tables.DATA_JOIN_MIMETYPES
   1975                 + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
   1976                         + "' AND " + GroupMembership.GROUP_ROW_ID + "="
   1977                                 + "(SELECT " + Tables.GROUPS + "." + Groups._ID
   1978                                 + " FROM " + Tables.GROUPS
   1979                                 + " WHERE " + Groups.TITLE + "="
   1980                                         + DatabaseUtils.sqlEscapeString(groupName) + "))";
   1981     }
   1982 
   1983     /**
   1984      * Build a WHERE clause that restricts the query to match people that are a member of
   1985      * a group with a particular system id. The projection map of the query must include
   1986      * {@link People#_ID}.
   1987      *
   1988      * @param groupName The name of the group
   1989      * @return The where clause.
   1990      */
   1991     private String buildGroupSystemIdMatchWhereClause(String systemId) {
   1992         return "people._id IN "
   1993                 + "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
   1994                 + " FROM " + Tables.DATA_JOIN_MIMETYPES
   1995                 + " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
   1996                         + "' AND " + GroupMembership.GROUP_ROW_ID + "="
   1997                                 + "(SELECT " + Tables.GROUPS + "." + Groups._ID
   1998                                 + " FROM " + Tables.GROUPS
   1999                                 + " WHERE " + Groups.SYSTEM_ID + "="
   2000                                         + DatabaseUtils.sqlEscapeString(systemId) + "))";
   2001     }
   2002 
   2003     private String getRawContactsByFilterAsNestedQuery(String filterParam) {
   2004         StringBuilder sb = new StringBuilder();
   2005         String normalizedName = NameNormalizer.normalize(filterParam);
   2006         if (TextUtils.isEmpty(normalizedName)) {
   2007             // Effectively an empty IN clause - SQL syntax does not allow an actual empty list here
   2008             sb.append("(0)");
   2009         } else {
   2010             sb.append("(" +
   2011                     "SELECT " + NameLookupColumns.RAW_CONTACT_ID +
   2012                     " FROM " + Tables.NAME_LOOKUP +
   2013                     " WHERE " + NameLookupColumns.NORMALIZED_NAME +
   2014                     " GLOB '");
   2015             // Should not use a "?" argument placeholder here, because
   2016             // that would prevent the SQL optimizer from using the index on NORMALIZED_NAME.
   2017             sb.append(normalizedName);
   2018             sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
   2019                     + NameLookupType.NAME_COLLATION_KEY + ","
   2020                     + NameLookupType.NICKNAME);
   2021             if (true) {
   2022                 sb.append("," + NameLookupType.EMAIL_BASED_NICKNAME);
   2023             }
   2024             sb.append("))");
   2025         }
   2026         return sb.toString();
   2027     }
   2028 
   2029     /**
   2030      * Called when a change has been made.
   2031      *
   2032      * @param uri the uri that the change was made to
   2033      */
   2034     private void onChange(Uri uri) {
   2035         mContext.getContentResolver().notifyChange(android.provider.Contacts.CONTENT_URI, null);
   2036     }
   2037 
   2038     public String getType(Uri uri) {
   2039         int match = sUriMatcher.match(uri);
   2040         switch (match) {
   2041             case EXTENSIONS:
   2042             case PEOPLE_EXTENSIONS:
   2043                 return Extensions.CONTENT_TYPE;
   2044             case EXTENSIONS_ID:
   2045             case PEOPLE_EXTENSIONS_ID:
   2046                 return Extensions.CONTENT_ITEM_TYPE;
   2047             case PEOPLE:
   2048                 return "vnd.android.cursor.dir/person";
   2049             case PEOPLE_ID:
   2050                 return "vnd.android.cursor.item/person";
   2051             case PEOPLE_PHONES:
   2052                 return "vnd.android.cursor.dir/phone";
   2053             case PEOPLE_PHONES_ID:
   2054                 return "vnd.android.cursor.item/phone";
   2055             case PEOPLE_CONTACTMETHODS:
   2056                 return "vnd.android.cursor.dir/contact-methods";
   2057             case PEOPLE_CONTACTMETHODS_ID:
   2058                 return getContactMethodType(uri);
   2059             case PHONES:
   2060                 return "vnd.android.cursor.dir/phone";
   2061             case PHONES_ID:
   2062                 return "vnd.android.cursor.item/phone";
   2063             case PHONES_FILTER:
   2064                 return "vnd.android.cursor.dir/phone";
   2065             case PHOTOS_ID:
   2066                 return "vnd.android.cursor.item/photo";
   2067             case PHOTOS:
   2068                 return "vnd.android.cursor.dir/photo";
   2069             case PEOPLE_PHOTO:
   2070                 return "vnd.android.cursor.item/photo";
   2071             case CONTACTMETHODS:
   2072                 return "vnd.android.cursor.dir/contact-methods";
   2073             case CONTACTMETHODS_ID:
   2074                 return getContactMethodType(uri);
   2075             case ORGANIZATIONS:
   2076                 return "vnd.android.cursor.dir/organizations";
   2077             case ORGANIZATIONS_ID:
   2078                 return "vnd.android.cursor.item/organization";
   2079             case SEARCH_SUGGESTIONS:
   2080                 return SearchManager.SUGGEST_MIME_TYPE;
   2081             case SEARCH_SHORTCUT:
   2082                 return SearchManager.SHORTCUT_MIME_TYPE;
   2083             default:
   2084                 throw new IllegalArgumentException(mDbHelper.exceptionMessage(uri));
   2085         }
   2086     }
   2087 
   2088     private String getContactMethodType(Uri url) {
   2089         String mime = null;
   2090 
   2091         Cursor c = query(url, new String[] {ContactMethods.KIND}, null, null, null, null);
   2092         if (c != null) {
   2093             try {
   2094                 if (c.moveToFirst()) {
   2095                     int kind = c.getInt(0);
   2096                     switch (kind) {
   2097                     case android.provider.Contacts.KIND_EMAIL:
   2098                         mime = "vnd.android.cursor.item/email";
   2099                         break;
   2100 
   2101                     case android.provider.Contacts.KIND_IM:
   2102                         mime = "vnd.android.cursor.item/jabber-im";
   2103                         break;
   2104 
   2105                     case android.provider.Contacts.KIND_POSTAL:
   2106                         mime = "vnd.android.cursor.item/postal-address";
   2107                         break;
   2108                     }
   2109                 }
   2110             } finally {
   2111                 c.close();
   2112             }
   2113         }
   2114         return mime;
   2115     }
   2116 }
   2117