blob: c111cf39553a15e9fd93139810fac819e4ab7280 [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License
*/
package com.android.providers.contacts;
import android.accounts.Account;
import android.app.SearchManager;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDoneException;
import android.database.sqlite.SQLiteQueryBuilder;
import android.database.sqlite.SQLiteStatement;
import android.net.Uri;
import android.provider.BaseColumns;
import android.provider.Contacts.ContactMethods;
import android.provider.Contacts.Extensions;
import android.provider.Contacts.People;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Note;
import android.provider.ContactsContract.CommonDataKinds.Organization;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.Groups;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.Settings;
import android.provider.ContactsContract.StatusUpdates;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import com.android.providers.contacts.ContactsDatabaseHelper.AccountsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.ExtensionsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType;
import com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
import java.util.Locale;
@SuppressWarnings("deprecation")
public class LegacyApiSupport {
private static final String TAG = "ContactsProviderV1";
private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
private static final int PEOPLE = 1;
private static final int PEOPLE_ID = 2;
private static final int PEOPLE_UPDATE_CONTACT_TIME = 3;
private static final int ORGANIZATIONS = 4;
private static final int ORGANIZATIONS_ID = 5;
private static final int PEOPLE_CONTACTMETHODS = 6;
private static final int PEOPLE_CONTACTMETHODS_ID = 7;
private static final int CONTACTMETHODS = 8;
private static final int CONTACTMETHODS_ID = 9;
private static final int PEOPLE_PHONES = 10;
private static final int PEOPLE_PHONES_ID = 11;
private static final int PHONES = 12;
private static final int PHONES_ID = 13;
private static final int EXTENSIONS = 14;
private static final int EXTENSIONS_ID = 15;
private static final int PEOPLE_EXTENSIONS = 16;
private static final int PEOPLE_EXTENSIONS_ID = 17;
private static final int GROUPS = 18;
private static final int GROUPS_ID = 19;
private static final int GROUPMEMBERSHIP = 20;
private static final int GROUPMEMBERSHIP_ID = 21;
private static final int PEOPLE_GROUPMEMBERSHIP = 22;
private static final int PEOPLE_GROUPMEMBERSHIP_ID = 23;
private static final int PEOPLE_PHOTO = 24;
private static final int PHOTOS = 25;
private static final int PHOTOS_ID = 26;
private static final int PEOPLE_FILTER = 29;
private static final int DELETED_PEOPLE = 30;
private static final int DELETED_GROUPS = 31;
private static final int SEARCH_SUGGESTIONS = 32;
private static final int SEARCH_SHORTCUT = 33;
private static final int PHONES_FILTER = 34;
private static final int CONTACTMETHODS_EMAIL = 39;
private static final int GROUP_NAME_MEMBERS = 40;
private static final int GROUP_SYSTEM_ID_MEMBERS = 41;
private static final int PEOPLE_ORGANIZATIONS = 42;
private static final int PEOPLE_ORGANIZATIONS_ID = 43;
private static final int SETTINGS = 44;
private static final String PEOPLE_JOINS =
" JOIN " + Tables.ACCOUNTS + " ON ("
+ RawContactsColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID + ")"
+ " LEFT OUTER JOIN data name ON (raw_contacts._id = name.raw_contact_id"
+ " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = name.mimetype_id)"
+ "='" + StructuredName.CONTENT_ITEM_TYPE + "')"
+ " LEFT OUTER JOIN data organization ON (raw_contacts._id = organization.raw_contact_id"
+ " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = organization.mimetype_id)"
+ "='" + Organization.CONTENT_ITEM_TYPE + "' AND organization.is_primary)"
+ " LEFT OUTER JOIN data email ON (raw_contacts._id = email.raw_contact_id"
+ " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = email.mimetype_id)"
+ "='" + Email.CONTENT_ITEM_TYPE + "' AND email.is_primary)"
+ " LEFT OUTER JOIN data note ON (raw_contacts._id = note.raw_contact_id"
+ " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = note.mimetype_id)"
+ "='" + Note.CONTENT_ITEM_TYPE + "')"
+ " LEFT OUTER JOIN data phone ON (raw_contacts._id = phone.raw_contact_id"
+ " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = phone.mimetype_id)"
+ "='" + Phone.CONTENT_ITEM_TYPE + "' AND phone.is_primary)";
public static final String DATA_JOINS =
" JOIN mimetypes ON (mimetypes._id = data.mimetype_id)"
+ " JOIN raw_contacts ON (raw_contacts._id = data.raw_contact_id)"
+ PEOPLE_JOINS;
public static final String PRESENCE_JOINS =
" LEFT OUTER JOIN " + Tables.PRESENCE +
" ON (" + Tables.PRESENCE + "." + StatusUpdates.DATA_ID + "=" +
"(SELECT MAX(" + StatusUpdates.DATA_ID + ")" +
" FROM " + Tables.PRESENCE +
" WHERE people._id = " + PresenceColumns.RAW_CONTACT_ID + ")" +
" )";
private static final String PHONETIC_NAME_SQL = "trim(trim("
+ "ifnull(name." + StructuredName.PHONETIC_GIVEN_NAME + ",' ')||' '||"
+ "ifnull(name." + StructuredName.PHONETIC_MIDDLE_NAME + ",' '))||' '||"
+ "ifnull(name." + StructuredName.PHONETIC_FAMILY_NAME + ",' ')) ";
private static final String CONTACT_METHOD_KIND_SQL =
"CAST ((CASE WHEN mimetype='" + Email.CONTENT_ITEM_TYPE + "'"
+ " THEN " + android.provider.Contacts.KIND_EMAIL
+ " ELSE"
+ " (CASE WHEN mimetype='" + Im.CONTENT_ITEM_TYPE +"'"
+ " THEN " + android.provider.Contacts.KIND_IM
+ " ELSE"
+ " (CASE WHEN mimetype='" + StructuredPostal.CONTENT_ITEM_TYPE + "'"
+ " THEN " + android.provider.Contacts.KIND_POSTAL
+ " ELSE"
+ " NULL"
+ " END)"
+ " END)"
+ " END) AS INTEGER)";
private static final String IM_PROTOCOL_SQL =
"(CASE WHEN " + StatusUpdates.PROTOCOL + "=" + Im.PROTOCOL_CUSTOM
+ " THEN 'custom:'||" + StatusUpdates.CUSTOM_PROTOCOL
+ " ELSE 'pre:'||" + StatusUpdates.PROTOCOL
+ " END)";
private static String CONTACT_METHOD_DATA_SQL =
"(CASE WHEN " + Data.MIMETYPE + "='" + Im.CONTENT_ITEM_TYPE + "'"
+ " THEN (CASE WHEN " + Tables.DATA + "." + Im.PROTOCOL + "=" + Im.PROTOCOL_CUSTOM
+ " THEN 'custom:'||" + Tables.DATA + "." + Im.CUSTOM_PROTOCOL
+ " ELSE 'pre:'||" + Tables.DATA + "." + Im.PROTOCOL
+ " END)"
+ " ELSE " + Tables.DATA + "." + Email.DATA
+ " END)";
private String[] mSelectionArgs1 = new String[1];
private String[] mSelectionArgs2 = new String[2];
public interface LegacyTables {
public static final String PEOPLE = "view_v1_people";
public static final String PEOPLE_JOIN_PRESENCE = "view_v1_people people " + PRESENCE_JOINS;
public static final String GROUPS = "view_v1_groups";
public static final String ORGANIZATIONS = "view_v1_organizations";
public static final String CONTACT_METHODS = "view_v1_contact_methods";
public static final String PHONES = "view_v1_phones";
public static final String EXTENSIONS = "view_v1_extensions";
public static final String GROUP_MEMBERSHIP = "view_v1_group_membership";
public static final String PHOTOS = "view_v1_photos";
public static final String SETTINGS = "v1_settings";
}
private static final String[] ORGANIZATION_MIME_TYPES = new String[] {
Organization.CONTENT_ITEM_TYPE
};
private static final String[] CONTACT_METHOD_MIME_TYPES = new String[] {
Email.CONTENT_ITEM_TYPE,
Im.CONTENT_ITEM_TYPE,
StructuredPostal.CONTENT_ITEM_TYPE,
};
private static final String[] PHONE_MIME_TYPES = new String[] {
Phone.CONTENT_ITEM_TYPE
};
private static final String[] PHOTO_MIME_TYPES = new String[] {
Photo.CONTENT_ITEM_TYPE
};
private static final String[] GROUP_MEMBERSHIP_MIME_TYPES = new String[] {
GroupMembership.CONTENT_ITEM_TYPE
};
private static final String[] EXTENSION_MIME_TYPES = new String[] {
android.provider.Contacts.Extensions.CONTENT_ITEM_TYPE
};
private interface IdQuery {
String[] COLUMNS = { BaseColumns._ID };
int _ID = 0;
}
/**
* A custom data row that is used to store legacy photo data fields no
* longer directly supported by the API.
*/
private interface LegacyPhotoData {
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/photo_v1_extras";
public static final String PHOTO_DATA_ID = Data.DATA1;
public static final String LOCAL_VERSION = Data.DATA2;
public static final String DOWNLOAD_REQUIRED = Data.DATA3;
public static final String EXISTS_ON_SERVER = Data.DATA4;
public static final String SYNC_ERROR = Data.DATA5;
}
public static final String LEGACY_PHOTO_JOIN =
" LEFT OUTER JOIN data legacy_photo ON (raw_contacts._id = legacy_photo.raw_contact_id"
+ " AND (SELECT mimetype FROM mimetypes WHERE mimetypes._id = legacy_photo.mimetype_id)"
+ "='" + LegacyPhotoData.CONTENT_ITEM_TYPE + "'"
+ " AND " + DataColumns.CONCRETE_ID + " = legacy_photo." + LegacyPhotoData.PHOTO_DATA_ID
+ ")";
private static final ArrayMap<String, String> sPeopleProjectionMap;
private static final ArrayMap<String, String> sOrganizationProjectionMap;
private static final ArrayMap<String, String> sContactMethodProjectionMap;
private static final ArrayMap<String, String> sPhoneProjectionMap;
private static final ArrayMap<String, String> sExtensionProjectionMap;
private static final ArrayMap<String, String> sGroupProjectionMap;
private static final ArrayMap<String, String> sGroupMembershipProjectionMap;
private static final ArrayMap<String, String> sPhotoProjectionMap;
static {
// Contacts URI matching table
UriMatcher matcher = sUriMatcher;
String authority = android.provider.Contacts.AUTHORITY;
matcher.addURI(authority, "extensions", EXTENSIONS);
matcher.addURI(authority, "extensions/#", EXTENSIONS_ID);
matcher.addURI(authority, "groups", GROUPS);
matcher.addURI(authority, "groups/#", GROUPS_ID);
matcher.addURI(authority, "groups/name/*/members", GROUP_NAME_MEMBERS);
// matcher.addURI(authority, "groups/name/*/members/filter/*",
// GROUP_NAME_MEMBERS_FILTER);
matcher.addURI(authority, "groups/system_id/*/members", GROUP_SYSTEM_ID_MEMBERS);
// matcher.addURI(authority, "groups/system_id/*/members/filter/*",
// GROUP_SYSTEM_ID_MEMBERS_FILTER);
matcher.addURI(authority, "groupmembership", GROUPMEMBERSHIP);
matcher.addURI(authority, "groupmembership/#", GROUPMEMBERSHIP_ID);
// matcher.addURI(authority, "groupmembershipraw", GROUPMEMBERSHIP_RAW);
matcher.addURI(authority, "people", PEOPLE);
// matcher.addURI(authority, "people/strequent", PEOPLE_STREQUENT);
// matcher.addURI(authority, "people/strequent/filter/*", PEOPLE_STREQUENT_FILTER);
matcher.addURI(authority, "people/filter/*", PEOPLE_FILTER);
// matcher.addURI(authority, "people/with_phones_filter/*",
// PEOPLE_WITH_PHONES_FILTER);
// matcher.addURI(authority, "people/with_email_or_im_filter/*",
// PEOPLE_WITH_EMAIL_OR_IM_FILTER);
matcher.addURI(authority, "people/#", PEOPLE_ID);
matcher.addURI(authority, "people/#/extensions", PEOPLE_EXTENSIONS);
matcher.addURI(authority, "people/#/extensions/#", PEOPLE_EXTENSIONS_ID);
matcher.addURI(authority, "people/#/phones", PEOPLE_PHONES);
matcher.addURI(authority, "people/#/phones/#", PEOPLE_PHONES_ID);
// matcher.addURI(authority, "people/#/phones_with_presence",
// PEOPLE_PHONES_WITH_PRESENCE);
matcher.addURI(authority, "people/#/photo", PEOPLE_PHOTO);
// matcher.addURI(authority, "people/#/photo/data", PEOPLE_PHOTO_DATA);
matcher.addURI(authority, "people/#/contact_methods", PEOPLE_CONTACTMETHODS);
// matcher.addURI(authority, "people/#/contact_methods_with_presence",
// PEOPLE_CONTACTMETHODS_WITH_PRESENCE);
matcher.addURI(authority, "people/#/contact_methods/#", PEOPLE_CONTACTMETHODS_ID);
matcher.addURI(authority, "people/#/organizations", PEOPLE_ORGANIZATIONS);
matcher.addURI(authority, "people/#/organizations/#", PEOPLE_ORGANIZATIONS_ID);
matcher.addURI(authority, "people/#/groupmembership", PEOPLE_GROUPMEMBERSHIP);
matcher.addURI(authority, "people/#/groupmembership/#", PEOPLE_GROUPMEMBERSHIP_ID);
// matcher.addURI(authority, "people/raw", PEOPLE_RAW);
// matcher.addURI(authority, "people/owner", PEOPLE_OWNER);
matcher.addURI(authority, "people/#/update_contact_time",
PEOPLE_UPDATE_CONTACT_TIME);
matcher.addURI(authority, "deleted_people", DELETED_PEOPLE);
matcher.addURI(authority, "deleted_groups", DELETED_GROUPS);
matcher.addURI(authority, "phones", PHONES);
// matcher.addURI(authority, "phones_with_presence", PHONES_WITH_PRESENCE);
matcher.addURI(authority, "phones/filter/*", PHONES_FILTER);
// matcher.addURI(authority, "phones/filter_name/*", PHONES_FILTER_NAME);
// matcher.addURI(authority, "phones/mobile_filter_name/*",
// PHONES_MOBILE_FILTER_NAME);
matcher.addURI(authority, "phones/#", PHONES_ID);
matcher.addURI(authority, "photos", PHOTOS);
matcher.addURI(authority, "photos/#", PHOTOS_ID);
matcher.addURI(authority, "contact_methods", CONTACTMETHODS);
matcher.addURI(authority, "contact_methods/email", CONTACTMETHODS_EMAIL);
// matcher.addURI(authority, "contact_methods/email/*", CONTACTMETHODS_EMAIL_FILTER);
matcher.addURI(authority, "contact_methods/#", CONTACTMETHODS_ID);
// matcher.addURI(authority, "contact_methods/with_presence",
// CONTACTMETHODS_WITH_PRESENCE);
matcher.addURI(authority, "organizations", ORGANIZATIONS);
matcher.addURI(authority, "organizations/#", ORGANIZATIONS_ID);
// matcher.addURI(authority, "voice_dialer_timestamp", VOICE_DIALER_TIMESTAMP);
matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_QUERY,
SEARCH_SUGGESTIONS);
matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_QUERY + "/*",
SEARCH_SUGGESTIONS);
matcher.addURI(authority, SearchManager.SUGGEST_URI_PATH_SHORTCUT + "/*",
SEARCH_SHORTCUT);
matcher.addURI(authority, "settings", SETTINGS);
ArrayMap<String, String> peopleProjectionMap = new ArrayMap<>();
peopleProjectionMap.put(People.NAME, People.NAME);
peopleProjectionMap.put(People.DISPLAY_NAME, People.DISPLAY_NAME);
peopleProjectionMap.put(People.PHONETIC_NAME, People.PHONETIC_NAME);
peopleProjectionMap.put(People.NOTES, People.NOTES);
peopleProjectionMap.put(People.TIMES_CONTACTED, People.TIMES_CONTACTED);
peopleProjectionMap.put(People.LAST_TIME_CONTACTED, People.LAST_TIME_CONTACTED);
peopleProjectionMap.put(People.CUSTOM_RINGTONE, People.CUSTOM_RINGTONE);
peopleProjectionMap.put(People.SEND_TO_VOICEMAIL, People.SEND_TO_VOICEMAIL);
peopleProjectionMap.put(People.STARRED, People.STARRED);
peopleProjectionMap.put(People.PRIMARY_ORGANIZATION_ID, People.PRIMARY_ORGANIZATION_ID);
peopleProjectionMap.put(People.PRIMARY_EMAIL_ID, People.PRIMARY_EMAIL_ID);
peopleProjectionMap.put(People.PRIMARY_PHONE_ID, People.PRIMARY_PHONE_ID);
sPeopleProjectionMap = new ArrayMap<>(peopleProjectionMap);
sPeopleProjectionMap.put(People._ID, People._ID);
sPeopleProjectionMap.put(People.NUMBER, People.NUMBER);
sPeopleProjectionMap.put(People.TYPE, People.TYPE);
sPeopleProjectionMap.put(People.LABEL, People.LABEL);
sPeopleProjectionMap.put(People.NUMBER_KEY, People.NUMBER_KEY);
sPeopleProjectionMap.put(People.IM_PROTOCOL, IM_PROTOCOL_SQL + " AS " + People.IM_PROTOCOL);
sPeopleProjectionMap.put(People.IM_HANDLE, People.IM_HANDLE);
sPeopleProjectionMap.put(People.IM_ACCOUNT, People.IM_ACCOUNT);
sPeopleProjectionMap.put(People.PRESENCE_STATUS, People.PRESENCE_STATUS);
sPeopleProjectionMap.put(People.PRESENCE_CUSTOM_STATUS,
"(SELECT " + StatusUpdates.STATUS +
" FROM " + Tables.STATUS_UPDATES +
" JOIN " + Tables.DATA +
" ON(" + StatusUpdatesColumns.DATA_ID + "=" + DataColumns.CONCRETE_ID + ")" +
" WHERE " + DataColumns.CONCRETE_RAW_CONTACT_ID + "=people." + People._ID +
" ORDER BY " + StatusUpdates.STATUS_TIMESTAMP + " DESC " +
" LIMIT 1" +
") AS " + People.PRESENCE_CUSTOM_STATUS);
sOrganizationProjectionMap = new ArrayMap<>();
sOrganizationProjectionMap.put(android.provider.Contacts.Organizations._ID,
android.provider.Contacts.Organizations._ID);
sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.PERSON_ID,
android.provider.Contacts.Organizations.PERSON_ID);
sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.ISPRIMARY,
android.provider.Contacts.Organizations.ISPRIMARY);
sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.COMPANY,
android.provider.Contacts.Organizations.COMPANY);
sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.TYPE,
android.provider.Contacts.Organizations.TYPE);
sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.LABEL,
android.provider.Contacts.Organizations.LABEL);
sOrganizationProjectionMap.put(android.provider.Contacts.Organizations.TITLE,
android.provider.Contacts.Organizations.TITLE);
sContactMethodProjectionMap = new ArrayMap<>(peopleProjectionMap);
sContactMethodProjectionMap.put(ContactMethods._ID, ContactMethods._ID);
sContactMethodProjectionMap.put(ContactMethods.PERSON_ID, ContactMethods.PERSON_ID);
sContactMethodProjectionMap.put(ContactMethods.KIND, ContactMethods.KIND);
sContactMethodProjectionMap.put(ContactMethods.ISPRIMARY, ContactMethods.ISPRIMARY);
sContactMethodProjectionMap.put(ContactMethods.TYPE, ContactMethods.TYPE);
sContactMethodProjectionMap.put(ContactMethods.DATA, ContactMethods.DATA);
sContactMethodProjectionMap.put(ContactMethods.LABEL, ContactMethods.LABEL);
sContactMethodProjectionMap.put(ContactMethods.AUX_DATA, ContactMethods.AUX_DATA);
sPhoneProjectionMap = new ArrayMap<>(peopleProjectionMap);
sPhoneProjectionMap.put(android.provider.Contacts.Phones._ID,
android.provider.Contacts.Phones._ID);
sPhoneProjectionMap.put(android.provider.Contacts.Phones.PERSON_ID,
android.provider.Contacts.Phones.PERSON_ID);
sPhoneProjectionMap.put(android.provider.Contacts.Phones.ISPRIMARY,
android.provider.Contacts.Phones.ISPRIMARY);
sPhoneProjectionMap.put(android.provider.Contacts.Phones.NUMBER,
android.provider.Contacts.Phones.NUMBER);
sPhoneProjectionMap.put(android.provider.Contacts.Phones.TYPE,
android.provider.Contacts.Phones.TYPE);
sPhoneProjectionMap.put(android.provider.Contacts.Phones.LABEL,
android.provider.Contacts.Phones.LABEL);
sPhoneProjectionMap.put(android.provider.Contacts.Phones.NUMBER_KEY,
android.provider.Contacts.Phones.NUMBER_KEY);
sExtensionProjectionMap = new ArrayMap<>();
sExtensionProjectionMap.put(android.provider.Contacts.Extensions._ID,
android.provider.Contacts.Extensions._ID);
sExtensionProjectionMap.put(android.provider.Contacts.Extensions.PERSON_ID,
android.provider.Contacts.Extensions.PERSON_ID);
sExtensionProjectionMap.put(android.provider.Contacts.Extensions.NAME,
android.provider.Contacts.Extensions.NAME);
sExtensionProjectionMap.put(android.provider.Contacts.Extensions.VALUE,
android.provider.Contacts.Extensions.VALUE);
sGroupProjectionMap = new ArrayMap<>();
sGroupProjectionMap.put(android.provider.Contacts.Groups._ID,
android.provider.Contacts.Groups._ID);
sGroupProjectionMap.put(android.provider.Contacts.Groups.NAME,
android.provider.Contacts.Groups.NAME);
sGroupProjectionMap.put(android.provider.Contacts.Groups.NOTES,
android.provider.Contacts.Groups.NOTES);
sGroupProjectionMap.put(android.provider.Contacts.Groups.SYSTEM_ID,
android.provider.Contacts.Groups.SYSTEM_ID);
sGroupMembershipProjectionMap = new ArrayMap<>(sGroupProjectionMap);
sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership._ID,
android.provider.Contacts.GroupMembership._ID);
sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership.PERSON_ID,
android.provider.Contacts.GroupMembership.PERSON_ID);
sGroupMembershipProjectionMap.put(android.provider.Contacts.GroupMembership.GROUP_ID,
android.provider.Contacts.GroupMembership.GROUP_ID);
sGroupMembershipProjectionMap.put(
android.provider.Contacts.GroupMembership.GROUP_SYNC_ID,
android.provider.Contacts.GroupMembership.GROUP_SYNC_ID);
sGroupMembershipProjectionMap.put(
android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT,
android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT);
sGroupMembershipProjectionMap.put(
android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT_TYPE,
android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT_TYPE);
sPhotoProjectionMap = new ArrayMap<>();
sPhotoProjectionMap.put(android.provider.Contacts.Photos._ID,
android.provider.Contacts.Photos._ID);
sPhotoProjectionMap.put(android.provider.Contacts.Photos.PERSON_ID,
android.provider.Contacts.Photos.PERSON_ID);
sPhotoProjectionMap.put(android.provider.Contacts.Photos.DATA,
android.provider.Contacts.Photos.DATA);
sPhotoProjectionMap.put(android.provider.Contacts.Photos.LOCAL_VERSION,
android.provider.Contacts.Photos.LOCAL_VERSION);
sPhotoProjectionMap.put(android.provider.Contacts.Photos.DOWNLOAD_REQUIRED,
android.provider.Contacts.Photos.DOWNLOAD_REQUIRED);
sPhotoProjectionMap.put(android.provider.Contacts.Photos.EXISTS_ON_SERVER,
android.provider.Contacts.Photos.EXISTS_ON_SERVER);
sPhotoProjectionMap.put(android.provider.Contacts.Photos.SYNC_ERROR,
android.provider.Contacts.Photos.SYNC_ERROR);
}
private final Context mContext;
private final ContactsDatabaseHelper mDbHelper;
private final ContactsProvider2 mContactsProvider;
private final NameSplitter mPhoneticNameSplitter;
private final GlobalSearchSupport mGlobalSearchSupport;
private final SQLiteStatement mDataMimetypeQuery;
private final SQLiteStatement mDataRawContactIdQuery;
private final ContentValues mValues = new ContentValues();
private final ContentValues mValues2 = new ContentValues();
private final ContentValues mValues3 = new ContentValues();
private boolean mDefaultAccountKnown;
private Account mAccount;
private final long mMimetypeEmail;
private final long mMimetypeIm;
private final long mMimetypePostal;
public LegacyApiSupport(Context context, ContactsDatabaseHelper contactsDatabaseHelper,
ContactsProvider2 contactsProvider, GlobalSearchSupport globalSearchSupport) {
mContext = context;
mContactsProvider = contactsProvider;
mDbHelper = contactsDatabaseHelper;
mGlobalSearchSupport = globalSearchSupport;
mPhoneticNameSplitter = new NameSplitter("", "", "", context
.getString(com.android.internal.R.string.common_name_conjunctions), Locale
.getDefault());
SQLiteDatabase db = mDbHelper.getReadableDatabase();
mDataMimetypeQuery = db.compileStatement(
"SELECT " + DataColumns.MIMETYPE_ID +
" FROM " + Tables.DATA +
" WHERE " + Data._ID + "=?");
mDataRawContactIdQuery = db.compileStatement(
"SELECT " + Data.RAW_CONTACT_ID +
" FROM " + Tables.DATA +
" WHERE " + Data._ID + "=?");
mMimetypeEmail = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE);
mMimetypeIm = mDbHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE);
mMimetypePostal = mDbHelper.getMimeTypeId(StructuredPostal.CONTENT_ITEM_TYPE);
}
private void ensureDefaultAccount() {
if (!mDefaultAccountKnown) {
mAccount = mContactsProvider.getDefaultAccount();
mDefaultAccountKnown = true;
}
}
public static void createDatabase(SQLiteDatabase db) {
Log.i(TAG, "Bootstrapping database legacy support");
createViews(db);
createSettingsTable(db);
}
public static void createViews(SQLiteDatabase db) {
String peopleColumns = "name." + StructuredName.DISPLAY_NAME
+ " AS " + People.NAME + ", " +
Tables.RAW_CONTACTS + "." + RawContactsColumns.DISPLAY_NAME
+ " AS " + People.DISPLAY_NAME + ", " +
PHONETIC_NAME_SQL
+ " AS " + People.PHONETIC_NAME + " , " +
"note." + Note.NOTE
+ " AS " + People.NOTES + ", " +
AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
// We no longer return even low-res values from CP1.
// Note if we just use the value 0 below, certain seletion wouldn't work.
"cast(0 as int) AS " + People.TIMES_CONTACTED + ", " +
"cast(0 as int) AS " + People.LAST_TIME_CONTACTED + ", " +
Tables.RAW_CONTACTS + "." + RawContacts.CUSTOM_RINGTONE
+ " AS " + People.CUSTOM_RINGTONE + ", " +
Tables.RAW_CONTACTS + "." + RawContacts.SEND_TO_VOICEMAIL
+ " AS " + People.SEND_TO_VOICEMAIL + ", " +
Tables.RAW_CONTACTS + "." + RawContacts.STARRED
+ " AS " + People.STARRED + ", " +
"organization." + Data._ID
+ " AS " + People.PRIMARY_ORGANIZATION_ID + ", " +
"email." + Data._ID
+ " AS " + People.PRIMARY_EMAIL_ID + ", " +
"phone." + Data._ID
+ " AS " + People.PRIMARY_PHONE_ID + ", " +
"phone." + Phone.NUMBER
+ " AS " + People.NUMBER + ", " +
"phone." + Phone.TYPE
+ " AS " + People.TYPE + ", " +
"phone." + Phone.LABEL
+ " AS " + People.LABEL + ", " +
"_PHONE_NUMBER_STRIPPED_REVERSED(phone." + Phone.NUMBER + ")"
+ " AS " + People.NUMBER_KEY;
db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.PEOPLE + ";");
db.execSQL("CREATE VIEW " + LegacyTables.PEOPLE + " AS SELECT " +
RawContactsColumns.CONCRETE_ID
+ " AS " + android.provider.Contacts.People._ID + ", " +
peopleColumns +
" FROM " + Tables.RAW_CONTACTS + PEOPLE_JOINS +
" WHERE " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0;");
db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.ORGANIZATIONS + ";");
db.execSQL("CREATE VIEW " + LegacyTables.ORGANIZATIONS + " AS SELECT " +
DataColumns.CONCRETE_ID
+ " AS " + android.provider.Contacts.Organizations._ID + ", " +
Data.RAW_CONTACT_ID
+ " AS " + android.provider.Contacts.Organizations.PERSON_ID + ", " +
Data.IS_PRIMARY
+ " AS " + android.provider.Contacts.Organizations.ISPRIMARY + ", " +
AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
Organization.COMPANY
+ " AS " + android.provider.Contacts.Organizations.COMPANY + ", " +
Organization.TYPE
+ " AS " + android.provider.Contacts.Organizations.TYPE + ", " +
Organization.LABEL
+ " AS " + android.provider.Contacts.Organizations.LABEL + ", " +
Organization.TITLE
+ " AS " + android.provider.Contacts.Organizations.TITLE +
" FROM " + Tables.DATA_JOIN_MIMETYPE_RAW_CONTACTS +
" WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
+ Organization.CONTENT_ITEM_TYPE + "'"
+ " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
";");
db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.CONTACT_METHODS + ";");
db.execSQL("CREATE VIEW " + LegacyTables.CONTACT_METHODS + " AS SELECT " +
DataColumns.CONCRETE_ID
+ " AS " + ContactMethods._ID + ", " +
DataColumns.CONCRETE_RAW_CONTACT_ID
+ " AS " + ContactMethods.PERSON_ID + ", " +
CONTACT_METHOD_KIND_SQL
+ " AS " + ContactMethods.KIND + ", " +
DataColumns.CONCRETE_IS_PRIMARY
+ " AS " + ContactMethods.ISPRIMARY + ", " +
Tables.DATA + "." + Email.TYPE
+ " AS " + ContactMethods.TYPE + ", " +
CONTACT_METHOD_DATA_SQL
+ " AS " + ContactMethods.DATA + ", " +
Tables.DATA + "." + Email.LABEL
+ " AS " + ContactMethods.LABEL + ", " +
DataColumns.CONCRETE_DATA14
+ " AS " + ContactMethods.AUX_DATA + ", " +
peopleColumns +
" FROM " + Tables.DATA + DATA_JOINS +
" WHERE " + ContactMethods.KIND + " IS NOT NULL"
+ " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
";");
db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.PHONES + ";");
db.execSQL("CREATE VIEW " + LegacyTables.PHONES + " AS SELECT DISTINCT " +
DataColumns.CONCRETE_ID
+ " AS " + android.provider.Contacts.Phones._ID + ", " +
DataColumns.CONCRETE_RAW_CONTACT_ID
+ " AS " + android.provider.Contacts.Phones.PERSON_ID + ", " +
DataColumns.CONCRETE_IS_PRIMARY
+ " AS " + android.provider.Contacts.Phones.ISPRIMARY + ", " +
Tables.DATA + "." + Phone.NUMBER
+ " AS " + android.provider.Contacts.Phones.NUMBER + ", " +
Tables.DATA + "." + Phone.TYPE
+ " AS " + android.provider.Contacts.Phones.TYPE + ", " +
Tables.DATA + "." + Phone.LABEL
+ " AS " + android.provider.Contacts.Phones.LABEL + ", " +
"_PHONE_NUMBER_STRIPPED_REVERSED(" + Tables.DATA + "." + Phone.NUMBER + ")"
+ " AS " + android.provider.Contacts.Phones.NUMBER_KEY + ", " +
peopleColumns +
" FROM " + Tables.DATA
+ " JOIN " + Tables.PHONE_LOOKUP
+ " ON (" + Tables.DATA + "._id = "
+ Tables.PHONE_LOOKUP + "." + PhoneLookupColumns.DATA_ID + ")"
+ DATA_JOINS +
" WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
+ Phone.CONTENT_ITEM_TYPE + "'"
+ " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
";");
db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.EXTENSIONS + ";");
db.execSQL("CREATE VIEW " + LegacyTables.EXTENSIONS + " AS SELECT " +
DataColumns.CONCRETE_ID
+ " AS " + android.provider.Contacts.Extensions._ID + ", " +
DataColumns.CONCRETE_RAW_CONTACT_ID
+ " AS " + android.provider.Contacts.Extensions.PERSON_ID + ", " +
AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
ExtensionsColumns.NAME
+ " AS " + android.provider.Contacts.Extensions.NAME + ", " +
ExtensionsColumns.VALUE
+ " AS " + android.provider.Contacts.Extensions.VALUE +
" FROM " + Tables.DATA_JOIN_MIMETYPE_RAW_CONTACTS +
" WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
+ android.provider.Contacts.Extensions.CONTENT_ITEM_TYPE + "'"
+ " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
";");
db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.GROUPS + ";");
db.execSQL("CREATE VIEW " + LegacyTables.GROUPS + " AS SELECT " +
GroupsColumns.CONCRETE_ID + " AS " + android.provider.Contacts.Groups._ID + ", " +
AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
Groups.TITLE + " AS " + android.provider.Contacts.Groups.NAME + ", " +
Groups.NOTES + " AS " + android.provider.Contacts.Groups.NOTES + " , " +
Groups.SYSTEM_ID + " AS " + android.provider.Contacts.Groups.SYSTEM_ID +
" FROM " + Tables.GROUPS +
" JOIN " + Tables.ACCOUNTS + " ON (" +
GroupsColumns.CONCRETE_ACCOUNT_ID + "=" + AccountsColumns.CONCRETE_ID + ")" +
";");
db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.GROUP_MEMBERSHIP + ";");
db.execSQL("CREATE VIEW " + LegacyTables.GROUP_MEMBERSHIP + " AS SELECT " +
DataColumns.CONCRETE_ID
+ " AS " + android.provider.Contacts.GroupMembership._ID + ", " +
DataColumns.CONCRETE_RAW_CONTACT_ID
+ " AS " + android.provider.Contacts.GroupMembership.PERSON_ID + ", " +
AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
GroupMembership.GROUP_ROW_ID
+ " AS " + android.provider.Contacts.GroupMembership.GROUP_ID + ", " +
Groups.TITLE
+ " AS " + android.provider.Contacts.GroupMembership.NAME + ", " +
Groups.NOTES
+ " AS " + android.provider.Contacts.GroupMembership.NOTES + ", " +
Groups.SYSTEM_ID
+ " AS " + android.provider.Contacts.GroupMembership.SYSTEM_ID + ", " +
GroupsColumns.CONCRETE_SOURCE_ID
+ " AS "
+ android.provider.Contacts.GroupMembership.GROUP_SYNC_ID + ", " +
AccountsColumns.CONCRETE_ACCOUNT_NAME
+ " AS "
+ android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT + ", " +
AccountsColumns.CONCRETE_ACCOUNT_TYPE
+ " AS "
+ android.provider.Contacts.GroupMembership.GROUP_SYNC_ACCOUNT_TYPE +
" FROM " + Tables.DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_GROUPS +
" WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
+ GroupMembership.CONTENT_ITEM_TYPE + "'"
+ " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
";");
db.execSQL("DROP VIEW IF EXISTS " + LegacyTables.PHOTOS + ";");
db.execSQL("CREATE VIEW " + LegacyTables.PHOTOS + " AS SELECT " +
DataColumns.CONCRETE_ID
+ " AS " + android.provider.Contacts.Photos._ID + ", " +
DataColumns.CONCRETE_RAW_CONTACT_ID
+ " AS " + android.provider.Contacts.Photos.PERSON_ID + ", " +
AccountsColumns.CONCRETE_ACCOUNT_NAME + ", " +
AccountsColumns.CONCRETE_ACCOUNT_TYPE + ", " +
Tables.DATA + "." + Photo.PHOTO
+ " AS " + android.provider.Contacts.Photos.DATA + ", " +
"legacy_photo." + LegacyPhotoData.EXISTS_ON_SERVER
+ " AS " + android.provider.Contacts.Photos.EXISTS_ON_SERVER + ", " +
"legacy_photo." + LegacyPhotoData.DOWNLOAD_REQUIRED
+ " AS " + android.provider.Contacts.Photos.DOWNLOAD_REQUIRED + ", " +
"legacy_photo." + LegacyPhotoData.LOCAL_VERSION
+ " AS " + android.provider.Contacts.Photos.LOCAL_VERSION + ", " +
"legacy_photo." + LegacyPhotoData.SYNC_ERROR
+ " AS " + android.provider.Contacts.Photos.SYNC_ERROR +
" FROM " + Tables.DATA + DATA_JOINS + LEGACY_PHOTO_JOIN +
" WHERE " + MimetypesColumns.CONCRETE_MIMETYPE + "='"
+ Photo.CONTENT_ITEM_TYPE + "'"
+ " AND " + Tables.RAW_CONTACTS + "." + RawContacts.DELETED + "=0" +
";");
}
public static void createSettingsTable(SQLiteDatabase db) {
db.execSQL("DROP TABLE IF EXISTS " + LegacyTables.SETTINGS + ";");
db.execSQL("CREATE TABLE " + LegacyTables.SETTINGS + " (" +
android.provider.Contacts.Settings._ID + " INTEGER PRIMARY KEY," +
android.provider.Contacts.Settings._SYNC_ACCOUNT + " TEXT," +
android.provider.Contacts.Settings._SYNC_ACCOUNT_TYPE + " TEXT," +
android.provider.Contacts.Settings.KEY + " STRING NOT NULL," +
android.provider.Contacts.Settings.VALUE + " STRING " +
");");
}
public Uri insert(Uri uri, ContentValues values) {
ensureDefaultAccount();
final int match = sUriMatcher.match(uri);
long id = 0;
switch (match) {
case PEOPLE:
id = insertPeople(values);
break;
case ORGANIZATIONS:
id = insertOrganization(values);
break;
case PEOPLE_CONTACTMETHODS: {
long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
id = insertContactMethod(rawContactId, values);
break;
}
case CONTACTMETHODS: {
long rawContactId = getRequiredValue(values, ContactMethods.PERSON_ID);
id = insertContactMethod(rawContactId, values);
break;
}
case PHONES: {
long rawContactId = getRequiredValue(values,
android.provider.Contacts.Phones.PERSON_ID);
id = insertPhone(rawContactId, values);
break;
}
case PEOPLE_PHONES: {
long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
id = insertPhone(rawContactId, values);
break;
}
case EXTENSIONS: {
long rawContactId = getRequiredValue(values,
android.provider.Contacts.Extensions.PERSON_ID);
id = insertExtension(rawContactId, values);
break;
}
case GROUPS:
id = insertGroup(values);
break;
case GROUPMEMBERSHIP: {
long rawContactId = getRequiredValue(values,
android.provider.Contacts.GroupMembership.PERSON_ID);
long groupId = getRequiredValue(values,
android.provider.Contacts.GroupMembership.GROUP_ID);
id = insertGroupMembership(rawContactId, groupId);
break;
}
default:
throw new UnsupportedOperationException(mDbHelper.exceptionMessage(uri));
}
if (id < 0) {
return null;
}
final Uri result = ContentUris.withAppendedId(uri, id);
onChange(result);
return result;
}
private long getRequiredValue(ContentValues values, String column) {
final Long value = values.getAsLong(column);
if (value == null) {
throw new RuntimeException("Required value: " + column);
}
return value;
}
private long insertPeople(ContentValues values) {
parsePeopleValues(values);
Uri contactUri = mContactsProvider.insertInTransaction(RawContacts.CONTENT_URI, mValues);
long rawContactId = ContentUris.parseId(contactUri);
if (mValues2.size() != 0) {
mValues2.put(Data.RAW_CONTACT_ID, rawContactId);
mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues2);
}
if (mValues3.size() != 0) {
mValues3.put(Data.RAW_CONTACT_ID, rawContactId);
mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues3);
}
return rawContactId;
}
private long insertOrganization(ContentValues values) {
parseOrganizationValues(values);
ContactsDatabaseHelper.copyLongValue(mValues, Data.RAW_CONTACT_ID,
values, android.provider.Contacts.Organizations.PERSON_ID);
Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
return ContentUris.parseId(uri);
}
private long insertPhone(long rawContactId, ContentValues values) {
parsePhoneValues(values);
mValues.put(Data.RAW_CONTACT_ID, rawContactId);
Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
return ContentUris.parseId(uri);
}
private long insertContactMethod(long rawContactId, ContentValues values) {
Integer kind = values.getAsInteger(ContactMethods.KIND);
if (kind == null) {
throw new RuntimeException("Required value: " + ContactMethods.KIND);
}
parseContactMethodValues(kind, values);
mValues.put(Data.RAW_CONTACT_ID, rawContactId);
Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
return ContentUris.parseId(uri);
}
private long insertExtension(long rawContactId, ContentValues values) {
mValues.clear();
mValues.put(Data.RAW_CONTACT_ID, rawContactId);
mValues.put(Data.MIMETYPE, android.provider.Contacts.Extensions.CONTENT_ITEM_TYPE);
parseExtensionValues(values);
Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
return ContentUris.parseId(uri);
}
private long insertGroup(ContentValues values) {
parseGroupValues(values);
if (mAccount != null) {
mValues.put(Groups.ACCOUNT_NAME, mAccount.name);
mValues.put(Groups.ACCOUNT_TYPE, mAccount.type);
}
Uri uri = mContactsProvider.insertInTransaction(Groups.CONTENT_URI, mValues);
return ContentUris.parseId(uri);
}
private long insertGroupMembership(long rawContactId, long groupId) {
mValues.clear();
mValues.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
mValues.put(GroupMembership.RAW_CONTACT_ID, rawContactId);
mValues.put(GroupMembership.GROUP_ROW_ID, groupId);
Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
return ContentUris.parseId(uri);
}
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
ensureDefaultAccount();
int match = sUriMatcher.match(uri);
int count = 0;
switch(match) {
case PEOPLE_UPDATE_CONTACT_TIME: {
count = 0; // No longer supported.
break;
}
case PEOPLE_PHOTO: {
long rawContactId = Long.parseLong(uri.getPathSegments().get(1));
return updatePhoto(rawContactId, values);
}
case SETTINGS: {
return updateSettings(values);
}
case GROUPMEMBERSHIP:
case GROUPMEMBERSHIP_ID:
case -1: {
throw new UnsupportedOperationException(mDbHelper.exceptionMessage(uri));
}
default: {
count = updateAll(uri, match, values, selection, selectionArgs);
}
}
if (count > 0) {
mContext.getContentResolver().notifyChange(uri, null);
}
return count;
}
private int updateAll(Uri uri, final int match, ContentValues values, String selection,
String[] selectionArgs) {
Cursor c = query(uri, IdQuery.COLUMNS, selection, selectionArgs, null, null);
if (c == null) {
return 0;
}
int count = 0;
try {
while (c.moveToNext()) {
long id = c.getLong(IdQuery._ID);
count += update(match, id, values);
}
} finally {
c.close();
}
return count;
}
public int update(int match, long id, ContentValues values) {
int count = 0;
switch(match) {
case PEOPLE:
case PEOPLE_ID: {
count = updatePeople(id, values);
break;
}
case ORGANIZATIONS:
case ORGANIZATIONS_ID: {
count = updateOrganizations(id, values);
break;
}
case PHONES:
case PHONES_ID: {
count = updatePhones(id, values);
break;
}
case CONTACTMETHODS:
case CONTACTMETHODS_ID: {
count = updateContactMethods(id, values);
break;
}
case EXTENSIONS:
case EXTENSIONS_ID: {
count = updateExtensions(id, values);
break;
}
case GROUPS:
case GROUPS_ID: {
count = updateGroups(id, values);
break;
}
case PHOTOS:
case PHOTOS_ID:
count = updatePhotoByDataId(id, values);
break;
}
return count;
}
private int updatePeople(long rawContactId, ContentValues values) {
parsePeopleValues(values);
int count = mContactsProvider.updateInTransaction(RawContacts.CONTENT_URI,
mValues, RawContacts._ID + "=" + rawContactId, null);
if (count == 0) {
return 0;
}
if (mValues2.size() != 0) {
Uri dataUri = findFirstDataRow(rawContactId, StructuredName.CONTENT_ITEM_TYPE);
if (dataUri != null) {
mContactsProvider.updateInTransaction(dataUri, mValues2, null, null);
} else {
mValues2.put(Data.RAW_CONTACT_ID, rawContactId);
mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues2);
}
}
if (mValues3.size() != 0) {
Uri dataUri = findFirstDataRow(rawContactId, Note.CONTENT_ITEM_TYPE);
if (dataUri != null) {
mContactsProvider.updateInTransaction(dataUri, mValues3, null, null);
} else {
mValues3.put(Data.RAW_CONTACT_ID, rawContactId);
mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues3);
}
}
return count;
}
private int updateOrganizations(long dataId, ContentValues values) {
parseOrganizationValues(values);
return mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
Data._ID + "=" + dataId, null);
}
private int updatePhones(long dataId, ContentValues values) {
parsePhoneValues(values);
return mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
Data._ID + "=" + dataId, null);
}
private int updateContactMethods(long dataId, ContentValues values) {
int kind;
mDataMimetypeQuery.bindLong(1, dataId);
long mimetype_id;
try {
mimetype_id = mDataMimetypeQuery.simpleQueryForLong();
} catch (SQLiteDoneException e) {
// Data row not found
return 0;
}
if (mimetype_id == mMimetypeEmail) {
kind = android.provider.Contacts.KIND_EMAIL;
} else if (mimetype_id == mMimetypeIm) {
kind = android.provider.Contacts.KIND_IM;
} else if (mimetype_id == mMimetypePostal) {
kind = android.provider.Contacts.KIND_POSTAL;
} else {
// Non-legacy kind: return "Not found"
return 0;
}
parseContactMethodValues(kind, values);
return mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
Data._ID + "=" + dataId, null);
}
private int updateExtensions(long dataId, ContentValues values) {
parseExtensionValues(values);
return mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
Data._ID + "=" + dataId, null);
}
private int updateGroups(long groupId, ContentValues values) {
parseGroupValues(values);
return mContactsProvider.updateInTransaction(Groups.CONTENT_URI, mValues,
Groups._ID + "=" + groupId, null);
}
private int updatePhoto(long rawContactId, ContentValues values) {
// TODO check sanctions
int count;
long dataId = findFirstDataId(rawContactId, Photo.CONTENT_ITEM_TYPE);
mValues.clear();
byte[] bytes = values.getAsByteArray(android.provider.Contacts.Photos.DATA);
mValues.put(Photo.PHOTO, bytes);
if (dataId == -1) {
mValues.put(Data.MIMETYPE, Photo.CONTENT_ITEM_TYPE);
mValues.put(Data.RAW_CONTACT_ID, rawContactId);
Uri dataUri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
dataId = ContentUris.parseId(dataUri);
count = 1;
} else {
Uri dataUri = ContentUris.withAppendedId(Data.CONTENT_URI, dataId);
count = mContactsProvider.updateInTransaction(dataUri, mValues, null, null);
}
updateLegacyPhotoData(rawContactId, dataId, values);
return count;
}
private int updatePhotoByDataId(long dataId, ContentValues values) {
mDataRawContactIdQuery.bindLong(1, dataId);
long rawContactId;
try {
rawContactId = mDataRawContactIdQuery.simpleQueryForLong();
} catch (SQLiteDoneException e) {
// Data row not found
return 0;
}
if (values.containsKey(android.provider.Contacts.Photos.DATA)) {
byte[] bytes = values.getAsByteArray(android.provider.Contacts.Photos.DATA);
mValues.clear();
mValues.put(Photo.PHOTO, bytes);
mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
Data._ID + "=" + dataId, null);
}
updateLegacyPhotoData(rawContactId, dataId, values);
return 1;
}
private void updateLegacyPhotoData(long rawContactId, long dataId, ContentValues values) {
mValues.clear();
ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.LOCAL_VERSION,
values, android.provider.Contacts.Photos.LOCAL_VERSION);
ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.DOWNLOAD_REQUIRED,
values, android.provider.Contacts.Photos.DOWNLOAD_REQUIRED);
ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.EXISTS_ON_SERVER,
values, android.provider.Contacts.Photos.EXISTS_ON_SERVER);
ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.SYNC_ERROR,
values, android.provider.Contacts.Photos.SYNC_ERROR);
int updated = mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues,
Data.MIMETYPE + "='" + LegacyPhotoData.CONTENT_ITEM_TYPE + "'"
+ " AND " + Data.RAW_CONTACT_ID + "=" + rawContactId
+ " AND " + LegacyPhotoData.PHOTO_DATA_ID + "=" + dataId, null);
if (updated == 0) {
mValues.put(Data.RAW_CONTACT_ID, rawContactId);
mValues.put(Data.MIMETYPE, LegacyPhotoData.CONTENT_ITEM_TYPE);
mValues.put(LegacyPhotoData.PHOTO_DATA_ID, dataId);
mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues);
}
}
private int updateSettings(ContentValues values) {
SQLiteDatabase db = mDbHelper.getWritableDatabase();
String accountName = values.getAsString(android.provider.Contacts.Settings._SYNC_ACCOUNT);
String accountType =
values.getAsString(android.provider.Contacts.Settings._SYNC_ACCOUNT_TYPE);
String key = values.getAsString(android.provider.Contacts.Settings.KEY);
if (key == null) {
throw new IllegalArgumentException("you must specify the key when updating settings");
}
updateSetting(db, accountName, accountType, values);
if (key.equals(android.provider.Contacts.Settings.SYNC_EVERYTHING)) {
mValues.clear();
mValues.put(Settings.SHOULD_SYNC,
values.getAsInteger(android.provider.Contacts.Settings.VALUE));
String selection;
String[] selectionArgs;
if (accountName != null && accountType != null) {
selectionArgs = new String[]{accountName, accountType};
selection = Settings.ACCOUNT_NAME + "=?"
+ " AND " + Settings.ACCOUNT_TYPE + "=?"
+ " AND " + Settings.DATA_SET + " IS NULL";
} else {
selectionArgs = null;
selection = Settings.ACCOUNT_NAME + " IS NULL"
+ " AND " + Settings.ACCOUNT_TYPE + " IS NULL"
+ " AND " + Settings.DATA_SET + " IS NULL";
}
int count = mContactsProvider.updateInTransaction(Settings.CONTENT_URI, mValues,
selection, selectionArgs);
if (count == 0) {
mValues.put(Settings.ACCOUNT_NAME, accountName);
mValues.put(Settings.ACCOUNT_TYPE, accountType);
mContactsProvider.insertInTransaction(Settings.CONTENT_URI, mValues);
}
}
return 1;
}
private void updateSetting(SQLiteDatabase db, String accountName, String accountType,
ContentValues values) {
final String key = values.getAsString(android.provider.Contacts.Settings.KEY);
if (accountName == null || accountType == null) {
db.delete(LegacyTables.SETTINGS, "_sync_account IS NULL AND key=?", new String[]{key});
} else {
db.delete(LegacyTables.SETTINGS, "_sync_account=? AND _sync_account_type=? AND key=?",
new String[]{accountName, accountType, key});
}
long rowId = db.insert(LegacyTables.SETTINGS,
android.provider.Contacts.Settings.KEY, values);
if (rowId < 0) {
throw new SQLException("error updating settings with " + values);
}
}
private interface SettingsMatchQuery {
String SQL =
"SELECT "
+ ContactsContract.Settings.ACCOUNT_NAME + ","
+ ContactsContract.Settings.ACCOUNT_TYPE + ","
+ ContactsContract.Settings.SHOULD_SYNC +
" FROM " + Tables.SETTINGS + " LEFT OUTER JOIN " + LegacyTables.SETTINGS +
" ON (" + ContactsContract.Settings.ACCOUNT_NAME + "="
+ android.provider.Contacts.Settings._SYNC_ACCOUNT +
" AND " + ContactsContract.Settings.ACCOUNT_TYPE + "="
+ android.provider.Contacts.Settings._SYNC_ACCOUNT_TYPE +
" AND " + ContactsContract.Settings.DATA_SET + " IS NULL" +
" AND " + android.provider.Contacts.Settings.KEY + "='"
+ android.provider.Contacts.Settings.SYNC_EVERYTHING + "'" +
")" +
" WHERE " + ContactsContract.Settings.SHOULD_SYNC + "<>"
+ android.provider.Contacts.Settings.VALUE;
int ACCOUNT_NAME = 0;
int ACCOUNT_TYPE = 1;
int SHOULD_SYNC = 2;
}
/**
* Brings legacy settings table in sync with the new settings.
*/
public void copySettingsToLegacySettings() {
SQLiteDatabase db = mDbHelper.getWritableDatabase();
Cursor cursor = db.rawQuery(SettingsMatchQuery.SQL, null);
try {
while(cursor.moveToNext()) {
String accountName = cursor.getString(SettingsMatchQuery.ACCOUNT_NAME);
String accountType = cursor.getString(SettingsMatchQuery.ACCOUNT_TYPE);
String value = cursor.getString(SettingsMatchQuery.SHOULD_SYNC);
mValues.clear();
mValues.put(android.provider.Contacts.Settings._SYNC_ACCOUNT, accountName);
mValues.put(android.provider.Contacts.Settings._SYNC_ACCOUNT_TYPE, accountType);
mValues.put(android.provider.Contacts.Settings.KEY,
android.provider.Contacts.Settings.SYNC_EVERYTHING);
mValues.put(android.provider.Contacts.Settings.VALUE, value);
updateSetting(db, accountName, accountType, mValues);
}
} finally {
cursor.close();
}
}
private void parsePeopleValues(ContentValues values) {
mValues.clear();
mValues2.clear();
mValues3.clear();
ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE,
values, People.CUSTOM_RINGTONE);
ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL,
values, People.SEND_TO_VOICEMAIL);
// We no longer support the following fields in CP1.
// ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED,
// values, People.LAST_TIME_CONTACTED);
// ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED,
// values, People.TIMES_CONTACTED);
ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED,
values, People.STARRED);
if (mAccount != null) {
mValues.put(RawContacts.ACCOUNT_NAME, mAccount.name);
mValues.put(RawContacts.ACCOUNT_TYPE, mAccount.type);
}
if (values.containsKey(People.NAME) || values.containsKey(People.PHONETIC_NAME)) {
mValues2.put(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
ContactsDatabaseHelper.copyStringValue(mValues2, StructuredName.DISPLAY_NAME,
values, People.NAME);
if (values.containsKey(People.PHONETIC_NAME)) {
String phoneticName = values.getAsString(People.PHONETIC_NAME);
NameSplitter.Name parsedName = new NameSplitter.Name();
mPhoneticNameSplitter.split(parsedName, phoneticName);
mValues2.put(StructuredName.PHONETIC_GIVEN_NAME, parsedName.getGivenNames());
mValues2.put(StructuredName.PHONETIC_MIDDLE_NAME, parsedName.getMiddleName());
mValues2.put(StructuredName.PHONETIC_FAMILY_NAME, parsedName.getFamilyName());
}
}
if (values.containsKey(People.NOTES)) {
mValues3.put(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE);
ContactsDatabaseHelper.copyStringValue(mValues3, Note.NOTE, values, People.NOTES);
}
}
private void parseOrganizationValues(ContentValues values) {
mValues.clear();
mValues.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE);
ContactsDatabaseHelper.copyLongValue(mValues, Data.IS_PRIMARY,
values, android.provider.Contacts.Organizations.ISPRIMARY);
ContactsDatabaseHelper.copyStringValue(mValues, Organization.COMPANY,
values, android.provider.Contacts.Organizations.COMPANY);
// TYPE values happen to remain the same between V1 and V2 - can just copy the value
ContactsDatabaseHelper.copyLongValue(mValues, Organization.TYPE,
values, android.provider.Contacts.Organizations.TYPE);
ContactsDatabaseHelper.copyStringValue(mValues, Organization.LABEL,
values, android.provider.Contacts.Organizations.LABEL);
ContactsDatabaseHelper.copyStringValue(mValues, Organization.TITLE,
values, android.provider.Contacts.Organizations.TITLE);
}
private void parsePhoneValues(ContentValues values) {
mValues.clear();
mValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
ContactsDatabaseHelper.copyLongValue(mValues, Data.IS_PRIMARY,
values, android.provider.Contacts.Phones.ISPRIMARY);
ContactsDatabaseHelper.copyStringValue(mValues, Phone.NUMBER,
values, android.provider.Contacts.Phones.NUMBER);
// TYPE values happen to remain the same between V1 and V2 - can just copy the value
ContactsDatabaseHelper.copyLongValue(mValues, Phone.TYPE,
values, android.provider.Contacts.Phones.TYPE);
ContactsDatabaseHelper.copyStringValue(mValues, Phone.LABEL,
values, android.provider.Contacts.Phones.LABEL);
}
private void parseContactMethodValues(int kind, ContentValues values) {
mValues.clear();
ContactsDatabaseHelper.copyLongValue(mValues, Data.IS_PRIMARY, values,
ContactMethods.ISPRIMARY);
switch (kind) {
case android.provider.Contacts.KIND_EMAIL: {
copyCommonFields(values, Email.CONTENT_ITEM_TYPE, Email.TYPE, Email.LABEL,
Data.DATA14);
ContactsDatabaseHelper.copyStringValue(mValues, Email.DATA, values,
ContactMethods.DATA);
break;
}
case android.provider.Contacts.KIND_IM: {
String protocol = values.getAsString(ContactMethods.DATA);
if (protocol.startsWith("pre:")) {
mValues.put(Im.PROTOCOL, Integer.parseInt(protocol.substring(4)));
} else if (protocol.startsWith("custom:")) {
mValues.put(Im.PROTOCOL, Im.PROTOCOL_CUSTOM);
mValues.put(Im.CUSTOM_PROTOCOL, protocol.substring(7));
}
copyCommonFields(values, Im.CONTENT_ITEM_TYPE, Im.TYPE, Im.LABEL, Data.DATA14);
break;
}
case android.provider.Contacts.KIND_POSTAL: {
copyCommonFields(values, StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE,
StructuredPostal.LABEL, Data.DATA14);
ContactsDatabaseHelper.copyStringValue(mValues, StructuredPostal.FORMATTED_ADDRESS,
values, ContactMethods.DATA);
break;
}
}
}
private void copyCommonFields(ContentValues values, String mimeType, String typeColumn,
String labelColumn, String auxDataColumn) {
mValues.put(Data.MIMETYPE, mimeType);
ContactsDatabaseHelper.copyLongValue(mValues, typeColumn, values,
ContactMethods.TYPE);
ContactsDatabaseHelper.copyStringValue(mValues, labelColumn, values,
ContactMethods.LABEL);
ContactsDatabaseHelper.copyStringValue(mValues, auxDataColumn, values,
ContactMethods.AUX_DATA);
}
private void parseGroupValues(ContentValues values) {
mValues.clear();
ContactsDatabaseHelper.copyStringValue(mValues, Groups.TITLE,
values, android.provider.Contacts.Groups.NAME);
ContactsDatabaseHelper.copyStringValue(mValues, Groups.NOTES,
values, android.provider.Contacts.Groups.NOTES);
ContactsDatabaseHelper.copyStringValue(mValues, Groups.SYSTEM_ID,
values, android.provider.Contacts.Groups.SYSTEM_ID);
}
private void parseExtensionValues(ContentValues values) {
ContactsDatabaseHelper.copyStringValue(mValues, ExtensionsColumns.NAME,
values, android.provider.Contacts.People.Extensions.NAME);
ContactsDatabaseHelper.copyStringValue(mValues, ExtensionsColumns.VALUE,
values, android.provider.Contacts.People.Extensions.VALUE);
}
private Uri findFirstDataRow(long rawContactId, String contentItemType) {
long dataId = findFirstDataId(rawContactId, contentItemType);
if (dataId == -1) {
return null;
}
return ContentUris.withAppendedId(Data.CONTENT_URI, dataId);
}
private long findFirstDataId(long rawContactId, String mimeType) {
long dataId = -1;
Cursor c = mContactsProvider.query(Data.CONTENT_URI, IdQuery.COLUMNS,
Data.RAW_CONTACT_ID + "=" + rawContactId + " AND "
+ Data.MIMETYPE + "='" + mimeType + "'",
null, null);
try {
if (c.moveToFirst()) {
dataId = c.getLong(IdQuery._ID);
}
} finally {
c.close();
}
return dataId;
}
public int delete(Uri uri, String selection, String[] selectionArgs) {
final int match = sUriMatcher.match(uri);
if (match == -1 || match == SETTINGS) {
throw new UnsupportedOperationException(mDbHelper.exceptionMessage(uri));
}
Cursor c = query(uri, IdQuery.COLUMNS, selection, selectionArgs, null, null);
if (c == null) {
return 0;
}
int count = 0;
try {
while (c.moveToNext()) {
long id = c.getLong(IdQuery._ID);
count += delete(uri, match, id);
}
} finally {
c.close();
}
return count;
}
public int delete(Uri uri, int match, long id) {
int count = 0;
switch (match) {
case PEOPLE:
case PEOPLE_ID:
count = mContactsProvider.deleteRawContact(id, mDbHelper.getContactId(id), false);
break;
case PEOPLE_PHOTO:
mValues.clear();
mValues.putNull(android.provider.Contacts.Photos.DATA);
updatePhoto(id, mValues);
break;
case ORGANIZATIONS:
case ORGANIZATIONS_ID:
count = mContactsProvider.deleteData(id, ORGANIZATION_MIME_TYPES);
break;
case CONTACTMETHODS:
case CONTACTMETHODS_ID:
count = mContactsProvider.deleteData(id, CONTACT_METHOD_MIME_TYPES);
break;
case PHONES:
case PHONES_ID:
count = mContactsProvider.deleteData(id, PHONE_MIME_TYPES);
break;
case EXTENSIONS:
case EXTENSIONS_ID:
count = mContactsProvider.deleteData(id, EXTENSION_MIME_TYPES);
break;
case PHOTOS:
case PHOTOS_ID:
count = mContactsProvider.deleteData(id, PHOTO_MIME_TYPES);
break;
case GROUPMEMBERSHIP:
case GROUPMEMBERSHIP_ID:
count = mContactsProvider.deleteData(id, GROUP_MEMBERSHIP_MIME_TYPES);
break;
case GROUPS:
case GROUPS_ID:
count = mContactsProvider.deleteGroup(uri, id, false);
break;
default:
throw new UnsupportedOperationException(mDbHelper.exceptionMessage(uri));
}
return count;
}
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder, String limit) {
ensureDefaultAccount();
final SQLiteDatabase db = mDbHelper.getReadableDatabase();
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
String groupBy = null;
final int match = sUriMatcher.match(uri);
switch (match) {
case PEOPLE: {
qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
qb.setProjectionMap(sPeopleProjectionMap);
applyRawContactsAccount(qb);
break;
}
case PEOPLE_ID:
qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
qb.setProjectionMap(sPeopleProjectionMap);
applyRawContactsAccount(qb);
qb.appendWhere(" AND " + People._ID + "=");
qb.appendWhere(uri.getPathSegments().get(1));
break;
case PEOPLE_FILTER: {
qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
qb.setProjectionMap(sPeopleProjectionMap);
applyRawContactsAccount(qb);
String filterParam = uri.getPathSegments().get(2);
qb.appendWhere(" AND " + People._ID + " IN "
+ getRawContactsByFilterAsNestedQuery(filterParam));
break;
}
case GROUP_NAME_MEMBERS:
qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
qb.setProjectionMap(sPeopleProjectionMap);
applyRawContactsAccount(qb);
String group = uri.getPathSegments().get(2);
qb.appendWhere(" AND " + buildGroupNameMatchWhereClause(group));
break;
case GROUP_SYSTEM_ID_MEMBERS:
qb.setTables(LegacyTables.PEOPLE_JOIN_PRESENCE);
qb.setProjectionMap(sPeopleProjectionMap);
applyRawContactsAccount(qb);
String systemId = uri.getPathSegments().get(2);
qb.appendWhere(" AND " + buildGroupSystemIdMatchWhereClause(systemId));
break;
case ORGANIZATIONS:
qb.setTables(LegacyTables.ORGANIZATIONS + " organizations");
qb.setProjectionMap(sOrganizationProjectionMap);
applyRawContactsAccount(qb);
break;
case ORGANIZATIONS_ID:
qb.setTables(LegacyTables.ORGANIZATIONS + " organizations");
qb.setProjectionMap(sOrganizationProjectionMap);
applyRawContactsAccount(qb);
qb.appendWhere(" AND " + android.provider.Contacts.Organizations._ID + "=");
qb.appendWhere(uri.getPathSegments().get(1));
break;
case PEOPLE_ORGANIZATIONS:
qb.setTables(LegacyTables.ORGANIZATIONS + " organizations");
qb.setProjectionMap(sOrganizationProjectionMap);
applyRawContactsAccount(qb);
qb.appendWhere(" AND " + android.provider.Contacts.Organizations.PERSON_ID + "=");
qb.appendWhere(uri.getPathSegments().get(1));
break;
case PEOPLE_ORGANIZATIONS_ID:
qb.setTables(LegacyTables.ORGANIZATIONS + " organizations");
qb.setProjectionMap(sOrganizationProjectionMap);
applyRawContactsAccount(qb);
qb.appendWhere(" AND " + android.provider.Contacts.Organizations.PERSON_ID + "=");
qb.appendWhere(uri.getPathSegments().get(1));
qb.appendWhere(" AND " + android.provider.Contacts.Organizations._ID + "=");
qb.appendWhere(uri.getPathSegments().get(3));
break;
case CONTACTMETHODS:
qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
qb.setProjectionMap(sContactMethodProjectionMap);
applyRawContactsAccount(qb);
break;
case CONTACTMETHODS_ID:
qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
qb.setProjectionMap(sContactMethodProjectionMap);
applyRawContactsAccount(qb);
qb.appendWhere(" AND " + ContactMethods._ID + "=");
qb.appendWhere(uri.getPathSegments().get(1));
break;
case CONTACTMETHODS_EMAIL:
qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
qb.setProjectionMap(sContactMethodProjectionMap);
applyRawContactsAccount(qb);
qb.appendWhere(" AND " + ContactMethods.KIND + "="
+ android.provider.Contacts.KIND_EMAIL);
break;
case PEOPLE_CONTACTMETHODS:
qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
qb.setProjectionMap(sContactMethodProjectionMap);
applyRawContactsAccount(qb);
qb.appendWhere(" AND " + ContactMethods.PERSON_ID + "=");
qb.appendWhere(uri.getPathSegments().get(1));
qb.appendWhere(" AND " + ContactMethods.KIND + " IS NOT NULL");
break;
case PEOPLE_CONTACTMETHODS_ID:
qb.setTables(LegacyTables.CONTACT_METHODS + " contact_methods");
qb.setProjectionMap(sContactMethodProjectionMap);
applyRawContactsAccount(qb);
qb.appendWhere(" AND " + ContactMethods.PERSON_ID + "=");
qb.appendWhere(uri.getPathSegments().get(1));
qb.appendWhere(" AND " + ContactMethods._ID + "=");
qb.appendWhere(uri.getPathSegments().get(3));
qb.appendWhere(" AND " + ContactMethods.KIND + " IS NOT NULL");
break;
case PHONES:
qb.setTables(LegacyTables.PHONES + " phones");
qb.setProjectionMap(sPhoneProjectionMap);
applyRawContactsAccount(qb);
break;
case PHONES_ID:
qb.setTables(LegacyTables.PHONES + " phones");
qb.setProjectionMap(sPhoneProjectionMap);
applyRawContactsAccount(qb);
qb.appendWhere(" AND " + android.provider.Contacts.Phones._ID + "=");
qb.appendWhere(uri.getPathSegments().get(1));
break;
case PHONES_FILTER:
qb.setTables(LegacyTables.PHONES + " phones");
qb.setProjectionMap(sPhoneProjectionMap);
applyRawContactsAccount(qb);
if (uri.getPathSegments().size() > 2) {
String filterParam = uri.getLastPathSegment();
qb.appendWhere(" AND person =");
qb.appendWhere(mDbHelper.buildPhoneLookupAsNestedQuery(filterParam));
qb.setDistinct(true);
}
break;
case PEOPLE_PHONES:
qb.setTables(LegacyTables.PHONES + " phones");
qb.setProjectionMap(sPhoneProjectionMap);
applyRawContactsAccount(qb);
qb.appendWhere(" AND " + android.provider.Contacts.Phones.PERSON_ID + "=");
qb.appendWhere(uri.getPathSegments().get(1));
break;
case PEOPLE_PHONES_ID:
qb.setTables(LegacyTables.PHONES + " phones");
qb.setProjectionMap(sPhoneProjectionMap);
applyRawContactsAccount(qb);
qb.appendWhere(" AND " + android.provider.Contacts.Phones.PERSON_ID + "=");
qb.appendWhere(uri.getPathSegments().get(1));
qb.appendWhere(" AND " + android.provider.Contacts.Phones._ID + "=");
qb.appendWhere(uri.getPathSegments().get(3));
break;
case EXTENSIONS:
qb.setTables(LegacyTables.EXTENSIONS + " extensions");
qb.setProjectionMap(sExtensionProjectionMap);
applyRawContactsAccount(qb);
break;
case EXTENSIONS_ID:
qb.setTables(LegacyTables.EXTENSIONS + " extensions");
qb.setProjectionMap(sExtensionProjectionMap);
applyRawContactsAccount(qb);
qb.appendWhere(" AND " + android.provider.Contacts.Extensions._ID + "=");
qb.appendWhere(uri.getPathSegments().get(1));
break;
case PEOPLE_EXTENSIONS:
qb.setTables(LegacyTables.EXTENSIONS + " extensions");
qb.setProjectionMap(sExtensionProjectionMap);
applyRawContactsAccount(qb);
qb.appendWhere(" AND " + android.provider.Contacts.Extensions.PERSON_ID + "=");
qb.appendWhere(uri.getPathSegments().get(1));
break;
case PEOPLE_EXTENSIONS_ID:
qb.setTables(LegacyTables.EXTENSIONS + " extensions");
qb.setProjectionMap(sExtensionProjectionMap);
applyRawContactsAccount(qb);
qb.appendWhere(" AND " + android.provider.Contacts.Extensions.PERSON_ID + "=");
qb.appendWhere(uri.getPathSegments().get(1));
qb.appendWhere(" AND " + android.provider.Contacts.Extensions._ID + "=");
qb.appendWhere(uri.getPathSegments().get(3));
break;
case GROUPS:
qb.setTables(LegacyTables.GROUPS + " groups");
qb.setProjectionMap(sGroupProjectionMap);
applyGroupAccount(qb);
break;
case GROUPS_ID:
qb.setTables(LegacyTables.GROUPS + " groups");
qb.setProjectionMap(sGroupProjectionMap);
applyGroupAccount(qb);
qb.appendWhere(" AND " + android.provider.Contacts.Groups._ID + "=");
qb.appendWhere(uri.getPathSegments().get(1));
break;
case GROUPMEMBERSHIP:
qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership");
qb.setProjectionMap(sGroupMembershipProjectionMap);
applyRawContactsAccount(qb);
break;
case GROUPMEMBERSHIP_ID:
qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership");
qb.setProjectionMap(sGroupMembershipProjectionMap);
applyRawContactsAccount(qb);
qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership._ID + "=");
qb.appendWhere(uri.getPathSegments().get(1));
break;
case PEOPLE_GROUPMEMBERSHIP:
qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership");
qb.setProjectionMap(sGroupMembershipProjectionMap);
applyRawContactsAccount(qb);
qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership.PERSON_ID + "=");
qb.appendWhere(uri.getPathSegments().get(1));
break;
case PEOPLE_GROUPMEMBERSHIP_ID:
qb.setTables(LegacyTables.GROUP_MEMBERSHIP + " groupmembership");
qb.setProjectionMap(sGroupMembershipProjectionMap);
applyRawContactsAccount(qb);
qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership.PERSON_ID + "=");
qb.appendWhere(uri.getPathSegments().get(1));
qb.appendWhere(" AND " + android.provider.Contacts.GroupMembership._ID + "=");
qb.appendWhere(uri.getPathSegments().get(3));
break;
case PEOPLE_PHOTO:
qb.setTables(LegacyTables.PHOTOS + " photos");
qb.setProjectionMap(sPhotoProjectionMap);
applyRawContactsAccount(qb);
qb.appendWhere(" AND " + android.provider.Contacts.Photos.PERSON_ID + "=");
qb.appendWhere(uri.getPathSegments().get(1));
limit = "1";
break;
case PHOTOS:
qb.setTables(LegacyTables.PHOTOS + " photos");
qb.setProjectionMap(sPhotoProjectionMap);
applyRawContactsAccount(qb);
break;
case PHOTOS_ID:
qb.setTables(LegacyTables.PHOTOS + " photos");
qb.setProjectionMap(sPhotoProjectionMap);
applyRawContactsAccount(qb);
qb.appendWhere(" AND " + android.provider.Contacts.Photos._ID + "=");
qb.appendWhere(uri.getPathSegments().get(1));
break;
case SEARCH_SUGGESTIONS:
return mGlobalSearchSupport.handleSearchSuggestionsQuery(
db, uri, projection, limit, null);
case SEARCH_SHORTCUT: {
String lookupKey = uri.getLastPathSegment();
String filter = ContactsProvider2.getQueryParameter(uri, "filter");
return mGlobalSearchSupport.handleSearchShortcutRefresh(
db, projection, lookupKey, filter, null);
}
case DELETED_PEOPLE:
case DELETED_GROUPS:
throw new UnsupportedOperationException(mDbHelper.exceptionMessage(uri));
case SETTINGS:
copySettingsToLegacySettings();
qb.setTables(LegacyTables.SETTINGS);
break;
default:
throw new IllegalArgumentException(mDbHelper.exceptionMessage(uri));
}
// Perform the query and set the notification uri
final Cursor c = qb.query(db, projection, selection, selectionArgs,
groupBy, null, sortOrder, limit);
if (c != null) {
c.setNotificationUri(mContext.getContentResolver(),
android.provider.Contacts.CONTENT_URI);
}
return c;
}
private void applyRawContactsAccount(SQLiteQueryBuilder qb) {
StringBuilder sb = new StringBuilder();
appendRawContactsAccount(sb);
qb.appendWhere(sb.toString());
}
private void appendRawContactsAccount(StringBuilder sb) {
if (mAccount != null) {
sb.append(RawContacts.ACCOUNT_NAME + "=");
DatabaseUtils.appendEscapedSQLString(sb, mAccount.name);
sb.append(" AND " + RawContacts.ACCOUNT_TYPE + "=");
DatabaseUtils.appendEscapedSQLString(sb, mAccount.type);
} else {
sb.append(RawContacts.ACCOUNT_NAME + " IS NULL" +
" AND " + RawContacts.ACCOUNT_TYPE + " IS NULL");
}
}
private void applyGroupAccount(SQLiteQueryBuilder qb) {
StringBuilder sb = new StringBuilder();
appendGroupAccount(sb);
qb.appendWhere(sb.toString());
}
private void appendGroupAccount(StringBuilder sb) {
if (mAccount != null) {
sb.append(Groups.ACCOUNT_NAME + "=");
DatabaseUtils.appendEscapedSQLString(sb, mAccount.name);
sb.append(" AND " + Groups.ACCOUNT_TYPE + "=");
DatabaseUtils.appendEscapedSQLString(sb, mAccount.type);
} else {
sb.append(Groups.ACCOUNT_NAME + " IS NULL" +
" AND " + Groups.ACCOUNT_TYPE + " IS NULL");
}
}
/**
* Build a WHERE clause that restricts the query to match people that are a member of
* a group with a particular name. The projection map of the query must include
* {@link People#_ID}.
*
* @param groupName The name of the group
* @return The where clause.
*/
private String buildGroupNameMatchWhereClause(String groupName) {
return "people._id IN "
+ "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
+ " FROM " + Tables.DATA_JOIN_MIMETYPES
+ " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
+ "' AND " + GroupMembership.GROUP_ROW_ID + "="
+ "(SELECT " + Tables.GROUPS + "." + Groups._ID
+ " FROM " + Tables.GROUPS
+ " WHERE " + Groups.TITLE + "="
+ DatabaseUtils.sqlEscapeString(groupName) + "))";
}
/**
* Build a WHERE clause that restricts the query to match people that are a member of
* a group with a particular system id. The projection map of the query must include
* {@link People#_ID}.
*
* @return The where clause.
*/
private String buildGroupSystemIdMatchWhereClause(String systemId) {
return "people._id IN "
+ "(SELECT " + DataColumns.CONCRETE_RAW_CONTACT_ID
+ " FROM " + Tables.DATA_JOIN_MIMETYPES
+ " WHERE " + Data.MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE
+ "' AND " + GroupMembership.GROUP_ROW_ID + "="
+ "(SELECT " + Tables.GROUPS + "." + Groups._ID
+ " FROM " + Tables.GROUPS
+ " WHERE " + Groups.SYSTEM_ID + "="
+ DatabaseUtils.sqlEscapeString(systemId) + "))";
}
private String getRawContactsByFilterAsNestedQuery(String filterParam) {
StringBuilder sb = new StringBuilder();
String normalizedName = NameNormalizer.normalize(filterParam);
if (TextUtils.isEmpty(normalizedName)) {
// Effectively an empty IN clause - SQL syntax does not allow an actual empty list here
sb.append("(0)");
} else {
sb.append("(" +
"SELECT " + NameLookupColumns.RAW_CONTACT_ID +
" FROM " + Tables.NAME_LOOKUP +
" WHERE " + NameLookupColumns.NORMALIZED_NAME +
" GLOB '");
// Should not use a "?" argument placeholder here, because
// that would prevent the SQL optimizer from using the index on NORMALIZED_NAME.
sb.append(normalizedName);
sb.append("*' AND " + NameLookupColumns.NAME_TYPE + " IN ("
+ NameLookupType.NAME_COLLATION_KEY + ","
+ NameLookupType.NICKNAME);
if (true) {
sb.append("," + NameLookupType.EMAIL_BASED_NICKNAME);
}
sb.append("))");
}
return sb.toString();
}
/**
* Called when a change has been made.
*
* @param uri the uri that the change was made to
*/
private void onChange(Uri uri) {
mContext.getContentResolver().notifyChange(android.provider.Contacts.CONTENT_URI, null);
}
public String getType(Uri uri) {
int match = sUriMatcher.match(uri);
switch (match) {
case EXTENSIONS:
case PEOPLE_EXTENSIONS:
return Extensions.CONTENT_TYPE;
case EXTENSIONS_ID:
case PEOPLE_EXTENSIONS_ID:
return Extensions.CONTENT_ITEM_TYPE;
case PEOPLE:
return "vnd.android.cursor.dir/person";
case PEOPLE_ID:
return "vnd.android.cursor.item/person";
case PEOPLE_PHONES:
return "vnd.android.cursor.dir/phone";
case PEOPLE_PHONES_ID:
return "vnd.android.cursor.item/phone";
case PEOPLE_CONTACTMETHODS:
return "vnd.android.cursor.dir/contact-methods";
case PEOPLE_CONTACTMETHODS_ID:
return getContactMethodType(uri);
case PHONES:
return "vnd.android.cursor.dir/phone";
case PHONES_ID:
return "vnd.android.cursor.item/phone";
case PHONES_FILTER:
return "vnd.android.cursor.dir/phone";
case PHOTOS_ID:
return "vnd.android.cursor.item/photo";
case PHOTOS:
return "vnd.android.cursor.dir/photo";
case PEOPLE_PHOTO:
return "vnd.android.cursor.item/photo";
case CONTACTMETHODS:
return "vnd.android.cursor.dir/contact-methods";
case CONTACTMETHODS_ID:
return getContactMethodType(uri);
case ORGANIZATIONS:
return "vnd.android.cursor.dir/organizations";
case ORGANIZATIONS_ID:
return "vnd.android.cursor.item/organization";
case SEARCH_SUGGESTIONS:
return SearchManager.SUGGEST_MIME_TYPE;
case SEARCH_SHORTCUT:
return SearchManager.SHORTCUT_MIME_TYPE;
default:
throw new IllegalArgumentException(mDbHelper.exceptionMessage(uri));
}
}
private String getContactMethodType(Uri url) {
String mime = null;
Cursor c = query(url, new String[] {ContactMethods.KIND}, null, null, null, null);
if (c != null) {
try {
if (c.moveToFirst()) {
int kind = c.getInt(0);
switch (kind) {
case android.provider.Contacts.KIND_EMAIL:
mime = "vnd.android.cursor.item/email";
break;
case android.provider.Contacts.KIND_IM:
mime = "vnd.android.cursor.item/jabber-im";
break;
case android.provider.Contacts.KIND_POSTAL:
mime = "vnd.android.cursor.item/postal-address";
break;
}
}
} finally {
c.close();
}
}
return mime;
}
}