From b38ed2c5ffeb20efc677b4a9229db4a00603aa8d Mon Sep 17 00:00:00 2001 From: Dmitri Plotnikov Date: Tue, 29 Sep 2009 10:54:56 -0700 Subject: Renaming OpenHelper to ContactsDatabaseHelper Change-Id: Iccdf1ebcd8a3ab430941c957f712bb9e7dd2706c --- .../providers/contacts/CallLogProvider.java | 18 +- .../providers/contacts/ContactAggregator.java | 64 +- .../android/providers/contacts/ContactMatcher.java | 2 +- .../providers/contacts/ContactsDatabaseHelper.java | 1713 +++++++++++++++++++ .../providers/contacts/ContactsProvider2.java | 202 +-- .../providers/contacts/GlobalSearchSupport.java | 10 +- .../providers/contacts/LegacyApiSupport.java | 109 +- .../providers/contacts/LegacyContactImporter.java | 36 +- .../providers/contacts/NameLookupBuilder.java | 2 +- src/com/android/providers/contacts/OpenHelper.java | 1715 -------------------- .../providers/contacts/SQLiteContentProvider.java | 6 +- .../android/providers/contacts/SocialProvider.java | 28 +- .../contacts/BaseContactsProvider2Test.java | 2 +- .../providers/contacts/CallLogProviderTest.java | 10 +- .../providers/contacts/ContactsProvider2Test.java | 2 +- .../LegacyContactImporterPerformanceTest.java | 10 +- .../contacts/LegacyContactImporterTest.java | 10 +- .../contacts/SynchronousContactsProvider2.java | 18 +- 18 files changed, 1981 insertions(+), 1976 deletions(-) create mode 100644 src/com/android/providers/contacts/ContactsDatabaseHelper.java delete mode 100644 src/com/android/providers/contacts/OpenHelper.java diff --git a/src/com/android/providers/contacts/CallLogProvider.java b/src/com/android/providers/contacts/CallLogProvider.java index 240eb414..ec4bf445 100644 --- a/src/com/android/providers/contacts/CallLogProvider.java +++ b/src/com/android/providers/contacts/CallLogProvider.java @@ -16,7 +16,7 @@ package com.android.providers.contacts; -import com.android.providers.contacts.OpenHelper.Tables; +import com.android.providers.contacts.ContactsDatabaseHelper.Tables; import android.content.ContentProvider; import android.content.ContentUris; @@ -67,7 +67,7 @@ public class CallLogProvider extends ContentProvider { sCallsProjectionMap.put(Calls.CACHED_NUMBER_LABEL, Calls.CACHED_NUMBER_LABEL); } - private OpenHelper mOpenHelper; + private ContactsDatabaseHelper mDbHelper; private DatabaseUtils.InsertHelper mCallsInserter; private boolean mUseStrictPhoneNumberComparation; @@ -75,8 +75,8 @@ public class CallLogProvider extends ContentProvider { public boolean onCreate() { final Context context = getContext(); - mOpenHelper = getOpenHelper(context); - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + mDbHelper = getDatabaseHelper(context); + SQLiteDatabase db = mDbHelper.getWritableDatabase(); mCallsInserter = new DatabaseUtils.InsertHelper(db, Tables.CALLS); mUseStrictPhoneNumberComparation = @@ -87,8 +87,8 @@ public class CallLogProvider extends ContentProvider { } /* Visible for testing */ - protected OpenHelper getOpenHelper(final Context context) { - return OpenHelper.getInstance(context); + protected ContactsDatabaseHelper getDatabaseHelper(final Context context) { + return ContactsDatabaseHelper.getInstance(context); } @Override @@ -125,7 +125,7 @@ public class CallLogProvider extends ContentProvider { throw new IllegalArgumentException("Unknown URL " + uri); } - final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + final SQLiteDatabase db = mDbHelper.getReadableDatabase(); Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder, null); if (c != null) { c.setNotificationUri(getContext().getContentResolver(), CallLog.CONTENT_URI); @@ -160,7 +160,7 @@ public class CallLogProvider extends ContentProvider { @Override public int update(Uri url, ContentValues values, String selection, String[] selectionArgs) { - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + final SQLiteDatabase db = mDbHelper.getWritableDatabase(); String where; final int matchedUriId = sURIMatcher.match(url); switch (matchedUriId) { @@ -184,7 +184,7 @@ public class CallLogProvider extends ContentProvider { @Override public int delete(Uri uri, String selection, String[] selectionArgs) { - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + final SQLiteDatabase db = mDbHelper.getWritableDatabase(); final int matchedUriId = sURIMatcher.match(uri); switch (matchedUriId) { diff --git a/src/com/android/providers/contacts/ContactAggregator.java b/src/com/android/providers/contacts/ContactAggregator.java index 2fa8d116..7e18fd9a 100644 --- a/src/com/android/providers/contacts/ContactAggregator.java +++ b/src/com/android/providers/contacts/ContactAggregator.java @@ -17,16 +17,16 @@ package com.android.providers.contacts; import com.android.providers.contacts.ContactMatcher.MatchScore; -import com.android.providers.contacts.OpenHelper.AggregatedPresenceColumns; -import com.android.providers.contacts.OpenHelper.ContactsColumns; -import com.android.providers.contacts.OpenHelper.DataColumns; -import com.android.providers.contacts.OpenHelper.DisplayNameSources; -import com.android.providers.contacts.OpenHelper.MimetypesColumns; -import com.android.providers.contacts.OpenHelper.NameLookupColumns; -import com.android.providers.contacts.OpenHelper.NameLookupType; -import com.android.providers.contacts.OpenHelper.PresenceColumns; -import com.android.providers.contacts.OpenHelper.RawContactsColumns; -import com.android.providers.contacts.OpenHelper.Tables; +import com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.DisplayNameSources; +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.PresenceColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.Tables; import android.content.ContentValues; import android.database.Cursor; @@ -166,7 +166,7 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator private static final int FIRST_LETTER_SUGGESTION_HIT_LIMIT = 100; private final ContactsProvider2 mContactsProvider; - private final OpenHelper mOpenHelper; + private final ContactsDatabaseHelper mDbHelper; private final ContactAggregationScheduler mScheduler; private boolean mEnabled = true; @@ -259,12 +259,12 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator * aggregation thread. Call {@link #schedule} to kick off the aggregation process after * a delay of {@link ContactAggregationScheduler#AGGREGATION_DELAY} milliseconds. */ - public ContactAggregator(ContactsProvider2 contactsProvider, OpenHelper openHelper, - ContactAggregationScheduler scheduler) { + public ContactAggregator(ContactsProvider2 contactsProvider, + ContactsDatabaseHelper contactsDatabaseHelper, ContactAggregationScheduler scheduler) { mContactsProvider = contactsProvider; - mOpenHelper = openHelper; + mDbHelper = contactsDatabaseHelper; - SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + SQLiteDatabase db = mDbHelper.getReadableDatabase(); // Since we have no way of determining which custom status was set last, // we'll just pick one randomly. We are using MAX as an approximation of randomness @@ -417,7 +417,7 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator mCancel = false; - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + SQLiteDatabase db = mDbHelper.getWritableDatabase(); MatchCandidateList candidates = new MatchCandidateList(); ContactMatcher matcher = new ContactMatcher(); ContentValues values = new ContentValues(); @@ -601,7 +601,7 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator RawContactsColumns.CONCRETE_ID + "=" + rawContactId, contactValues); long contactId = db.insert(Tables.CONTACTS, Contacts.DISPLAY_NAME, contactValues); setContactId(rawContactId, contactId); - mOpenHelper.updateContactVisible(contactId); + mDbHelper.updateContactVisible(contactId); } /** @@ -624,12 +624,12 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator return; } - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + final SQLiteDatabase db = mDbHelper.getWritableDatabase(); final ContentValues values = new ContentValues(); computeAggregateData(db, contactId, values); db.update(Tables.CONTACTS, values, Contacts._ID + "=" + contactId, null); - mOpenHelper.updateContactVisible(contactId); + mDbHelper.updateContactVisible(contactId); updateAggregatedPresence(contactId); } @@ -681,7 +681,7 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator RawContactsColumns.CONCRETE_ID + "=" + rawContactId, contactValues); contactId = db.insert(Tables.CONTACTS, Contacts.DISPLAY_NAME, contactValues); setContactIdAndMarkAggregated(rawContactId, contactId); - mOpenHelper.updateContactVisible(contactId); + mDbHelper.updateContactVisible(contactId); setPresenceContactId(rawContactId, contactId); @@ -704,7 +704,7 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator setContactIdAndMarkAggregated(rawContactId, contactId); computeAggregateData(db, contactId, values); db.update(Tables.CONTACTS, values, Contacts._ID + "=" + contactId, null); - mOpenHelper.updateContactVisible(contactId); + mDbHelper.updateContactVisible(contactId); updateAggregatedPresence(contactId); } } @@ -1066,7 +1066,7 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator private void lookupPhoneMatches(SQLiteDatabase db, String phoneNumber, ContactMatcher matcher) { SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - mOpenHelper.buildPhoneLookupAndRawContactQuery(qb, phoneNumber); + mDbHelper.buildPhoneLookupAndRawContactQuery(qb, phoneNumber); Cursor c = qb.query(db, CONTACT_ID_COLUMNS, RawContactsColumns.AGGREGATION_NEEDED + "=0", null, null, null, null, String.valueOf(SECONDARY_HIT_LIMIT)); try { @@ -1083,7 +1083,7 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator * Finds exact email matches and updates their match scores. */ private void lookupEmailMatches(SQLiteDatabase db, String address, ContactMatcher matcher) { - long mimetypeId = mOpenHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE); + long mimetypeId = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE); Cursor c = db.query(EmailLookupQuery.TABLE, EmailLookupQuery.COLUMNS, DataColumns.MIMETYPE_ID + "=" + mimetypeId + " AND " + Email.DATA + "=?" @@ -1181,8 +1181,8 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator int hasPhoneNumber = 0; StringBuilder lookupKey = new StringBuilder(); - long photoMimeType = mOpenHelper.getMimeTypeId(Photo.CONTENT_ITEM_TYPE); - long phoneMimeType = mOpenHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE); + long photoMimeType = mDbHelper.getMimeTypeId(Photo.CONTENT_ITEM_TYPE); + long phoneMimeType = mDbHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE); String isPhotoSql = "(" + DataColumns.MIMETYPE_ID + "=" + photoMimeType + " AND " + Photo.PHOTO + " NOT NULL)"; @@ -1328,7 +1328,7 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator public void updatePhotoId(SQLiteDatabase db, long rawContactId) { - long contactId = mOpenHelper.getContactId(rawContactId); + long contactId = mDbHelper.getContactId(rawContactId); if (contactId == 0) { return; } @@ -1336,7 +1336,7 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator long bestPhotoId = -1; String photoAccount = null; - long photoMimeType = mOpenHelper.getMimeTypeId(Photo.CONTENT_ITEM_TYPE); + long photoMimeType = mDbHelper.getMimeTypeId(Photo.CONTENT_ITEM_TYPE); String tables = Tables.RAW_CONTACTS + " JOIN " + Tables.DATA + " ON(" + DataColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID @@ -1386,7 +1386,7 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator public void updateDisplayName(SQLiteDatabase db, long rawContactId) { - long contactId = mOpenHelper.getContactId(rawContactId); + long contactId = mDbHelper.getContactId(rawContactId); if (contactId == 0) { return; } @@ -1435,12 +1435,12 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator */ public void updateHasPhoneNumber(SQLiteDatabase db, long rawContactId) { - long contactId = mOpenHelper.getContactId(rawContactId); + long contactId = mDbHelper.getContactId(rawContactId); if (contactId == 0) { return; } - mHasPhoneNumberUpdate.bindLong(1, mOpenHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE)); + mHasPhoneNumberUpdate.bindLong(1, mDbHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE)); mHasPhoneNumberUpdate.bindLong(2, contactId); mHasPhoneNumberUpdate.bindLong(3, contactId); mHasPhoneNumberUpdate.execute(); @@ -1461,7 +1461,7 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator } public void updateLookupKey(SQLiteDatabase db, long rawContactId) { - long contactId = mOpenHelper.getContactId(rawContactId); + long contactId = mDbHelper.getContactId(rawContactId); if (contactId == 0) { return; } @@ -1504,7 +1504,7 @@ public class ContactAggregator implements ContactAggregationScheduler.Aggregator */ public Cursor queryAggregationSuggestions(SQLiteQueryBuilder qb, String[] projection, long contactId, int maxSuggestions, String filter) { - final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + final SQLiteDatabase db = mDbHelper.getReadableDatabase(); List bestMatches = findMatchingContacts(db, contactId); return queryMatchingContacts(qb, db, contactId, projection, bestMatches, maxSuggestions, diff --git a/src/com/android/providers/contacts/ContactMatcher.java b/src/com/android/providers/contacts/ContactMatcher.java index 4ad36d9c..c314d5ea 100644 --- a/src/com/android/providers/contacts/ContactMatcher.java +++ b/src/com/android/providers/contacts/ContactMatcher.java @@ -15,7 +15,7 @@ */ package com.android.providers.contacts; -import com.android.providers.contacts.OpenHelper.NameLookupType; +import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType; import java.util.ArrayList; import java.util.Collections; diff --git a/src/com/android/providers/contacts/ContactsDatabaseHelper.java b/src/com/android/providers/contacts/ContactsDatabaseHelper.java new file mode 100644 index 00000000..d84fe0ea --- /dev/null +++ b/src/com/android/providers/contacts/ContactsDatabaseHelper.java @@ -0,0 +1,1713 @@ +/* + * 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 com.android.internal.content.SyncStateContentProviderHelper; + +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.database.DatabaseUtils; +import android.database.SQLException; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteDoneException; +import android.database.sqlite.SQLiteException; +import android.database.sqlite.SQLiteOpenHelper; +import android.database.sqlite.SQLiteQueryBuilder; +import android.database.sqlite.SQLiteStatement; +import android.os.Binder; +import android.os.Bundle; +import android.provider.BaseColumns; +import android.provider.ContactsContract; +import android.provider.CallLog.Calls; +import android.provider.ContactsContract.AggregationExceptions; +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.provider.ContactsContract.CommonDataKinds.GroupMembership; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.SocialContract.Activities; +import android.telephony.PhoneNumberUtils; +import android.util.Log; + +import java.util.HashMap; + +/** + * Database helper for contacts. Designed as a singleton to make sure that all + * {@link android.content.ContentProvider} users get the same reference. + * Provides handy methods for maintaining package and mime-type lookup tables. + */ +/* package */ class ContactsDatabaseHelper extends SQLiteOpenHelper { + private static final String TAG = "ContactsDatabaseHelper"; + + private static final int DATABASE_VERSION = 98; + + private static final String DATABASE_NAME = "contacts2.db"; + private static final String DATABASE_PRESENCE = "presence_db"; + + public interface Tables { + public static final String CONTACTS = "contacts"; + public static final String RAW_CONTACTS = "raw_contacts"; + public static final String PACKAGES = "packages"; + public static final String MIMETYPES = "mimetypes"; + public static final String PHONE_LOOKUP = "phone_lookup"; + public static final String NAME_LOOKUP = "name_lookup"; + public static final String AGGREGATION_EXCEPTIONS = "agg_exceptions"; + public static final String SETTINGS = "settings"; + public static final String DATA = "data"; + public static final String GROUPS = "groups"; + public static final String PRESENCE = "presence"; + public static final String AGGREGATED_PRESENCE = "agg_presence"; + public static final String NICKNAME_LOOKUP = "nickname_lookup"; + public static final String CALLS = "calls"; + public static final String CONTACT_ENTITIES = "contact_entities_view"; + public static final String CONTACT_ENTITIES_RESTRICTED = "contact_entities_view_restricted"; + public static final String STATUS_UPDATES = "status_updates"; + + public static final String DATA_JOIN_MIMETYPES = "data " + + "JOIN mimetypes ON (data.mimetype_id = mimetypes._id)"; + + public static final String DATA_JOIN_RAW_CONTACTS = "data " + + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id)"; + + public static final String DATA_JOIN_MIMETYPE_RAW_CONTACTS = "data " + + "JOIN mimetypes ON (data.mimetype_id = mimetypes._id) " + + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id)"; + + public static final String DATA_JOIN_RAW_CONTACTS_GROUPS = "data " + + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id)" + + "LEFT OUTER JOIN groups ON (groups._id = data." + GroupMembership.GROUP_ROW_ID + + ")"; + + public static final String DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS = "data " + + "JOIN mimetypes ON (data.mimetype_id = mimetypes._id) " + + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) " + + "LEFT OUTER JOIN packages ON (data.package_id = packages._id)"; + + public static final String DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_CONTACTS = "data " + + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) " + + "JOIN mimetypes ON (data.mimetype_id = mimetypes._id) " + + "LEFT OUTER JOIN packages ON (data.package_id = packages._id) " + + "LEFT OUTER JOIN contacts ON (raw_contacts.contact_id = contacts._id)"; + + public static final String RAW_CONTACTS_JOIN_CONTACTS = "raw_contacts " + + "LEFT OUTER JOIN contacts ON (raw_contacts.contact_id = contacts._id)"; + + // NOTE: This requires late binding of GroupMembership MIME-type + public static final String RAW_CONTACTS_JOIN_SETTINGS_DATA_GROUPS = "raw_contacts " + + "LEFT OUTER JOIN settings ON (" + + "raw_contacts.account_name = settings.account_name AND " + + "raw_contacts.account_type = settings.account_type) " + + "LEFT OUTER JOIN data ON (data.mimetype_id=? AND " + + "data.raw_contact_id = raw_contacts._id) " + + "LEFT OUTER JOIN groups ON (groups._id = data." + GroupMembership.GROUP_ROW_ID + + ")"; + + // NOTE: This requires late binding of GroupMembership MIME-type + public static final String SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS = "settings " + + "LEFT OUTER JOIN raw_contacts ON (" + + "raw_contacts.account_name = settings.account_name AND " + + "raw_contacts.account_type = settings.account_type) " + + "LEFT OUTER JOIN data ON (data.mimetype_id=? AND " + + "data.raw_contact_id = raw_contacts._id) " + + "LEFT OUTER JOIN contacts ON (raw_contacts.contact_id = contacts._id)"; + + public static final String DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS = "data " + + "JOIN mimetypes ON (data.mimetype_id = mimetypes._id) " + + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) " + + "LEFT OUTER JOIN contacts ON (raw_contacts.contact_id = contacts._id)"; + + public static final String DATA_INNER_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS = "data " + + "JOIN mimetypes ON (data.mimetype_id = mimetypes._id) " + + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) " + + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)"; + + public static final String DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_CONTACTS_GROUPS = + "data " + + "JOIN mimetypes ON (data.mimetype_id = mimetypes._id) " + + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) " + + "LEFT OUTER JOIN packages ON (data.package_id = packages._id) " + + "LEFT OUTER JOIN groups " + + " ON (mimetypes.mimetype='" + GroupMembership.CONTENT_ITEM_TYPE + "' " + + " AND groups._id = data." + GroupMembership.GROUP_ROW_ID + ") " + + "LEFT OUTER JOIN contacts ON (raw_contacts.contact_id = contacts._id)"; + + public static final String DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_GROUPS = "data " + + "JOIN mimetypes ON (data.mimetype_id = mimetypes._id) " + + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) " + + "LEFT OUTER JOIN packages ON (data.package_id = packages._id) " + + "LEFT OUTER JOIN groups " + + " ON (mimetypes.mimetype='" + GroupMembership.CONTENT_ITEM_TYPE + "' " + + " AND groups._id = data." + GroupMembership.GROUP_ROW_ID + ") "; + + public static final String GROUPS_JOIN_PACKAGES = "groups " + + "LEFT OUTER JOIN packages ON (groups.package_id = packages._id)"; + + public static final String GROUPS_JOIN_PACKAGES_DATA_RAW_CONTACTS_CONTACTS = "groups " + + "LEFT OUTER JOIN packages ON (groups.package_id = packages._id) " + + "LEFT OUTER JOIN data " + + " ON (groups._id = data." + GroupMembership.GROUP_ROW_ID + ") " + + "LEFT OUTER JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) " + + "LEFT OUTER JOIN contacts ON (raw_contacts.contact_id = contacts._id)"; + + public static final String ACTIVITIES = "activities"; + + public static final String ACTIVITIES_JOIN_MIMETYPES = "activities " + + "LEFT OUTER JOIN mimetypes ON (activities.mimetype_id = mimetypes._id)"; + + public static final String ACTIVITIES_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_CONTACTS = + "activities " + + "LEFT OUTER JOIN packages ON (activities.package_id = packages._id) " + + "LEFT OUTER JOIN mimetypes ON (activities.mimetype_id = mimetypes._id) " + + "LEFT OUTER JOIN raw_contacts ON (activities.author_contact_id = " + + "raw_contacts._id) " + + "LEFT OUTER JOIN contacts ON (raw_contacts.contact_id = contacts._id)"; + + public static final String NAME_LOOKUP_JOIN_RAW_CONTACTS = "name_lookup " + + "INNER JOIN raw_contacts ON (name_lookup.raw_contact_id = raw_contacts._id)"; + } + + public interface Views { + public static final String DATA_ALL = "view_data"; + public static final String DATA_RESTRICTED = "view_data_restricted"; + + public static final String RAW_CONTACTS_ALL = "view_raw_contacts"; + public static final String RAW_CONTACTS_RESTRICTED = "view_raw_contacts_restricted"; + + public static final String CONTACTS_ALL = "view_contacts"; + public static final String CONTACTS_RESTRICTED = "view_contacts_restricted"; + + public static final String GROUPS_ALL = "view_groups"; + } + + public interface Clauses { + final String MIMETYPE_IS_GROUP_MEMBERSHIP = MimetypesColumns.CONCRETE_MIMETYPE + "='" + + GroupMembership.CONTENT_ITEM_TYPE + "'"; + + final String BELONGS_TO_GROUP = DataColumns.CONCRETE_GROUP_ID + "=" + + GroupsColumns.CONCRETE_ID; + + final String HAVING_NO_GROUPS = "COUNT(" + DataColumns.CONCRETE_GROUP_ID + ") == 0"; + + final String GROUP_BY_ACCOUNT_CONTACT_ID = SettingsColumns.CONCRETE_ACCOUNT_NAME + "," + + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "," + RawContacts.CONTACT_ID; + + final String RAW_CONTACT_IS_LOCAL = RawContactsColumns.CONCRETE_ACCOUNT_NAME + + " IS NULL AND " + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL"; + + final String ZERO_GROUP_MEMBERSHIPS = "COUNT(" + GroupsColumns.CONCRETE_ID + ")=0"; + + final String OUTER_RAW_CONTACTS = "outer_raw_contacts"; + final String OUTER_RAW_CONTACTS_ID = OUTER_RAW_CONTACTS + "." + RawContacts._ID; + + final String CONTACT_IS_VISIBLE = + "SELECT " + + "MAX((SELECT (CASE WHEN " + + "(CASE" + + " WHEN " + RAW_CONTACT_IS_LOCAL + + " THEN 1 " + + " WHEN " + ZERO_GROUP_MEMBERSHIPS + + " THEN " + Settings.UNGROUPED_VISIBLE + + " ELSE MAX(" + Groups.GROUP_VISIBLE + ")" + + "END)=1 THEN 1 ELSE 0 END)" + + " FROM " + Tables.RAW_CONTACTS_JOIN_SETTINGS_DATA_GROUPS + + " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + OUTER_RAW_CONTACTS_ID + "))" + + " FROM " + Tables.RAW_CONTACTS + " AS " + OUTER_RAW_CONTACTS + + " WHERE " + RawContacts.CONTACT_ID + "=" + ContactsColumns.CONCRETE_ID + + " GROUP BY " + RawContacts.CONTACT_ID; + + final String GROUP_HAS_ACCOUNT_AND_SOURCE_ID = Groups.SOURCE_ID + "=? AND " + + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?"; + } + + public interface ContactsColumns { + /** + * This flag is set for a contact if it has only one constituent raw contact and + * it is restricted. + */ + public static final String SINGLE_IS_RESTRICTED = "single_is_restricted"; + + public static final String LAST_STATUS_UPDATE_ID = "status_update_id"; + + public static final String CONCRETE_ID = Tables.CONTACTS + "." + BaseColumns._ID; + public static final String CONCRETE_DISPLAY_NAME = Tables.CONTACTS + "." + + Contacts.DISPLAY_NAME; + + public static final String CONCRETE_TIMES_CONTACTED = Tables.CONTACTS + "." + + Contacts.TIMES_CONTACTED; + public static final String CONCRETE_LAST_TIME_CONTACTED = Tables.CONTACTS + "." + + Contacts.LAST_TIME_CONTACTED; + public static final String CONCRETE_STARRED = Tables.CONTACTS + "." + Contacts.STARRED; + public static final String CONCRETE_CUSTOM_RINGTONE = Tables.CONTACTS + "." + + Contacts.CUSTOM_RINGTONE; + public static final String CONCRETE_SEND_TO_VOICEMAIL = Tables.CONTACTS + "." + + Contacts.SEND_TO_VOICEMAIL; + } + + public interface RawContactsColumns { + public static final String CONCRETE_ID = + Tables.RAW_CONTACTS + "." + BaseColumns._ID; + public static final String CONCRETE_ACCOUNT_NAME = + Tables.RAW_CONTACTS + "." + RawContacts.ACCOUNT_NAME; + public static final String CONCRETE_ACCOUNT_TYPE = + Tables.RAW_CONTACTS + "." + RawContacts.ACCOUNT_TYPE; + public static final String CONCRETE_SOURCE_ID = + Tables.RAW_CONTACTS + "." + RawContacts.SOURCE_ID; + public static final String CONCRETE_VERSION = + Tables.RAW_CONTACTS + "." + RawContacts.VERSION; + public static final String CONCRETE_DIRTY = + Tables.RAW_CONTACTS + "." + RawContacts.DIRTY; + public static final String CONCRETE_DELETED = + Tables.RAW_CONTACTS + "." + RawContacts.DELETED; + public static final String CONCRETE_SYNC1 = + Tables.RAW_CONTACTS + "." + RawContacts.SYNC1; + public static final String CONCRETE_SYNC2 = + Tables.RAW_CONTACTS + "." + RawContacts.SYNC2; + public static final String CONCRETE_SYNC3 = + Tables.RAW_CONTACTS + "." + RawContacts.SYNC3; + public static final String CONCRETE_SYNC4 = + Tables.RAW_CONTACTS + "." + RawContacts.SYNC4; + public static final String CONCRETE_STARRED = + Tables.RAW_CONTACTS + "." + RawContacts.STARRED; + + public static final String DISPLAY_NAME = "display_name"; + public static final String DISPLAY_NAME_SOURCE = "display_name_source"; + public static final String AGGREGATION_NEEDED = "aggregation_needed"; + } + + /** + * Types of data used to produce the display name for a contact. Listed in the order + * of increasing priority. + */ + public interface DisplayNameSources { + int UNDEFINED = 0; + int EMAIL = 10; + int PHONE = 20; + int ORGANIZATION = 30; + int NICKNAME = 35; + int STRUCTURED_NAME = 40; + } + + public interface DataColumns { + public static final String PACKAGE_ID = "package_id"; + public static final String MIMETYPE_ID = "mimetype_id"; + + public static final String CONCRETE_ID = Tables.DATA + "." + BaseColumns._ID; + public static final String CONCRETE_MIMETYPE_ID = Tables.DATA + "." + MIMETYPE_ID; + public static final String CONCRETE_RAW_CONTACT_ID = Tables.DATA + "." + + Data.RAW_CONTACT_ID; + public static final String CONCRETE_GROUP_ID = Tables.DATA + "." + + GroupMembership.GROUP_ROW_ID; + + public static final String CONCRETE_DATA1 = Tables.DATA + "." + Data.DATA1; + public static final String CONCRETE_DATA2 = Tables.DATA + "." + Data.DATA2; + public static final String CONCRETE_DATA3 = Tables.DATA + "." + Data.DATA3; + public static final String CONCRETE_DATA4 = Tables.DATA + "." + Data.DATA4; + public static final String CONCRETE_DATA5 = Tables.DATA + "." + Data.DATA5; + public static final String CONCRETE_DATA6 = Tables.DATA + "." + Data.DATA6; + public static final String CONCRETE_DATA7 = Tables.DATA + "." + Data.DATA7; + public static final String CONCRETE_DATA8 = Tables.DATA + "." + Data.DATA8; + public static final String CONCRETE_DATA9 = Tables.DATA + "." + Data.DATA9; + public static final String CONCRETE_DATA10 = Tables.DATA + "." + Data.DATA10; + public static final String CONCRETE_DATA11 = Tables.DATA + "." + Data.DATA11; + public static final String CONCRETE_DATA12 = Tables.DATA + "." + Data.DATA12; + public static final String CONCRETE_DATA13 = Tables.DATA + "." + Data.DATA13; + public static final String CONCRETE_DATA14 = Tables.DATA + "." + Data.DATA14; + public static final String CONCRETE_DATA15 = Tables.DATA + "." + Data.DATA15; + public static final String CONCRETE_IS_PRIMARY = Tables.DATA + "." + Data.IS_PRIMARY; + public static final String CONCRETE_PACKAGE_ID = Tables.DATA + "." + PACKAGE_ID; + } + + // Used only for legacy API support + public interface ExtensionsColumns { + public static final String NAME = Data.DATA1; + public static final String VALUE = Data.DATA2; + } + + public interface GroupMembershipColumns { + public static final String RAW_CONTACT_ID = Data.RAW_CONTACT_ID; + public static final String GROUP_ROW_ID = GroupMembership.GROUP_ROW_ID; + } + + public interface PhoneColumns { + public static final String NORMALIZED_NUMBER = Data.DATA4; + public static final String CONCRETE_NORMALIZED_NUMBER = DataColumns.CONCRETE_DATA4; + } + + public interface GroupsColumns { + public static final String PACKAGE_ID = "package_id"; + + public static final String CONCRETE_ID = Tables.GROUPS + "." + BaseColumns._ID; + public static final String CONCRETE_SOURCE_ID = Tables.GROUPS + "." + Groups.SOURCE_ID; + public static final String CONCRETE_ACCOUNT_NAME = Tables.GROUPS + "." + Groups.ACCOUNT_NAME; + public static final String CONCRETE_ACCOUNT_TYPE = Tables.GROUPS + "." + Groups.ACCOUNT_TYPE; + } + + public interface ActivitiesColumns { + public static final String PACKAGE_ID = "package_id"; + public static final String MIMETYPE_ID = "mimetype_id"; + } + + public interface PhoneLookupColumns { + public static final String _ID = BaseColumns._ID; + public static final String DATA_ID = "data_id"; + public static final String RAW_CONTACT_ID = "raw_contact_id"; + public static final String NORMALIZED_NUMBER = "normalized_number"; + } + + public interface NameLookupColumns { + public static final String RAW_CONTACT_ID = "raw_contact_id"; + public static final String DATA_ID = "data_id"; + public static final String NORMALIZED_NAME = "normalized_name"; + public static final String NAME_TYPE = "name_type"; + } + + public final static class NameLookupType { + public static final int NAME_EXACT = 0; + public static final int NAME_VARIANT = 1; + public static final int NAME_COLLATION_KEY = 2; + public static final int NICKNAME = 3; + public static final int EMAIL_BASED_NICKNAME = 4; + public static final int ORGANIZATION = 5; + + // This is the highest name lookup type code plus one + public static final int TYPE_COUNT = 6; + + public static boolean isBasedOnStructuredName(int nameLookupType) { + return nameLookupType == NameLookupType.NAME_EXACT + || nameLookupType == NameLookupType.NAME_VARIANT + || nameLookupType == NameLookupType.NAME_COLLATION_KEY; + } + } + + public interface PackagesColumns { + public static final String _ID = BaseColumns._ID; + public static final String PACKAGE = "package"; + + public static final String CONCRETE_ID = Tables.PACKAGES + "." + _ID; + } + + public interface MimetypesColumns { + public static final String _ID = BaseColumns._ID; + public static final String MIMETYPE = "mimetype"; + + public static final String CONCRETE_ID = Tables.MIMETYPES + "." + BaseColumns._ID; + public static final String CONCRETE_MIMETYPE = Tables.MIMETYPES + "." + MIMETYPE; + } + + public interface AggregationExceptionColumns { + public static final String _ID = BaseColumns._ID; + } + + public interface NicknameLookupColumns { + public static final String NAME = "name"; + public static final String CLUSTER = "cluster"; + } + + public interface SettingsColumns { + public static final String CONCRETE_ACCOUNT_NAME = Tables.SETTINGS + "." + + Settings.ACCOUNT_NAME; + public static final String CONCRETE_ACCOUNT_TYPE = Tables.SETTINGS + "." + + Settings.ACCOUNT_TYPE; + } + + public interface PresenceColumns { + String RAW_CONTACT_ID = "presence_raw_contact_id"; + String CONTACT_ID = "presence_contact_id"; + } + + public interface AggregatedPresenceColumns { + String CONTACT_ID = "presence_contact_id"; + } + + public interface StatusUpdatesColumns { + String DATA_ID = "status_update_data_id"; + } + + /** In-memory cache of previously found mimetype mappings */ + private final HashMap mMimetypeCache = new HashMap(); + /** In-memory cache of previously found package name mappings */ + private final HashMap mPackageCache = new HashMap(); + + + /** Compiled statements for querying and inserting mappings */ + private SQLiteStatement mMimetypeQuery; + private SQLiteStatement mPackageQuery; + private SQLiteStatement mContactIdQuery; + private SQLiteStatement mAggregationModeQuery; + private SQLiteStatement mMimetypeInsert; + private SQLiteStatement mPackageInsert; + private SQLiteStatement mDataMimetypeQuery; + private SQLiteStatement mActivitiesMimetypeQuery; + + private final Context mContext; + private final SyncStateContentProviderHelper mSyncState; + + + /** Compiled statements for updating {@link Contacts#IN_VISIBLE_GROUP}. */ + private SQLiteStatement mVisibleUpdate; + private SQLiteStatement mVisibleSpecificUpdate; + + private boolean mReopenDatabase = false; + + private static ContactsDatabaseHelper sSingleton = null; + + private boolean mUseStrictPhoneNumberComparation; + + public static synchronized ContactsDatabaseHelper getInstance(Context context) { + if (sSingleton == null) { + sSingleton = new ContactsDatabaseHelper(context); + } + return sSingleton; + } + + /** + * Private constructor, callers except unit tests should obtain an instance through + * {@link #getInstance(android.content.Context)} instead. + */ + ContactsDatabaseHelper(Context context) { + super(context, DATABASE_NAME, null, DATABASE_VERSION); + Log.i(TAG, "Creating OpenHelper"); + + mContext = context; + mSyncState = new SyncStateContentProviderHelper(); + mUseStrictPhoneNumberComparation = + context.getResources().getBoolean( + com.android.internal.R.bool.config_use_strict_phone_number_comparation); + } + + @Override + public void onOpen(SQLiteDatabase db) { + mSyncState.onDatabaseOpened(db); + + // Create compiled statements for package and mimetype lookups + mMimetypeQuery = db.compileStatement("SELECT " + MimetypesColumns._ID + " FROM " + + Tables.MIMETYPES + " WHERE " + MimetypesColumns.MIMETYPE + "=?"); + mPackageQuery = db.compileStatement("SELECT " + PackagesColumns._ID + " FROM " + + Tables.PACKAGES + " WHERE " + PackagesColumns.PACKAGE + "=?"); + mContactIdQuery = db.compileStatement("SELECT " + RawContacts.CONTACT_ID + " FROM " + + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?"); + mAggregationModeQuery = db.compileStatement("SELECT " + RawContacts.AGGREGATION_MODE + + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?"); + mMimetypeInsert = db.compileStatement("INSERT INTO " + Tables.MIMETYPES + "(" + + MimetypesColumns.MIMETYPE + ") VALUES (?)"); + mPackageInsert = db.compileStatement("INSERT INTO " + Tables.PACKAGES + "(" + + PackagesColumns.PACKAGE + ") VALUES (?)"); + + mDataMimetypeQuery = db.compileStatement("SELECT " + MimetypesColumns.MIMETYPE + " FROM " + + Tables.DATA_JOIN_MIMETYPES + " WHERE " + Tables.DATA + "." + Data._ID + "=?"); + mActivitiesMimetypeQuery = db.compileStatement("SELECT " + MimetypesColumns.MIMETYPE + + " FROM " + Tables.ACTIVITIES_JOIN_MIMETYPES + " WHERE " + Tables.ACTIVITIES + "." + + Activities._ID + "=?"); + + // Compile statements for updating visibility + final String visibleUpdate = "UPDATE " + Tables.CONTACTS + " SET " + + Contacts.IN_VISIBLE_GROUP + "=(" + Clauses.CONTACT_IS_VISIBLE + ")"; + + mVisibleUpdate = db.compileStatement(visibleUpdate); + mVisibleSpecificUpdate = db.compileStatement(visibleUpdate + " WHERE " + + ContactsColumns.CONCRETE_ID + "=?"); + + db.execSQL("ATTACH DATABASE ':memory:' AS " + DATABASE_PRESENCE + ";"); + db.execSQL("CREATE TABLE IF NOT EXISTS " + DATABASE_PRESENCE + "." + Tables.PRESENCE + " ("+ + StatusUpdates.DATA_ID + " INTEGER PRIMARY KEY REFERENCES data(_id)," + + StatusUpdates.PROTOCOL + " INTEGER NOT NULL," + + StatusUpdates.CUSTOM_PROTOCOL + " TEXT," + + StatusUpdates.IM_HANDLE + " TEXT," + + StatusUpdates.IM_ACCOUNT + " TEXT," + + PresenceColumns.CONTACT_ID + " INTEGER REFERENCES contacts(_id)," + + PresenceColumns.RAW_CONTACT_ID + " INTEGER REFERENCES raw_contacts(_id)," + + StatusUpdates.PRESENCE + " INTEGER," + + "UNIQUE(" + StatusUpdates.PROTOCOL + ", " + StatusUpdates.CUSTOM_PROTOCOL + + ", " + StatusUpdates.IM_HANDLE + ", " + StatusUpdates.IM_ACCOUNT + ")" + + ");"); + + db.execSQL("CREATE INDEX IF NOT EXISTS " + DATABASE_PRESENCE + ".presenceIndex" + " ON " + + Tables.PRESENCE + " (" + PresenceColumns.RAW_CONTACT_ID + ");"); + + db.execSQL("CREATE TABLE IF NOT EXISTS " + + DATABASE_PRESENCE + "." + Tables.AGGREGATED_PRESENCE + " ("+ + AggregatedPresenceColumns.CONTACT_ID + + " INTEGER PRIMARY KEY REFERENCES contacts(_id)," + + StatusUpdates.PRESENCE_STATUS + " INTEGER" + + ");"); + + + db.execSQL("CREATE TRIGGER " + DATABASE_PRESENCE + "." + Tables.PRESENCE + "_deleted" + + " BEFORE DELETE ON " + DATABASE_PRESENCE + "." + Tables.PRESENCE + + " BEGIN " + + " DELETE FROM " + Tables.AGGREGATED_PRESENCE + + " WHERE " + AggregatedPresenceColumns.CONTACT_ID + " = " + + "(SELECT " + PresenceColumns.CONTACT_ID + + " FROM " + Tables.PRESENCE + + " WHERE " + PresenceColumns.RAW_CONTACT_ID + + "=OLD." + PresenceColumns.RAW_CONTACT_ID + + " AND NOT EXISTS" + + "(SELECT " + PresenceColumns.RAW_CONTACT_ID + + " FROM " + Tables.PRESENCE + + " WHERE " + PresenceColumns.CONTACT_ID + + "=OLD." + PresenceColumns.CONTACT_ID + + " AND " + PresenceColumns.RAW_CONTACT_ID + + "!=OLD." + PresenceColumns.RAW_CONTACT_ID + "));" + + " END"); + + String replaceAggregatePresenceSql = + "INSERT OR REPLACE INTO " + Tables.AGGREGATED_PRESENCE + "(" + + AggregatedPresenceColumns.CONTACT_ID + ", " + + StatusUpdates.PRESENCE_STATUS + ")" + + " SELECT " + PresenceColumns.CONTACT_ID + "," + + "MAX(" + StatusUpdates.PRESENCE_STATUS + ")" + + " FROM " + Tables.PRESENCE + + " WHERE " + PresenceColumns.CONTACT_ID + + "=NEW." + PresenceColumns.CONTACT_ID + ";"; + + db.execSQL("CREATE TRIGGER " + DATABASE_PRESENCE + "." + Tables.PRESENCE + "_inserted" + + " AFTER INSERT ON " + DATABASE_PRESENCE + "." + Tables.PRESENCE + + " BEGIN " + + replaceAggregatePresenceSql + + " END"); + + db.execSQL("CREATE TRIGGER " + DATABASE_PRESENCE + "." + Tables.PRESENCE + "_updated" + + " AFTER UPDATE ON " + DATABASE_PRESENCE + "." + Tables.PRESENCE + + " BEGIN " + + replaceAggregatePresenceSql + + " END"); + } + + @Override + public void onCreate(SQLiteDatabase db) { + Log.i(TAG, "Bootstrapping database"); + + mSyncState.createDatabase(db); + + // One row per group of contacts corresponding to the same person + db.execSQL("CREATE TABLE " + Tables.CONTACTS + " (" + + BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + + Contacts.DISPLAY_NAME + " TEXT," + + Contacts.PHOTO_ID + " INTEGER REFERENCES data(_id)," + + Contacts.CUSTOM_RINGTONE + " TEXT," + + Contacts.SEND_TO_VOICEMAIL + " INTEGER NOT NULL DEFAULT 0," + + Contacts.TIMES_CONTACTED + " INTEGER NOT NULL DEFAULT 0," + + Contacts.LAST_TIME_CONTACTED + " INTEGER," + + Contacts.STARRED + " INTEGER NOT NULL DEFAULT 0," + + Contacts.IN_VISIBLE_GROUP + " INTEGER NOT NULL DEFAULT 1," + + Contacts.HAS_PHONE_NUMBER + " INTEGER NOT NULL DEFAULT 0," + + Contacts.LOOKUP_KEY + " TEXT," + + ContactsColumns.LAST_STATUS_UPDATE_ID + " INTEGER REFERENCES data(_id)," + + ContactsColumns.SINGLE_IS_RESTRICTED + " INTEGER NOT NULL DEFAULT 0" + + ");"); + + db.execSQL("CREATE TRIGGER contacts_times_contacted UPDATE OF " + + Contacts.LAST_TIME_CONTACTED + " ON " + Tables.CONTACTS + " " + + "BEGIN " + + "UPDATE " + Tables.CONTACTS + " SET " + + Contacts.TIMES_CONTACTED + " = " + "" + + "(new." + Contacts.TIMES_CONTACTED + " + 1)" + + " WHERE _id = new._id;" + + "END"); + + db.execSQL("CREATE INDEX contacts_visible_index ON " + Tables.CONTACTS + " (" + + Contacts.IN_VISIBLE_GROUP + "," + + Contacts.DISPLAY_NAME + " COLLATE LOCALIZED" + + ");"); + + db.execSQL("CREATE INDEX contacts_has_phone_index ON " + Tables.CONTACTS + " (" + + Contacts.HAS_PHONE_NUMBER + + ");"); + + db.execSQL("CREATE INDEX contacts_restricted_index ON " + Tables.CONTACTS + " (" + + ContactsColumns.SINGLE_IS_RESTRICTED + + ");"); + + // Contacts table + db.execSQL("CREATE TABLE " + Tables.RAW_CONTACTS + " (" + + RawContacts._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + + RawContacts.IS_RESTRICTED + " INTEGER DEFAULT 0," + + RawContacts.ACCOUNT_NAME + " STRING DEFAULT NULL, " + + RawContacts.ACCOUNT_TYPE + " STRING DEFAULT NULL, " + + RawContacts.SOURCE_ID + " TEXT," + + RawContacts.VERSION + " INTEGER NOT NULL DEFAULT 1," + + RawContacts.DIRTY + " INTEGER NOT NULL DEFAULT 0," + + RawContacts.DELETED + " INTEGER NOT NULL DEFAULT 0," + + RawContacts.CONTACT_ID + " INTEGER REFERENCES contacts(_id)," + + RawContacts.AGGREGATION_MODE + " INTEGER NOT NULL DEFAULT " + + RawContacts.AGGREGATION_MODE_DEFAULT + "," + + RawContactsColumns.AGGREGATION_NEEDED + " INTEGER NOT NULL DEFAULT 1," + + RawContacts.CUSTOM_RINGTONE + " TEXT," + + RawContacts.SEND_TO_VOICEMAIL + " INTEGER NOT NULL DEFAULT 0," + + RawContacts.TIMES_CONTACTED + " INTEGER NOT NULL DEFAULT 0," + + RawContacts.LAST_TIME_CONTACTED + " INTEGER," + + RawContacts.STARRED + " INTEGER NOT NULL DEFAULT 0," + + RawContactsColumns.DISPLAY_NAME + " TEXT," + + RawContactsColumns.DISPLAY_NAME_SOURCE + " INTEGER NOT NULL DEFAULT " + + DisplayNameSources.UNDEFINED + "," + + RawContacts.SYNC1 + " TEXT, " + + RawContacts.SYNC2 + " TEXT, " + + RawContacts.SYNC3 + " TEXT, " + + RawContacts.SYNC4 + " TEXT " + + ");"); + + db.execSQL("CREATE TRIGGER raw_contacts_times_contacted UPDATE OF " + + RawContacts.LAST_TIME_CONTACTED + " ON " + Tables.RAW_CONTACTS + " " + + "BEGIN " + + "UPDATE " + Tables.RAW_CONTACTS + " SET " + + RawContacts.TIMES_CONTACTED + " = " + "" + + "(new." + RawContacts.TIMES_CONTACTED + " + 1)" + + " WHERE _id = new._id;" + + "END"); + + db.execSQL("CREATE INDEX raw_contacts_contact_id_index ON " + Tables.RAW_CONTACTS + " (" + + RawContacts.CONTACT_ID + + ");"); + + db.execSQL("CREATE INDEX raw_contacts_source_id_index ON " + Tables.RAW_CONTACTS + " (" + + RawContacts.SOURCE_ID + ", " + + RawContacts.ACCOUNT_TYPE + ", " + + RawContacts.ACCOUNT_NAME + + ");"); + + // TODO readd the index and investigate a controlled use of it +// db.execSQL("CREATE INDEX raw_contacts_agg_index ON " + Tables.RAW_CONTACTS + " (" + +// RawContactsColumns.AGGREGATION_NEEDED + +// ");"); + + // Package name mapping table + db.execSQL("CREATE TABLE " + Tables.PACKAGES + " (" + + PackagesColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + + PackagesColumns.PACKAGE + " TEXT NOT NULL" + + ");"); + + // Mimetype mapping table + db.execSQL("CREATE TABLE " + Tables.MIMETYPES + " (" + + MimetypesColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + + MimetypesColumns.MIMETYPE + " TEXT NOT NULL" + + ");"); + + // Public generic data table + db.execSQL("CREATE TABLE " + Tables.DATA + " (" + + Data._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + + DataColumns.PACKAGE_ID + " INTEGER REFERENCES package(_id)," + + DataColumns.MIMETYPE_ID + " INTEGER REFERENCES mimetype(_id) NOT NULL," + + Data.RAW_CONTACT_ID + " INTEGER REFERENCES raw_contacts(_id) NOT NULL," + + Data.IS_PRIMARY + " INTEGER NOT NULL DEFAULT 0," + + Data.IS_SUPER_PRIMARY + " INTEGER NOT NULL DEFAULT 0," + + Data.DATA_VERSION + " INTEGER NOT NULL DEFAULT 0," + + Data.DATA1 + " TEXT," + + Data.DATA2 + " TEXT," + + Data.DATA3 + " TEXT," + + Data.DATA4 + " TEXT," + + Data.DATA5 + " TEXT," + + Data.DATA6 + " TEXT," + + Data.DATA7 + " TEXT," + + Data.DATA8 + " TEXT," + + Data.DATA9 + " TEXT," + + Data.DATA10 + " TEXT," + + Data.DATA11 + " TEXT," + + Data.DATA12 + " TEXT," + + Data.DATA13 + " TEXT," + + Data.DATA14 + " TEXT," + + Data.DATA15 + " TEXT," + + Data.SYNC1 + " TEXT, " + + Data.SYNC2 + " TEXT, " + + Data.SYNC3 + " TEXT, " + + Data.SYNC4 + " TEXT " + + ");"); + + db.execSQL("CREATE INDEX data_raw_contact_id ON " + Tables.DATA + " (" + + Data.RAW_CONTACT_ID + + ");"); + + /** + * For email lookup and similar queries. + */ + db.execSQL("CREATE INDEX data_mimetype_data1_index ON " + Tables.DATA + " (" + + DataColumns.MIMETYPE_ID + "," + + Data.DATA1 + + ");"); + + /** + * Automatically delete Data rows when a raw contact is deleted. + */ + db.execSQL("CREATE TRIGGER " + Tables.RAW_CONTACTS + "_deleted " + + " BEFORE DELETE ON " + Tables.RAW_CONTACTS + + " BEGIN " + + " DELETE FROM " + Tables.DATA + + " WHERE " + Data.RAW_CONTACT_ID + + "=OLD." + RawContacts._ID + ";" + + " DELETE FROM " + Tables.PHONE_LOOKUP + + " WHERE " + PhoneLookupColumns.RAW_CONTACT_ID + + "=OLD." + RawContacts._ID + ";" + + " DELETE FROM " + Tables.NAME_LOOKUP + + " WHERE " + NameLookupColumns.RAW_CONTACT_ID + + "=OLD." + RawContacts._ID + ";" + + " DELETE FROM " + Tables.AGGREGATION_EXCEPTIONS + + " WHERE " + AggregationExceptions.RAW_CONTACT_ID1 + + "=OLD." + RawContacts._ID + + " OR " + AggregationExceptions.RAW_CONTACT_ID2 + + "=OLD." + RawContacts._ID + ";" + + " DELETE FROM " + Tables.CONTACTS + + " WHERE " + Contacts._ID + "=OLD." + RawContacts.CONTACT_ID + + " AND (SELECT COUNT(*) FROM " + Tables.RAW_CONTACTS + + " WHERE " + RawContacts.CONTACT_ID + "=OLD." + RawContacts.CONTACT_ID + + " )=1;" + + " END"); + + /** + * Triggers that update {@link RawContacts#VERSION} when the contact is + * marked for deletion or any time a data row is inserted, updated or + * deleted. + */ + db.execSQL("CREATE TRIGGER " + Tables.RAW_CONTACTS + "_marked_deleted " + + " BEFORE UPDATE ON " + Tables.RAW_CONTACTS + + " BEGIN " + + " UPDATE " + Tables.RAW_CONTACTS + + " SET " + + RawContacts.VERSION + "=OLD." + RawContacts.VERSION + "+1 " + + " WHERE " + RawContacts._ID + "=OLD." + RawContacts._ID + + " AND NEW." + RawContacts.DELETED + "!= OLD." + RawContacts.DELETED + ";" + + " END"); + + db.execSQL("CREATE TRIGGER " + Tables.DATA + "_updated BEFORE UPDATE ON " + Tables.DATA + + " BEGIN " + + " UPDATE " + Tables.DATA + + " SET " + Data.DATA_VERSION + "=OLD." + Data.DATA_VERSION + "+1 " + + " WHERE " + Data._ID + "=OLD." + Data._ID + ";" + + " UPDATE " + Tables.RAW_CONTACTS + + " SET " + RawContacts.VERSION + "=" + RawContacts.VERSION + "+1 " + + " WHERE " + RawContacts._ID + "=OLD." + Data.RAW_CONTACT_ID + ";" + + " END"); + + db.execSQL("CREATE TRIGGER " + Tables.DATA + "_deleted BEFORE DELETE ON " + Tables.DATA + + " BEGIN " + + " UPDATE " + Tables.RAW_CONTACTS + + " SET " + RawContacts.VERSION + "=" + RawContacts.VERSION + "+1 " + + " WHERE " + RawContacts._ID + "=OLD." + Data.RAW_CONTACT_ID + ";" + + " DELETE FROM " + Tables.PHONE_LOOKUP + + " WHERE " + PhoneLookupColumns.DATA_ID + "=OLD." + Data._ID + ";" + + " END"); + + // Private phone numbers table used for lookup + db.execSQL("CREATE TABLE " + Tables.PHONE_LOOKUP + " (" + + PhoneLookupColumns.DATA_ID + + " INTEGER PRIMARY KEY REFERENCES data(_id) NOT NULL," + + PhoneLookupColumns.RAW_CONTACT_ID + + " INTEGER REFERENCES raw_contacts(_id) NOT NULL," + + PhoneLookupColumns.NORMALIZED_NUMBER + " TEXT NOT NULL" + + ");"); + + db.execSQL("CREATE INDEX phone_lookup_index ON " + Tables.PHONE_LOOKUP + " (" + + PhoneLookupColumns.NORMALIZED_NUMBER + "," + + PhoneLookupColumns.RAW_CONTACT_ID + "," + + PhoneLookupColumns.DATA_ID + + ");"); + + // Private name/nickname table used for lookup + db.execSQL("CREATE TABLE " + Tables.NAME_LOOKUP + " (" + + NameLookupColumns.DATA_ID + + " INTEGER REFERENCES data(_id) NOT NULL," + + NameLookupColumns.RAW_CONTACT_ID + + " INTEGER REFERENCES raw_contacts(_id) NOT NULL," + + NameLookupColumns.NORMALIZED_NAME + " TEXT NOT NULL," + + NameLookupColumns.NAME_TYPE + " INTEGER NOT NULL," + + "PRIMARY KEY (" + + NameLookupColumns.DATA_ID + ", " + + NameLookupColumns.NORMALIZED_NAME + ", " + + NameLookupColumns.NAME_TYPE + ")" + + ");"); + + db.execSQL("CREATE INDEX name_lookup_index ON " + Tables.NAME_LOOKUP + " (" + + NameLookupColumns.NORMALIZED_NAME + "," + + NameLookupColumns.NAME_TYPE + ", " + + NameLookupColumns.RAW_CONTACT_ID + + ");"); + + db.execSQL("CREATE INDEX name_lookup_raw_contact_id_index ON " + Tables.NAME_LOOKUP + " (" + + NameLookupColumns.RAW_CONTACT_ID + + ");"); + + db.execSQL("CREATE TABLE " + Tables.NICKNAME_LOOKUP + " (" + + NicknameLookupColumns.NAME + " TEXT," + + NicknameLookupColumns.CLUSTER + " TEXT" + + ");"); + + db.execSQL("CREATE UNIQUE INDEX nickname_lookup_index ON " + Tables.NICKNAME_LOOKUP + " (" + + NicknameLookupColumns.NAME + ", " + + NicknameLookupColumns.CLUSTER + + ");"); + + // Groups table + db.execSQL("CREATE TABLE " + Tables.GROUPS + " (" + + Groups._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + + GroupsColumns.PACKAGE_ID + " INTEGER REFERENCES package(_id)," + + Groups.ACCOUNT_NAME + " STRING DEFAULT NULL, " + + Groups.ACCOUNT_TYPE + " STRING DEFAULT NULL, " + + Groups.SOURCE_ID + " TEXT," + + Groups.VERSION + " INTEGER NOT NULL DEFAULT 1," + + Groups.DIRTY + " INTEGER NOT NULL DEFAULT 0," + + Groups.TITLE + " TEXT," + + Groups.TITLE_RES + " INTEGER," + + Groups.NOTES + " TEXT," + + Groups.SYSTEM_ID + " TEXT," + + Groups.DELETED + " INTEGER NOT NULL DEFAULT 0," + + Groups.GROUP_VISIBLE + " INTEGER NOT NULL DEFAULT 0," + + Groups.SHOULD_SYNC + " INTEGER NOT NULL DEFAULT 1," + + Groups.SYNC1 + " TEXT, " + + Groups.SYNC2 + " TEXT, " + + Groups.SYNC3 + " TEXT, " + + Groups.SYNC4 + " TEXT " + + ");"); + + db.execSQL("CREATE INDEX groups_source_id_index ON " + Tables.GROUPS + " (" + + Groups.SOURCE_ID + ", " + + Groups.ACCOUNT_TYPE + ", " + + Groups.ACCOUNT_NAME + + ");"); + + db.execSQL("CREATE TRIGGER " + Tables.GROUPS + "_updated1 " + + " BEFORE UPDATE ON " + Tables.GROUPS + + " BEGIN " + + " UPDATE " + Tables.GROUPS + + " SET " + + Groups.VERSION + "=OLD." + Groups.VERSION + "+1" + + " WHERE " + Groups._ID + "=OLD." + Groups._ID + ";" + + " END"); + + db.execSQL("CREATE TABLE IF NOT EXISTS " + Tables.AGGREGATION_EXCEPTIONS + " (" + + AggregationExceptionColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + + AggregationExceptions.TYPE + " INTEGER NOT NULL, " + + AggregationExceptions.RAW_CONTACT_ID1 + + " INTEGER REFERENCES raw_contacts(_id), " + + AggregationExceptions.RAW_CONTACT_ID2 + + " INTEGER REFERENCES raw_contacts(_id)" + + ");"); + + db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS aggregation_exception_index1 ON " + + Tables.AGGREGATION_EXCEPTIONS + " (" + + AggregationExceptions.RAW_CONTACT_ID1 + ", " + + AggregationExceptions.RAW_CONTACT_ID2 + + ");"); + + db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS aggregation_exception_index2 ON " + + Tables.AGGREGATION_EXCEPTIONS + " (" + + AggregationExceptions.RAW_CONTACT_ID2 + ", " + + AggregationExceptions.RAW_CONTACT_ID1 + + ");"); + + db.execSQL("CREATE TABLE IF NOT EXISTS " + Tables.SETTINGS + " (" + + Settings.ACCOUNT_NAME + " STRING NOT NULL," + + Settings.ACCOUNT_TYPE + " STRING NOT NULL," + + Settings.UNGROUPED_VISIBLE + " INTEGER NOT NULL DEFAULT 0," + + Settings.SHOULD_SYNC + " INTEGER NOT NULL DEFAULT 1, " + + "PRIMARY KEY (" + Settings.ACCOUNT_NAME + ", " + + Settings.ACCOUNT_TYPE + ") ON CONFLICT REPLACE" + + ");"); + + // The table for recent calls is here so we can do table joins + // on people, phones, and calls all in one place. + db.execSQL("CREATE TABLE " + Tables.CALLS + " (" + + Calls._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + + Calls.NUMBER + " TEXT," + + Calls.DATE + " INTEGER," + + Calls.DURATION + " INTEGER," + + Calls.TYPE + " INTEGER," + + Calls.NEW + " INTEGER," + + Calls.CACHED_NAME + " TEXT," + + Calls.CACHED_NUMBER_TYPE + " INTEGER," + + Calls.CACHED_NUMBER_LABEL + " TEXT" + + ");"); + + // Activities table + db.execSQL("CREATE TABLE " + Tables.ACTIVITIES + " (" + + Activities._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + + ActivitiesColumns.PACKAGE_ID + " INTEGER REFERENCES package(_id)," + + ActivitiesColumns.MIMETYPE_ID + " INTEGER REFERENCES mimetype(_id) NOT NULL," + + Activities.RAW_ID + " TEXT," + + Activities.IN_REPLY_TO + " TEXT," + + Activities.AUTHOR_CONTACT_ID + " INTEGER REFERENCES raw_contacts(_id)," + + Activities.TARGET_CONTACT_ID + " INTEGER REFERENCES raw_contacts(_id)," + + Activities.PUBLISHED + " INTEGER NOT NULL," + + Activities.THREAD_PUBLISHED + " INTEGER NOT NULL," + + Activities.TITLE + " TEXT NOT NULL," + + Activities.SUMMARY + " TEXT," + + Activities.LINK + " TEXT, " + + Activities.THUMBNAIL + " BLOB" + + ");"); + + db.execSQL("CREATE TABLE " + Tables.STATUS_UPDATES + " (" + + StatusUpdatesColumns.DATA_ID + " INTEGER PRIMARY KEY REFERENCES data(_id)," + + StatusUpdates.STATUS + " TEXT," + + StatusUpdates.STATUS_TIMESTAMP + " INTEGER," + + StatusUpdates.STATUS_RES_PACKAGE + " TEXT, " + + StatusUpdates.STATUS_LABEL + " INTEGER, " + + StatusUpdates.STATUS_ICON + " INTEGER" + + ");"); + + String contactEntitiesSelect = "SELECT " + + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AS " + RawContacts.ACCOUNT_NAME + "," + + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AS " + RawContacts.ACCOUNT_TYPE + "," + + RawContactsColumns.CONCRETE_SOURCE_ID + " AS " + RawContacts.SOURCE_ID + "," + + RawContactsColumns.CONCRETE_VERSION + " AS " + RawContacts.VERSION + "," + + RawContactsColumns.CONCRETE_DIRTY + " AS " + RawContacts.DIRTY + "," + + RawContactsColumns.CONCRETE_DELETED + " AS " + RawContacts.DELETED + "," + + PackagesColumns.PACKAGE + " AS " + Data.RES_PACKAGE + "," + + RawContacts.CONTACT_ID + ", " + + RawContactsColumns.CONCRETE_SYNC1 + " AS " + RawContacts.SYNC1 + ", " + + RawContactsColumns.CONCRETE_SYNC2 + " AS " + RawContacts.SYNC2 + ", " + + RawContactsColumns.CONCRETE_SYNC3 + " AS " + RawContacts.SYNC3 + ", " + + RawContactsColumns.CONCRETE_SYNC4 + " AS " + RawContacts.SYNC4 + ", " + + Data.MIMETYPE + ", " + + Data.DATA1 + ", " + + Data.DATA2 + ", " + + Data.DATA3 + ", " + + Data.DATA4 + ", " + + Data.DATA5 + ", " + + Data.DATA6 + ", " + + Data.DATA7 + ", " + + Data.DATA8 + ", " + + Data.DATA9 + ", " + + Data.DATA10 + ", " + + Data.DATA11 + ", " + + Data.DATA12 + ", " + + Data.DATA13 + ", " + + Data.DATA14 + ", " + + Data.DATA15 + ", " + + Data.SYNC1 + ", " + + Data.SYNC2 + ", " + + Data.SYNC3 + ", " + + Data.SYNC4 + ", " + + RawContactsColumns.CONCRETE_ID + " AS " + Data.RAW_CONTACT_ID + ", " + + Data.IS_PRIMARY + ", " + + Data.IS_SUPER_PRIMARY + ", " + + Data.DATA_VERSION + ", " + + DataColumns.CONCRETE_ID + " AS " + RawContacts._ID + "," + + RawContactsColumns.CONCRETE_STARRED + " AS " + RawContacts.STARRED + "," + + Tables.GROUPS + "." + Groups.SOURCE_ID + " AS " + GroupMembership.GROUP_SOURCE_ID + + " FROM " + Tables.RAW_CONTACTS + + " LEFT OUTER JOIN " + Tables.DATA + " ON (" + + DataColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID + ")" + + " LEFT OUTER JOIN " + Tables.PACKAGES + " ON (" + + DataColumns.CONCRETE_PACKAGE_ID + "=" + PackagesColumns.CONCRETE_ID + ")" + + " LEFT OUTER JOIN " + Tables.MIMETYPES + " ON (" + + DataColumns.CONCRETE_MIMETYPE_ID + "=" + MimetypesColumns.CONCRETE_ID + ")" + + " LEFT OUTER JOIN " + Tables.GROUPS + " ON (" + + MimetypesColumns.CONCRETE_MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE + + "' AND " + GroupsColumns.CONCRETE_ID + "=" + + Tables.DATA + "." + GroupMembership.GROUP_ROW_ID + ")"; + + db.execSQL("CREATE VIEW " + Tables.CONTACT_ENTITIES + " AS " + + contactEntitiesSelect); + db.execSQL("CREATE VIEW " + Tables.CONTACT_ENTITIES_RESTRICTED + " AS " + + contactEntitiesSelect + " WHERE " + RawContacts.IS_RESTRICTED + "=0"); + + String dataColumns = + Data.IS_PRIMARY + ", " + + Data.IS_SUPER_PRIMARY + ", " + + Data.DATA_VERSION + ", " + + PackagesColumns.PACKAGE + " AS " + Data.RES_PACKAGE + "," + + MimetypesColumns.MIMETYPE + " AS " + Data.MIMETYPE + ", " + + Data.DATA1 + ", " + + Data.DATA2 + ", " + + Data.DATA3 + ", " + + Data.DATA4 + ", " + + Data.DATA5 + ", " + + Data.DATA6 + ", " + + Data.DATA7 + ", " + + Data.DATA8 + ", " + + Data.DATA9 + ", " + + Data.DATA10 + ", " + + Data.DATA11 + ", " + + Data.DATA12 + ", " + + Data.DATA13 + ", " + + Data.DATA14 + ", " + + Data.DATA15 + ", " + + Data.SYNC1 + ", " + + Data.SYNC2 + ", " + + Data.SYNC3 + ", " + + Data.SYNC4; + + String syncColumns = + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AS " + RawContacts.ACCOUNT_NAME + "," + + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AS " + RawContacts.ACCOUNT_TYPE + "," + + RawContactsColumns.CONCRETE_SOURCE_ID + " AS " + RawContacts.SOURCE_ID + "," + + RawContactsColumns.CONCRETE_VERSION + " AS " + RawContacts.VERSION + "," + + RawContactsColumns.CONCRETE_DIRTY + " AS " + RawContacts.DIRTY + "," + + RawContactsColumns.CONCRETE_SYNC1 + " AS " + RawContacts.SYNC1 + "," + + RawContactsColumns.CONCRETE_SYNC2 + " AS " + RawContacts.SYNC2 + "," + + RawContactsColumns.CONCRETE_SYNC3 + " AS " + RawContacts.SYNC3 + "," + + RawContactsColumns.CONCRETE_SYNC4 + " AS " + RawContacts.SYNC4; + + String contactOptionColumns = + ContactsColumns.CONCRETE_CUSTOM_RINGTONE + + " AS " + RawContacts.CUSTOM_RINGTONE + "," + + ContactsColumns.CONCRETE_SEND_TO_VOICEMAIL + + " AS " + RawContacts.SEND_TO_VOICEMAIL + "," + + ContactsColumns.CONCRETE_LAST_TIME_CONTACTED + + " AS " + RawContacts.LAST_TIME_CONTACTED + "," + + ContactsColumns.CONCRETE_TIMES_CONTACTED + + " AS " + RawContacts.TIMES_CONTACTED + "," + + ContactsColumns.CONCRETE_STARRED + + " AS " + RawContacts.STARRED; + + String dataSelect = "SELECT " + + DataColumns.CONCRETE_ID + " AS " + Data._ID + "," + + Data.RAW_CONTACT_ID + ", " + + RawContacts.CONTACT_ID + ", " + + syncColumns + ", " + + dataColumns + ", " + + contactOptionColumns + ", " + + ContactsColumns.CONCRETE_DISPLAY_NAME + " AS " + Contacts.DISPLAY_NAME + ", " + + Contacts.LOOKUP_KEY + ", " + + Contacts.PHOTO_ID + ", " + + ContactsColumns.LAST_STATUS_UPDATE_ID + ", " + + Tables.GROUPS + "." + Groups.SOURCE_ID + " AS " + GroupMembership.GROUP_SOURCE_ID + + " FROM " + Tables.DATA + + " LEFT OUTER JOIN " + Tables.PACKAGES + " ON (" + + DataColumns.CONCRETE_PACKAGE_ID + "=" + PackagesColumns.CONCRETE_ID + ")" + + " LEFT OUTER JOIN " + Tables.MIMETYPES + " ON (" + + DataColumns.CONCRETE_MIMETYPE_ID + "=" + MimetypesColumns.CONCRETE_ID + ")" + + " LEFT OUTER JOIN " + Tables.RAW_CONTACTS + " ON (" + + DataColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID + ")" + + " LEFT OUTER JOIN " + Tables.GROUPS + " ON (" + + MimetypesColumns.CONCRETE_MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE + + "' AND " + GroupsColumns.CONCRETE_ID + "=" + + Tables.DATA + "." + GroupMembership.GROUP_ROW_ID + ")" + + " LEFT OUTER JOIN " + Tables.CONTACTS + " ON (" + + RawContacts.CONTACT_ID + "=" + Tables.CONTACTS + "." + Contacts._ID + ")"; + + db.execSQL("CREATE VIEW " + Views.DATA_ALL + " AS " + dataSelect); + db.execSQL("CREATE VIEW " + Views.DATA_RESTRICTED + " AS " + dataSelect + " WHERE " + + RawContacts.IS_RESTRICTED + "=0"); + + String rawContactOptionColumns = + RawContacts.CUSTOM_RINGTONE + "," + + RawContacts.SEND_TO_VOICEMAIL + "," + + RawContacts.LAST_TIME_CONTACTED + "," + + RawContacts.TIMES_CONTACTED + "," + + RawContacts.STARRED; + + String rawContactsSelect = "SELECT " + + RawContactsColumns.CONCRETE_ID + " AS " + RawContacts._ID + "," + + RawContacts.CONTACT_ID + ", " + + RawContacts.AGGREGATION_MODE + ", " + + RawContacts.DELETED + ", " + + rawContactOptionColumns + ", " + + syncColumns + + " FROM " + Tables.RAW_CONTACTS; + + db.execSQL("CREATE VIEW " + Views.RAW_CONTACTS_ALL + " AS " + rawContactsSelect); + db.execSQL("CREATE VIEW " + Views.RAW_CONTACTS_RESTRICTED + " AS " + rawContactsSelect + + " WHERE " + RawContacts.IS_RESTRICTED + "=0"); + + String contactsColumns = + ContactsColumns.CONCRETE_CUSTOM_RINGTONE + + " AS " + Contacts.CUSTOM_RINGTONE + ", " + + ContactsColumns.CONCRETE_DISPLAY_NAME + + " AS " + Contacts.DISPLAY_NAME + ", " + + Contacts.IN_VISIBLE_GROUP + ", " + + Contacts.HAS_PHONE_NUMBER + ", " + + Contacts.LOOKUP_KEY + ", " + + Contacts.PHOTO_ID + ", " + + ContactsColumns.CONCRETE_LAST_TIME_CONTACTED + + " AS " + Contacts.LAST_TIME_CONTACTED + ", " + + ContactsColumns.CONCRETE_SEND_TO_VOICEMAIL + + " AS " + Contacts.SEND_TO_VOICEMAIL + ", " + + ContactsColumns.CONCRETE_STARRED + + " AS " + Contacts.STARRED + ", " + + ContactsColumns.CONCRETE_TIMES_CONTACTED + + " AS " + Contacts.TIMES_CONTACTED + ", " + + ContactsColumns.LAST_STATUS_UPDATE_ID; + + String contactsSelect = "SELECT " + + ContactsColumns.CONCRETE_ID + " AS " + Contacts._ID + "," + + contactsColumns + + " FROM " + Tables.CONTACTS; + + String restrictedContactsSelect = "SELECT " + + ContactsColumns.CONCRETE_ID + " AS " + Contacts._ID + "," + + contactsColumns + + " FROM " + Tables.CONTACTS + + " WHERE " + ContactsColumns.SINGLE_IS_RESTRICTED + "=0"; + + db.execSQL("CREATE VIEW " + Views.CONTACTS_ALL + " AS " + contactsSelect); + db.execSQL("CREATE VIEW " + Views.CONTACTS_RESTRICTED + " AS " + restrictedContactsSelect); + + String groupsColumns = + Groups.ACCOUNT_NAME + "," + + Groups.ACCOUNT_TYPE + "," + + Groups.SOURCE_ID + "," + + Groups.VERSION + "," + + Groups.DIRTY + "," + + Groups.TITLE + "," + + Groups.TITLE_RES + "," + + Groups.NOTES + "," + + Groups.SYSTEM_ID + "," + + Groups.DELETED + "," + + Groups.GROUP_VISIBLE + "," + + Groups.SHOULD_SYNC + "," + + Groups.SYNC1 + "," + + Groups.SYNC2 + "," + + Groups.SYNC3 + "," + + Groups.SYNC4 + "," + + PackagesColumns.PACKAGE + " AS " + Groups.RES_PACKAGE; + + String groupsSelect = "SELECT " + + GroupsColumns.CONCRETE_ID + " AS " + Groups._ID + "," + + groupsColumns + + " FROM " + Tables.GROUPS_JOIN_PACKAGES; + + db.execSQL("CREATE VIEW " + Views.GROUPS_ALL + " AS " + groupsSelect); + + loadNicknameLookupTable(db); + + // Add the legacy API support views, etc + LegacyApiSupport.createDatabase(db); + + // This will create a sqlite_stat1 table that is used for query optimization + db.execSQL("ANALYZE;"); + + updateSqliteStats(db); + + // We need to close and reopen the database connection so that the stats are + // taken into account. Make a note of it and do the actual reopening in the + // getWritableDatabase method. + mReopenDatabase = true; + + ContentResolver.requestSync(null /* all accounts */, + ContactsContract.AUTHORITY, new Bundle()); + } + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + + Log.i(TAG, "Upgrading from version " + oldVersion + " to " + newVersion + + ", data will be lost!"); + + db.execSQL("DROP TABLE IF EXISTS " + Tables.CONTACTS + ";"); + db.execSQL("DROP TABLE IF EXISTS " + Tables.RAW_CONTACTS + ";"); + db.execSQL("DROP TABLE IF EXISTS " + Tables.PACKAGES + ";"); + db.execSQL("DROP TABLE IF EXISTS " + Tables.MIMETYPES + ";"); + db.execSQL("DROP TABLE IF EXISTS " + Tables.DATA + ";"); + db.execSQL("DROP TABLE IF EXISTS " + Tables.PHONE_LOOKUP + ";"); + db.execSQL("DROP TABLE IF EXISTS " + Tables.NAME_LOOKUP + ";"); + db.execSQL("DROP TABLE IF EXISTS " + Tables.NICKNAME_LOOKUP + ";"); + db.execSQL("DROP TABLE IF EXISTS " + Tables.GROUPS + ";"); + db.execSQL("DROP TABLE IF EXISTS " + Tables.ACTIVITIES + ";"); + db.execSQL("DROP TABLE IF EXISTS " + Tables.CALLS + ";"); + db.execSQL("DROP TABLE IF EXISTS " + Tables.SETTINGS + ";"); + db.execSQL("DROP TABLE IF EXISTS " + Tables.STATUS_UPDATES + ";"); + + db.execSQL("DROP VIEW IF EXISTS " + Tables.CONTACT_ENTITIES + ";"); + db.execSQL("DROP VIEW IF EXISTS " + Tables.CONTACT_ENTITIES_RESTRICTED + ";"); + db.execSQL("DROP VIEW IF EXISTS " + Views.CONTACTS_ALL + ";"); + db.execSQL("DROP VIEW IF EXISTS " + Views.CONTACTS_RESTRICTED + ";"); + db.execSQL("DROP VIEW IF EXISTS " + Views.DATA_ALL + ";"); + db.execSQL("DROP VIEW IF EXISTS " + Views.DATA_RESTRICTED + ";"); + db.execSQL("DROP VIEW IF EXISTS " + Views.RAW_CONTACTS_ALL + ";"); + db.execSQL("DROP VIEW IF EXISTS " + Views.RAW_CONTACTS_RESTRICTED + ";"); + db.execSQL("DROP VIEW IF EXISTS " + Views.GROUPS_ALL + ";"); + + // TODO: we should not be dropping agg_exceptions and contact_options. In case that table's + // schema changes, we should try to preserve the data, because it was entered by the user + // and has never been synched to the server. + db.execSQL("DROP TABLE IF EXISTS " + Tables.AGGREGATION_EXCEPTIONS + ";"); + + onCreate(db); + } + + /** + * Adds index stats into the SQLite database to force it to always use the lookup indexes. + */ + private void updateSqliteStats(SQLiteDatabase db) { + + // Specific stats strings are based on an actual large database after running ANALYZE + try { + updateIndexStats(db, Tables.CONTACTS, + "contacts_restricted_index", "10000 9000"); + updateIndexStats(db, Tables.CONTACTS, + "contacts_has_phone_index", "10000 500"); + updateIndexStats(db, Tables.CONTACTS, + "contacts_visible_index", "10000 500 1"); + + updateIndexStats(db, Tables.RAW_CONTACTS, + "raw_contacts_source_id_index", "10000 1 1 1"); + updateIndexStats(db, Tables.RAW_CONTACTS, + "raw_contacts_contact_id_index", "10000 2"); + + updateIndexStats(db, Tables.NAME_LOOKUP, + "name_lookup_raw_contact_id_index", "10000 3"); + updateIndexStats(db, Tables.NAME_LOOKUP, + "name_lookup_index", "10000 3 2 2"); + updateIndexStats(db, Tables.NAME_LOOKUP, + "sqlite_autoindex_name_lookup_1", "10000 3 2 1"); + + updateIndexStats(db, Tables.PHONE_LOOKUP, + "phone_lookup_index", "10000 2 2 1"); + + updateIndexStats(db, Tables.DATA, + "data_mimetype_data1_index", "60000 5000 2"); + updateIndexStats(db, Tables.DATA, + "data_raw_contact_id", "60000 10"); + + updateIndexStats(db, Tables.GROUPS, + "groups_source_id_index", "50 1 1 1"); + + updateIndexStats(db, Tables.NICKNAME_LOOKUP, + "sqlite_autoindex_name_lookup_1", "500 2 1"); + + } catch (SQLException e) { + Log.e(TAG, "Could not update index stats", e); + } + } + + /** + * Stores statistics for a given index. + * + * @param stats has the following structure: the first index is the expected size of + * the table. The following integer(s) are the expected number of records selected with the + * index. There should be one integer per indexed column. + */ + private void updateIndexStats(SQLiteDatabase db, String table, String index, String stats) { + db.execSQL("DELETE FROM sqlite_stat1 WHERE tbl='" + table + "' AND idx='" + index + "';"); + db.execSQL("INSERT INTO sqlite_stat1 (tbl,idx,stat)" + + " VALUES ('" + table + "','" + index + "','" + stats + "');"); + } + + @Override + public synchronized SQLiteDatabase getWritableDatabase() { + SQLiteDatabase db = super.getWritableDatabase(); + if (mReopenDatabase) { + mReopenDatabase = false; + close(); + db = super.getWritableDatabase(); + } + return db; + } + + /** + * Wipes all data except mime type and package lookup tables. + */ + public void wipeData() { + SQLiteDatabase db = getWritableDatabase(); + + db.execSQL("DELETE FROM " + Tables.CONTACTS + ";"); + db.execSQL("DELETE FROM " + Tables.RAW_CONTACTS + ";"); + db.execSQL("DELETE FROM " + Tables.DATA + ";"); + db.execSQL("DELETE FROM " + Tables.PHONE_LOOKUP + ";"); + db.execSQL("DELETE FROM " + Tables.NAME_LOOKUP + ";"); + db.execSQL("DELETE FROM " + Tables.GROUPS + ";"); + db.execSQL("DELETE FROM " + Tables.AGGREGATION_EXCEPTIONS + ";"); + db.execSQL("DELETE FROM " + Tables.SETTINGS + ";"); + db.execSQL("DELETE FROM " + Tables.ACTIVITIES + ";"); + db.execSQL("DELETE FROM " + Tables.CALLS + ";"); + + // Note: we are not removing reference data from Tables.NICKNAME_LOOKUP + + db.execSQL("VACUUM;"); + } + + /** + * Return the {@link ApplicationInfo#uid} for the given package name. + */ + public static int getUidForPackageName(PackageManager pm, String packageName) { + try { + ApplicationInfo clientInfo = pm.getApplicationInfo(packageName, 0 /* no flags */); + return clientInfo.uid; + } catch (NameNotFoundException e) { + throw new RuntimeException(e); + } + } + + /** + * Perform an internal string-to-integer lookup using the compiled + * {@link SQLiteStatement} provided, using the in-memory cache to speed up + * lookups. If a mapping isn't found in cache or database, it will be + * created. All new, uncached answers are added to the cache automatically. + * + * @param query Compiled statement used to query for the mapping. + * @param insert Compiled statement used to insert a new mapping when no + * existing one is found in cache or from query. + * @param value Value to find mapping for. + * @param cache In-memory cache of previous answers. + * @return An unique integer mapping for the given value. + */ + private synchronized long getCachedId(SQLiteStatement query, SQLiteStatement insert, + String value, HashMap cache) { + // Try an in-memory cache lookup + if (cache.containsKey(value)) { + return cache.get(value); + } + + long id = -1; + try { + // Try searching database for mapping + DatabaseUtils.bindObjectToProgram(query, 1, value); + id = query.simpleQueryForLong(); + } catch (SQLiteDoneException e) { + // Nothing found, so try inserting new mapping + DatabaseUtils.bindObjectToProgram(insert, 1, value); + id = insert.executeInsert(); + } + + if (id != -1) { + // Cache and return the new answer + cache.put(value, id); + return id; + } else { + // Otherwise throw if no mapping found or created + throw new IllegalStateException("Couldn't find or create internal " + + "lookup table entry for value " + value); + } + } + + /** + * Convert a package name into an integer, using {@link Tables#PACKAGES} for + * lookups and possible allocation of new IDs as needed. + */ + public long getPackageId(String packageName) { + // Make sure compiled statements are ready by opening database + getReadableDatabase(); + return getCachedId(mPackageQuery, mPackageInsert, packageName, mPackageCache); + } + + /** + * Convert a mimetype into an integer, using {@link Tables#MIMETYPES} for + * lookups and possible allocation of new IDs as needed. + */ + public long getMimeTypeId(String mimetype) { + // Make sure compiled statements are ready by opening database + getReadableDatabase(); + return getCachedId(mMimetypeQuery, mMimetypeInsert, mimetype, mMimetypeCache); + } + + /** + * Find the mimetype for the given {@link Data#_ID}. + */ + public String getDataMimeType(long dataId) { + // Make sure compiled statements are ready by opening database + getReadableDatabase(); + try { + // Try database query to find mimetype + DatabaseUtils.bindObjectToProgram(mDataMimetypeQuery, 1, dataId); + String mimetype = mDataMimetypeQuery.simpleQueryForString(); + return mimetype; + } catch (SQLiteDoneException e) { + // No valid mapping found, so return null + return null; + } + } + + /** + * Find the mime-type for the given {@link Activities#_ID}. + */ + public String getActivityMimeType(long activityId) { + // Make sure compiled statements are ready by opening database + getReadableDatabase(); + try { + // Try database query to find mimetype + DatabaseUtils.bindObjectToProgram(mActivitiesMimetypeQuery, 1, activityId); + String mimetype = mActivitiesMimetypeQuery.simpleQueryForString(); + return mimetype; + } catch (SQLiteDoneException e) { + // No valid mapping found, so return null + return null; + } + } + + /** + * Update {@link Contacts#IN_VISIBLE_GROUP} for all contacts. + */ + public void updateAllVisible() { + final long groupMembershipMimetypeId = getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE); + mVisibleUpdate.bindLong(1, groupMembershipMimetypeId); + mVisibleUpdate.execute(); + } + + /** + * Update {@link Contacts#IN_VISIBLE_GROUP} for a specific contact. + */ + public void updateContactVisible(long aggId) { + final long groupMembershipMimetypeId = getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE); + mVisibleSpecificUpdate.bindLong(1, groupMembershipMimetypeId); + mVisibleSpecificUpdate.bindLong(2, aggId); + mVisibleSpecificUpdate.execute(); + } + + /** + * Returns contact ID for the given contact or zero if it is NULL. + */ + public long getContactId(long rawContactId) { + getReadableDatabase(); + try { + DatabaseUtils.bindObjectToProgram(mContactIdQuery, 1, rawContactId); + return mContactIdQuery.simpleQueryForLong(); + } catch (SQLiteDoneException e) { + // No valid mapping found, so return 0 + return 0; + } + } + + public int getAggregationMode(long rawContactId) { + getReadableDatabase(); + try { + DatabaseUtils.bindObjectToProgram(mAggregationModeQuery, 1, rawContactId); + return (int)mAggregationModeQuery.simpleQueryForLong(); + } catch (SQLiteDoneException e) { + // No valid row found, so return "disabled" + return RawContacts.AGGREGATION_MODE_DISABLED; + } + } + + public void buildPhoneLookupAndRawContactQuery(SQLiteQueryBuilder qb, String number) { + String normalizedNumber = PhoneNumberUtils.toCallerIDMinMatch(number); + qb.setTables(Tables.DATA_JOIN_RAW_CONTACTS + + " JOIN " + Tables.PHONE_LOOKUP + + " ON(" + DataColumns.CONCRETE_ID + "=" + PhoneLookupColumns.DATA_ID + ")"); + + StringBuilder sb = new StringBuilder(); + sb.append(PhoneLookupColumns.NORMALIZED_NUMBER + " GLOB '"); + sb.append(normalizedNumber); + sb.append("*' AND PHONE_NUMBERS_EQUAL(data." + Phone.NUMBER + ", "); + DatabaseUtils.appendEscapedSQLString(sb, number); + sb.append(mUseStrictPhoneNumberComparation ? ", 1)" : ", 0)"); + + qb.appendWhere(sb.toString()); + } + + public void buildPhoneLookupAndContactQuery(SQLiteQueryBuilder qb, String number) { + String normalizedNumber = PhoneNumberUtils.toCallerIDMinMatch(number); + StringBuilder sb = new StringBuilder(); + appendPhoneLookupTables(sb, normalizedNumber, true); + qb.setTables(sb.toString()); + + sb = new StringBuilder(); + appendPhoneLookupSelection(sb, number); + qb.appendWhere(sb.toString()); + } + + public String buildPhoneLookupAsNestedQuery(String number) { + StringBuilder sb = new StringBuilder(); + final String normalizedNumber = PhoneNumberUtils.toCallerIDMinMatch(number); + sb.append("(SELECT DISTINCT raw_contact_id" + " FROM "); + appendPhoneLookupTables(sb, normalizedNumber, false); + sb.append(" WHERE "); + appendPhoneLookupSelection(sb, number); + sb.append(")"); + return sb.toString(); + } + + private void appendPhoneLookupTables(StringBuilder sb, final String normalizedNumber, + boolean joinContacts) { + sb.append(Tables.RAW_CONTACTS); + if (joinContacts) { + sb.append(" JOIN " + getContactView() + " contacts" + + " ON (contacts._id = raw_contacts.contact_id)"); + } + sb.append(", (SELECT data_id FROM phone_lookup " + + "WHERE (phone_lookup.normalized_number GLOB '"); + sb.append(normalizedNumber); + sb.append("*')) AS lookup, " + Tables.DATA); + } + + private void appendPhoneLookupSelection(StringBuilder sb, String number) { + sb.append("lookup.data_id=data._id AND data.raw_contact_id=raw_contacts._id" + + " AND PHONE_NUMBERS_EQUAL(data." + Phone.NUMBER + ", "); + DatabaseUtils.appendEscapedSQLString(sb, number); + sb.append(mUseStrictPhoneNumberComparation ? ", 1)" : ", 0)"); + } + + /** + * Loads common nickname mappings into the database. + */ + private void loadNicknameLookupTable(SQLiteDatabase db) { + String[] strings = mContext.getResources().getStringArray( + com.android.internal.R.array.common_nicknames); + if (strings == null || strings.length == 0) { + return; + } + + SQLiteStatement nicknameLookupInsert = db.compileStatement("INSERT INTO " + + Tables.NICKNAME_LOOKUP + "(" + NicknameLookupColumns.NAME + "," + + NicknameLookupColumns.CLUSTER + ") VALUES (?,?)"); + + for (int clusterId = 0; clusterId < strings.length; clusterId++) { + String[] names = strings[clusterId].split(","); + for (int j = 0; j < names.length; j++) { + String name = NameNormalizer.normalize(names[j]); + try { + DatabaseUtils.bindObjectToProgram(nicknameLookupInsert, 1, name); + DatabaseUtils.bindObjectToProgram(nicknameLookupInsert, 2, + String.valueOf(clusterId)); + nicknameLookupInsert.executeInsert(); + } catch (SQLiteException e) { + + // Print the exception and keep going - this is not a fatal error + Log.e(TAG, "Cannot insert nickname: " + names[j], e); + } + } + } + } + + public static void copyStringValue(ContentValues toValues, String toKey, + ContentValues fromValues, String fromKey) { + if (fromValues.containsKey(fromKey)) { + toValues.put(toKey, fromValues.getAsString(fromKey)); + } + } + + public static void copyLongValue(ContentValues toValues, String toKey, + ContentValues fromValues, String fromKey) { + if (fromValues.containsKey(fromKey)) { + long longValue; + Object value = fromValues.get(fromKey); + if (value instanceof Boolean) { + if ((Boolean)value) { + longValue = 1; + } else { + longValue = 0; + } + } else if (value instanceof String) { + longValue = Long.parseLong((String)value); + } else { + longValue = ((Number)value).longValue(); + } + toValues.put(toKey, longValue); + } + } + + public SyncStateContentProviderHelper getSyncState() { + return mSyncState; + } + + /** + * Delete the aggregate contact if it has no constituent raw contacts other + * than the supplied one. + */ + public void removeContactIfSingleton(long rawContactId) { + SQLiteDatabase db = getWritableDatabase(); + + // Obtain contact ID from the supplied raw contact ID + String contactIdFromRawContactId = "(SELECT " + RawContacts.CONTACT_ID + " FROM " + + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=" + rawContactId + ")"; + + // Find other raw contacts in the same aggregate contact + String otherRawContacts = "(SELECT contacts1." + RawContacts._ID + " FROM " + + Tables.RAW_CONTACTS + " contacts1 JOIN " + Tables.RAW_CONTACTS + " contacts2 ON (" + + "contacts1." + RawContacts.CONTACT_ID + "=contacts2." + RawContacts.CONTACT_ID + + ") WHERE contacts1." + RawContacts._ID + "!=" + rawContactId + "" + + " AND contacts2." + RawContacts._ID + "=" + rawContactId + ")"; + + db.execSQL("DELETE FROM " + Tables.CONTACTS + + " WHERE " + Contacts._ID + "=" + contactIdFromRawContactId + + " AND NOT EXISTS " + otherRawContacts + ";"); + } + + /** + * List of package names with access to {@link RawContacts#IS_RESTRICTED} data. + */ + static final String[] sAllowedPackages = new String[] { + "com.android.contacts", + "com.facebook.katana", + }; + + /** + * Check if {@link Binder#getCallingUid()} should be allowed access to + * {@link RawContacts#IS_RESTRICTED} data. + */ + boolean hasRestrictedAccess() { + final PackageManager pm = mContext.getPackageManager(); + final String[] callerPackages = pm.getPackagesForUid(Binder.getCallingUid()); + + // Has restricted access if caller matches any packages + for (String callerPackage : callerPackages) { + for (String allowedPackage : sAllowedPackages) { + if (allowedPackage.equals(callerPackage)) { + return true; + } + } + } + return false; + } + + public String getDataView() { + return getDataView(false); + } + + public String getDataView(boolean requireRestrictedView) { + return (hasRestrictedAccess() && !requireRestrictedView) ? + Views.DATA_ALL : Views.DATA_RESTRICTED; + } + + public String getRawContactView() { + return hasRestrictedAccess() ? Views.RAW_CONTACTS_ALL + : Views.RAW_CONTACTS_RESTRICTED; + } + + public String getContactView() { + return hasRestrictedAccess() ? Views.CONTACTS_ALL : Views.CONTACTS_RESTRICTED; + } + + public String getRestrictedContactView() { + return Views.CONTACTS_RESTRICTED; + } + + public String getGroupView() { + return Views.GROUPS_ALL; + } + + public String getContactEntitiesView() { + return getContactEntitiesView(false); + } + + public String getContactEntitiesView(boolean requireRestrictedView) { + return (hasRestrictedAccess() && !requireRestrictedView) ? + Tables.CONTACT_ENTITIES : Tables.CONTACT_ENTITIES_RESTRICTED; + } + + /** + * Test if any of the columns appear in the given projection. + */ + public boolean isInProjection(String[] projection, String... columns) { + if (projection == null) { + return true; + } + + // Optimized for a single-column test + if (columns.length == 1) { + String column = columns[0]; + for (String test : projection) { + if (column.equals(test)) { + return true; + } + } + } else { + for (String test : projection) { + for (String column : columns) { + if (column.equals(test)) { + return true; + } + } + } + } + return false; + } +} diff --git a/src/com/android/providers/contacts/ContactsProvider2.java b/src/com/android/providers/contacts/ContactsProvider2.java index 953fa9d6..8a26565d 100644 --- a/src/com/android/providers/contacts/ContactsProvider2.java +++ b/src/com/android/providers/contacts/ContactsProvider2.java @@ -18,24 +18,24 @@ package com.android.providers.contacts; import com.android.internal.content.SyncStateContentProviderHelper; import com.android.providers.contacts.ContactLookupKey.LookupKeySegment; -import com.android.providers.contacts.OpenHelper.AggregatedPresenceColumns; -import com.android.providers.contacts.OpenHelper.AggregationExceptionColumns; -import com.android.providers.contacts.OpenHelper.Clauses; -import com.android.providers.contacts.OpenHelper.ContactsColumns; -import com.android.providers.contacts.OpenHelper.DataColumns; -import com.android.providers.contacts.OpenHelper.DisplayNameSources; -import com.android.providers.contacts.OpenHelper.GroupsColumns; -import com.android.providers.contacts.OpenHelper.MimetypesColumns; -import com.android.providers.contacts.OpenHelper.NameLookupColumns; -import com.android.providers.contacts.OpenHelper.NameLookupType; -import com.android.providers.contacts.OpenHelper.NicknameLookupColumns; -import com.android.providers.contacts.OpenHelper.PhoneColumns; -import com.android.providers.contacts.OpenHelper.PhoneLookupColumns; -import com.android.providers.contacts.OpenHelper.PresenceColumns; -import com.android.providers.contacts.OpenHelper.RawContactsColumns; -import com.android.providers.contacts.OpenHelper.SettingsColumns; -import com.android.providers.contacts.OpenHelper.StatusUpdatesColumns; -import com.android.providers.contacts.OpenHelper.Tables; +import com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.AggregationExceptionColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.Clauses; +import com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.DisplayNameSources; +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.NicknameLookupColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns; +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.SettingsColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.StatusUpdatesColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.Tables; import com.google.android.collect.Lists; import com.google.android.collect.Maps; import com.google.android.collect.Sets; @@ -771,7 +771,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun protected long getMimeTypeId() { if (mMimetypeId == 0) { - mMimetypeId = mOpenHelper.getMimeTypeId(mMimetype); + mMimetypeId = mDbHelper.getMimeTypeId(mMimetype); } return mMimetypeId; } @@ -1419,9 +1419,9 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } private void updateVisibility(long rawContactId) { - long contactId = mOpenHelper.getContactId(rawContactId); + long contactId = mDbHelper.getContactId(rawContactId); if (contactId != 0) { - mOpenHelper.updateContactVisible(contactId); + mDbHelper.updateContactVisible(contactId); } } @@ -1499,7 +1499,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun private HashMap mDataRowHandlers; private final ContactAggregationScheduler mAggregationScheduler; - private OpenHelper mOpenHelper; + private ContactsDatabaseHelper mDbHelper; private NameSplitter mNameSplitter; private NameLookupBuilder mNameLookupBuilder; @@ -1540,13 +1540,13 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun super.onCreate(); final Context context = getContext(); - mOpenHelper = (OpenHelper)getOpenHelper(); + mDbHelper = (ContactsDatabaseHelper)getDatabaseHelper(); mGlobalSearchSupport = new GlobalSearchSupport(this); - mLegacyApiSupport = new LegacyApiSupport(context, mOpenHelper, this, mGlobalSearchSupport); - mContactAggregator = new ContactAggregator(this, mOpenHelper, mAggregationScheduler); + mLegacyApiSupport = new LegacyApiSupport(context, mDbHelper, this, mGlobalSearchSupport); + mContactAggregator = new ContactAggregator(this, mDbHelper, mAggregationScheduler); mContactAggregator.setEnabled(SystemProperties.getBoolean(AGGREGATE_CONTACTS, true)); - final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + final SQLiteDatabase db = mDbHelper.getReadableDatabase(); mSetPrimaryStatement = db.compileStatement( "UPDATE " + Tables.DATA + @@ -1684,8 +1684,8 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun /* Visible for testing */ @Override - protected OpenHelper getOpenHelper(final Context context) { - return OpenHelper.getInstance(context); + protected ContactsDatabaseHelper getDatabaseHelper(final Context context) { + return ContactsDatabaseHelper.getInstance(context); } /* package */ ContactAggregationScheduler getContactAggregationScheduler() { @@ -1774,7 +1774,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun * Wipes all data from the contacts database. */ /* package */ void wipeData() { - mOpenHelper.wipeData(); + mDbHelper.wipeData(); } /** @@ -1850,7 +1850,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun mContactAggregator.aggregateInTransaction(mDb); if (mVisibleTouched) { mVisibleTouched = false; - mOpenHelper.updateAllVisible(); + mDbHelper.updateAllVisible(); } } @@ -1871,7 +1871,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun for (Map.Entry entry : mUpdatedSyncStates.entrySet()) { long id = entry.getKey(); - mOpenHelper.getSyncState().update(mDb, id, entry.getValue()); + mDbHelper.getSyncState().update(mDb, id, entry.getValue()); } clearTransactionalChanges(); @@ -1934,7 +1934,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun switch (match) { case SYNCSTATE: - id = mOpenHelper.getSyncState().insert(mDb, values); + id = mDbHelper.getSyncState().insert(mDb, values); break; case CONTACTS: { @@ -2072,7 +2072,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun // Replace package with internal mapping final String packageName = mValues.getAsString(Data.RES_PACKAGE); if (packageName != null) { - mValues.put(DataColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName)); + mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName)); } mValues.remove(Data.RES_PACKAGE); @@ -2082,7 +2082,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun throw new IllegalArgumentException(Data.MIMETYPE + " is required"); } - mValues.put(DataColumns.MIMETYPE_ID, mOpenHelper.getMimeTypeId(mimeType)); + mValues.put(DataColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType)); mValues.remove(Data.MIMETYPE); DataRowHandler rowHandler = getDataRowHandler(mimeType); @@ -2103,7 +2103,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun return; } - int aggregationMode = mOpenHelper.getAggregationMode(rawContactId); + int aggregationMode = mDbHelper.getAggregationMode(rawContactId); switch (aggregationMode) { case RawContacts.AGGREGATION_MODE_DISABLED: break; @@ -2114,7 +2114,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } case RawContacts.AGGREGATION_MODE_SUSPENDED: { - long contactId = mOpenHelper.getContactId(rawContactId); + long contactId = mDbHelper.getContactId(rawContactId); if (contactId != 0) { mContactAggregator.updateAggregateData(contactId); @@ -2123,7 +2123,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } case RawContacts.AGGREGATION_MODE_IMMEDIATE: { - long contactId = mOpenHelper.getContactId(rawContactId); + long contactId = mDbHelper.getContactId(rawContactId); mContactAggregator.aggregateContact(mDb, rawContactId, contactId); break; } @@ -2259,7 +2259,8 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun /** * Inserts an item in the groups table */ - private long insertGroup(Uri uri, ContentValues values, Account account, boolean callerIsSyncAdapter) { + private long insertGroup(Uri uri, ContentValues values, Account account, + boolean callerIsSyncAdapter) { ContentValues overriddenValues = new ContentValues(values); if (!resolveAccount(overriddenValues, account)) { return -1; @@ -2268,7 +2269,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun // Replace package with internal mapping final String packageName = overriddenValues.getAsString(Groups.RES_PACKAGE); if (packageName != null) { - overriddenValues.put(GroupsColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName)); + overriddenValues.put(GroupsColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName)); } overriddenValues.remove(Groups.RES_PACKAGE); @@ -2483,13 +2484,13 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun final int match = sUriMatcher.match(uri); switch (match) { case SYNCSTATE: - return mOpenHelper.getSyncState().delete(mDb, selection, selectionArgs); + return mDbHelper.getSyncState().delete(mDb, selection, selectionArgs); case SYNCSTATE_ID: String selectionWithId = (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ") + (selection == null ? "" : " AND (" + selection + ")"); - return mOpenHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs); + return mDbHelper.getSyncState().delete(mDb, selectionWithId, selectionArgs); case CONTACTS: { // TODO @@ -2594,7 +2595,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } private int deleteGroup(Uri uri, long groupId, boolean callerIsSyncAdapter) { - final long groupMembershipMimetypeId = mOpenHelper + final long groupMembershipMimetypeId = mDbHelper .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE); mDb.delete(Tables.DATA, DataColumns.MIMETYPE_ID + "=" + groupMembershipMimetypeId + " AND " + GroupMembership.GROUP_ROW_ID + "=" @@ -2640,7 +2641,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun mDb.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=" + rawContactId, null); return mDb.delete(Tables.RAW_CONTACTS, RawContacts._ID + "=" + rawContactId, null); } else { - mOpenHelper.removeContactIfSingleton(rawContactId); + mDbHelper.removeContactIfSingleton(rawContactId); return markRawContactAsDeleted(rawContactId); } } @@ -2683,7 +2684,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun readBooleanQueryParameter(uri, ContactsContract.CALLER_IS_SYNCADAPTER, false); switch(match) { case SYNCSTATE: - return mOpenHelper.getSyncState().update(mDb, values, + return mDbHelper.getSyncState().update(mDb, values, appendAccountToSelection(uri, selection), selectionArgs); case SYNCSTATE_ID: { @@ -2691,7 +2692,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun String selectionWithId = (SyncStateContract.Columns._ID + "=" + ContentUris.parseId(uri) + " ") + (selection == null ? "" : " AND (" + selection + ")"); - return mOpenHelper.getSyncState().update(mDb, values, + return mDbHelper.getSyncState().update(mDb, values, selectionWithId, selectionArgs); } @@ -2826,7 +2827,8 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun return count; } - private int updateSettings(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + private int updateSettings(Uri uri, ContentValues values, String selection, + String[] selectionArgs) { final int count = mDb.update(Tables.SETTINGS, values, selection, selectionArgs); if (values.containsKey(Settings.UNGROUPED_VISIBLE)) { mVisibleTouched = true; @@ -2841,7 +2843,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } int count = 0; - Cursor cursor = mDb.query(mOpenHelper.getRawContactView(), + Cursor cursor = mDb.query(mDbHelper.getRawContactView(), new String[] { RawContacts._ID }, selection, selectionArgs, null, null, null); try { @@ -2902,7 +2904,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun String packageName = values.getAsString(Data.RES_PACKAGE); if (packageName != null) { mValues.remove(Data.RES_PACKAGE); - mValues.put(DataColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName)); + mValues.put(DataColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName)); } boolean containsIsSuperPrimary = mValues.containsKey(Data.IS_SUPER_PRIMARY); @@ -2954,7 +2956,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun private int updateContactOptions(ContentValues values, String selection, String[] selectionArgs) { int count = 0; - Cursor cursor = mDb.query(mOpenHelper.getContactView(), + Cursor cursor = mDb.query(mDbHelper.getContactView(), new String[] { Contacts._ID }, selection, selectionArgs, null, null, null); try { @@ -2973,15 +2975,15 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun private int updateContactOptions(long contactId, ContentValues values) { mValues.clear(); - OpenHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE, + ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE, values, Contacts.CUSTOM_RINGTONE); - OpenHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL, + ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL, values, Contacts.SEND_TO_VOICEMAIL); - OpenHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED, + ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED, values, Contacts.LAST_TIME_CONTACTED); - OpenHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED, + ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED, values, Contacts.TIMES_CONTACTED); - OpenHelper.copyLongValue(mValues, RawContacts.STARRED, + ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED, values, Contacts.STARRED); // Nothing to update - just return @@ -2999,15 +3001,15 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun // Copy changeable values to prevent automatically managed fields from // being explicitly updated by clients. mValues.clear(); - OpenHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE, + ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE, values, Contacts.CUSTOM_RINGTONE); - OpenHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL, + ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL, values, Contacts.SEND_TO_VOICEMAIL); - OpenHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED, + ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED, values, Contacts.LAST_TIME_CONTACTED); - OpenHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED, + ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED, values, Contacts.TIMES_CONTACTED); - OpenHelper.copyLongValue(mValues, RawContacts.STARRED, + ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED, values, Contacts.STARRED); return mDb.update(Tables.CONTACTS, mValues, Contacts._ID + "=" + contactId, null); @@ -3049,10 +3051,10 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun mContactAggregator.markForAggregation(rawContactId1); mContactAggregator.markForAggregation(rawContactId2); - long contactId1 = mOpenHelper.getContactId(rawContactId1); + long contactId1 = mDbHelper.getContactId(rawContactId1); mContactAggregator.aggregateContact(db, rawContactId1, contactId1); - long contactId2 = mOpenHelper.getContactId(rawContactId2); + long contactId2 = mDbHelper.getContactId(rawContactId2); mContactAggregator.aggregateContact(db, rawContactId2, contactId2); // The return value is fake - we just confirm that we made a change, not count actual @@ -3061,7 +3063,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } public void onAccountsUpdated(Account[] accounts) { - mDb = mOpenHelper.getWritableDatabase(); + mDb = mDbHelper.getWritableDatabase(); if (mDb == null) return; Set validAccounts = Sets.newHashSet(); @@ -3103,7 +3105,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun mDb.execSQL("DELETE FROM " + Tables.SETTINGS + " WHERE account_name = ? AND account_type = ?", params); } - mOpenHelper.getSyncState().onAccountsChanged(mDb, accounts); + mDbHelper.getSyncState().onAccountsChanged(mDb, accounts); mDb.setTransactionSuccessful(); } finally { mDb.endTransaction(); @@ -3129,7 +3131,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun Log.v(TAG, "query: " + uri); } - final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + final SQLiteDatabase db = mDbHelper.getReadableDatabase(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); String groupBy = null; @@ -3140,7 +3142,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun final int match = sUriMatcher.match(uri); switch (match) { case SYNCSTATE: - return mOpenHelper.getSyncState().query(db, projection, selection, selectionArgs, + return mDbHelper.getSyncState().query(db, projection, selection, selectionArgs, sortOrder); case CONTACTS: { @@ -3187,7 +3189,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun case CONTACTS_AS_VCARD: { // When reading as vCard always use restricted view final String lookupKey = uri.getPathSegments().get(2); - qb.setTables(mOpenHelper.getRestrictedContactView()); + qb.setTables(mDbHelper.getRestrictedContactView()); qb.setProjectionMap(sContactsVCardProjectionMap); qb.appendWhere(Contacts._ID + "=" + lookupContactIdByLookupKey(db, lookupKey)); break; @@ -3384,7 +3386,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } case RAW_CONTACTS: { - qb.setTables(mOpenHelper.getRawContactView()); + qb.setTables(mDbHelper.getRawContactView()); qb.setProjectionMap(sRawContactsProjectionMap); appendAccountFromParameter(qb, uri); break; @@ -3392,7 +3394,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun case RAW_CONTACTS_ID: { long rawContactId = ContentUris.parseId(uri); - qb.setTables(mOpenHelper.getRawContactView()); + qb.setTables(mDbHelper.getRawContactView()); qb.setProjectionMap(sRawContactsProjectionMap); appendAccountFromParameter(qb, uri); qb.appendWhere(" AND " + RawContacts._ID + "=" + rawContactId); @@ -3426,7 +3428,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } String number = uri.getPathSegments().size() > 1 ? uri.getLastPathSegment() : ""; - mOpenHelper.buildPhoneLookupAndContactQuery(qb, number); + mDbHelper.buildPhoneLookupAndContactQuery(qb, number); qb.setProjectionMap(sPhoneLookupProjectionMap); // Phone lookup cannot be combined with a selection @@ -3436,7 +3438,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } case GROUPS: { - qb.setTables(mOpenHelper.getGroupView()); + qb.setTables(mDbHelper.getGroupView()); qb.setProjectionMap(sGroupsProjectionMap); appendAccountFromParameter(qb, uri); break; @@ -3444,14 +3446,14 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun case GROUPS_ID: { long groupId = ContentUris.parseId(uri); - qb.setTables(mOpenHelper.getGroupView()); + qb.setTables(mDbHelper.getGroupView()); qb.setProjectionMap(sGroupsProjectionMap); qb.appendWhere(Groups._ID + "=" + groupId); break; } case GROUPS_SUMMARY: { - qb.setTables(mOpenHelper.getGroupView() + " AS groups"); + qb.setTables(mDbHelper.getGroupView() + " AS groups"); qb.setProjectionMap(sGroupsSummaryProjectionMap); appendAccountFromParameter(qb, uri); groupBy = Groups._ID; @@ -3490,14 +3492,14 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun // When requesting specific columns, this query requires // late-binding of the GroupMembership MIME-type. - final String groupMembershipMimetypeId = Long.toString(mOpenHelper + final String groupMembershipMimetypeId = Long.toString(mDbHelper .getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE)); if (projection != null && projection.length != 0 && - mOpenHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) { + mDbHelper.isInProjection(projection, Settings.UNGROUPED_COUNT)) { selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId); } if (projection != null && projection.length != 0 && - mOpenHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) { + mDbHelper.isInProjection(projection, Settings.UNGROUPED_WITH_PHONES)) { selectionArgs = insertSelectionArg(selectionArgs, groupMembershipMimetypeId); } @@ -3525,24 +3527,24 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } case LIVE_FOLDERS_CONTACTS: - qb.setTables(mOpenHelper.getContactView()); + qb.setTables(mDbHelper.getContactView()); qb.setProjectionMap(sLiveFoldersProjectionMap); break; case LIVE_FOLDERS_CONTACTS_WITH_PHONES: - qb.setTables(mOpenHelper.getContactView()); + qb.setTables(mDbHelper.getContactView()); qb.setProjectionMap(sLiveFoldersProjectionMap); qb.appendWhere(Contacts.HAS_PHONE_NUMBER + "=1"); break; case LIVE_FOLDERS_CONTACTS_FAVORITES: - qb.setTables(mOpenHelper.getContactView()); + qb.setTables(mDbHelper.getContactView()); qb.setProjectionMap(sLiveFoldersProjectionMap); qb.appendWhere(Contacts.STARRED + "=1"); break; case LIVE_FOLDERS_CONTACTS_GROUP_NAME: - qb.setTables(mOpenHelper.getContactView()); + qb.setTables(mDbHelper.getContactView()); qb.setProjectionMap(sLiveFoldersProjectionMap); qb.appendWhere(CONTACTS_IN_GROUP_SELECT); selectionArgs = insertSelectionArg(selectionArgs, uri.getLastPathSegment()); @@ -3757,13 +3759,13 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun private void setTablesAndProjectionMapForContacts(SQLiteQueryBuilder qb, String[] projection) { StringBuilder sb = new StringBuilder(); - sb.append(mOpenHelper.getContactView()); - if (mOpenHelper.isInProjection(projection, + sb.append(mDbHelper.getContactView()); + if (mDbHelper.isInProjection(projection, Contacts.CONTACT_PRESENCE)) { sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE + " ON (" + Contacts._ID + " = " + AggregatedPresenceColumns.CONTACT_ID + ")"); } - if (mOpenHelper.isInProjection(projection, + if (mDbHelper.isInProjection(projection, Contacts.CONTACT_STATUS, Contacts.CONTACT_STATUS_RES_PACKAGE, Contacts.CONTACT_STATUS_ICON, @@ -3783,16 +3785,16 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun // Note: currently, "export only" equals to "restricted", but may not in the future. boolean requireRestrictedData = readBooleanQueryParameter(uri, Data.FOR_EXPORT_ONLY, false); - sb.append(mOpenHelper.getDataView(requireRestrictedData)); + sb.append(mDbHelper.getDataView(requireRestrictedData)); sb.append(" data"); - if (mOpenHelper.isInProjection(projection, Data.CONTACT_PRESENCE)) { + if (mDbHelper.isInProjection(projection, Data.CONTACT_PRESENCE)) { sb.append(" LEFT OUTER JOIN " + Tables.AGGREGATED_PRESENCE + " ON (" + AggregatedPresenceColumns.CONTACT_ID + "=" + RawContacts.CONTACT_ID + ")"); } - if (mOpenHelper.isInProjection(projection, + if (mDbHelper.isInProjection(projection, Data.CONTACT_STATUS, Data.CONTACT_STATUS_RES_PACKAGE, Data.CONTACT_STATUS_ICON, @@ -3810,16 +3812,16 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun private void setTableAndProjectionMapForStatusUpdates(SQLiteQueryBuilder qb, String[] projection) { StringBuilder sb = new StringBuilder(); - sb.append(mOpenHelper.getDataView()); + sb.append(mDbHelper.getDataView()); sb.append(" data"); - if (mOpenHelper.isInProjection(projection, StatusUpdates.PRESENCE)) { + if (mDbHelper.isInProjection(projection, StatusUpdates.PRESENCE)) { sb.append(" LEFT OUTER JOIN " + Tables.PRESENCE + " ON(" + Tables.PRESENCE + "." + StatusUpdates.DATA_ID + "=" + DataColumns.CONCRETE_ID + ")"); } - if (mOpenHelper.isInProjection(projection, + if (mDbHelper.isInProjection(projection, StatusUpdates.STATUS, StatusUpdates.STATUS_RES_PACKAGE, StatusUpdates.STATUS_ICON, @@ -3921,7 +3923,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } String getContactsRestrictions() { - if (mOpenHelper.hasRestrictedAccess()) { + if (mDbHelper.hasRestrictedAccess()) { return "1"; } else { return RawContacts.IS_RESTRICTED + "=0"; @@ -3929,7 +3931,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } public String getContactsRestrictionExceptionAsNestedQuery(String contactIdColumn) { - if (mOpenHelper.hasRestrictedAccess()) { + if (mDbHelper.hasRestrictedAccess()) { return "1"; } else { return "(SELECT " + RawContacts.IS_RESTRICTED + " FROM " + Tables.RAW_CONTACTS @@ -3949,10 +3951,10 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun long contactId = Long.parseLong(uri.getPathSegments().get(1)); String sql = - "SELECT " + Photo.PHOTO + " FROM " + mOpenHelper.getDataView() + + "SELECT " + Photo.PHOTO + " FROM " + mDbHelper.getDataView() + " WHERE " + Data._ID + "=" + Contacts.PHOTO_ID + " AND " + RawContacts.CONTACT_ID + "=" + contactId; - SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + SQLiteDatabase db = mDbHelper.getReadableDatabase(); return SQLiteContentHelper.getBlobColumnAsAssetFile(db, sql, null); } @@ -4126,19 +4128,19 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun private static final int COLUMN_CONTACT_ID = 37; private static final int COLUMN_STARRED = 38; - public RawContactsEntityIterator(ContactsProvider2 provider, String contactsIdString, Uri uri, - String selection, String[] selectionArgs, String sortOrder) { + public RawContactsEntityIterator(ContactsProvider2 provider, String contactsIdString, + Uri uri, String selection, String[] selectionArgs, String sortOrder) { mIsClosed = false; final String updatedSortOrder = (sortOrder == null) ? Data.RAW_CONTACT_ID : (Data.RAW_CONTACT_ID + "," + sortOrder); - final SQLiteDatabase db = provider.mOpenHelper.getReadableDatabase(); + final SQLiteDatabase db = provider.mDbHelper.getReadableDatabase(); final SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); final boolean requireRestrictedView = provider.readBooleanQueryParameter(uri, Data.FOR_EXPORT_ONLY, false); - qb.setTables(provider.mOpenHelper.getContactEntitiesView(requireRestrictedView)); + qb.setTables(provider.mDbHelper.getContactEntitiesView(requireRestrictedView)); if (contactsIdString != null) { qb.appendWhere(Data.RAW_CONTACT_ID + "=" + contactsIdString); } @@ -4308,9 +4310,9 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun ? Groups._ID : (Groups._ID + "," + sortOrder); - final SQLiteDatabase db = provider.mOpenHelper.getReadableDatabase(); + final SQLiteDatabase db = provider.mDbHelper.getReadableDatabase(); final SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); - qb.setTables(provider.mOpenHelper.getGroupView()); + qb.setTables(provider.mDbHelper.getGroupView()); qb.setProjectionMap(sGroupsProjectionMap); if (groupIdString != null) { qb.appendWhere(Groups._ID + "=" + groupIdString); @@ -4438,7 +4440,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun case RAW_CONTACTS_ID: return RawContacts.CONTENT_ITEM_TYPE; case DATA_ID: - return mOpenHelper.getDataMimeType(ContentUris.parseId(uri)); + return mDbHelper.getDataMimeType(ContentUris.parseId(uri)); case PHONES: return Phone.CONTENT_TYPE; case PHONES_ID: @@ -4588,7 +4590,7 @@ public class ContactsProvider2 extends SQLiteContentProvider implements OnAccoun } protected String[] loadNicknameClusters(String normalizedName) { - SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + SQLiteDatabase db = mDbHelper.getReadableDatabase(); String[] clusters = null; Cursor cursor = db.query(NicknameLookupQuery.TABLE, NicknameLookupQuery.COLUMNS, NicknameLookupColumns.NAME + "=?", new String[] { normalizedName }, diff --git a/src/com/android/providers/contacts/GlobalSearchSupport.java b/src/com/android/providers/contacts/GlobalSearchSupport.java index d2f674ad..13ead671 100644 --- a/src/com/android/providers/contacts/GlobalSearchSupport.java +++ b/src/com/android/providers/contacts/GlobalSearchSupport.java @@ -17,11 +17,11 @@ package com.android.providers.contacts; import com.android.internal.database.ArrayListCursor; -import com.android.providers.contacts.OpenHelper.AggregatedPresenceColumns; -import com.android.providers.contacts.OpenHelper.ContactsColumns; -import com.android.providers.contacts.OpenHelper.DataColumns; -import com.android.providers.contacts.OpenHelper.MimetypesColumns; -import com.android.providers.contacts.OpenHelper.Tables; +import com.android.providers.contacts.ContactsDatabaseHelper.AggregatedPresenceColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.Tables; import android.app.SearchManager; import android.content.ContentUris; diff --git a/src/com/android/providers/contacts/LegacyApiSupport.java b/src/com/android/providers/contacts/LegacyApiSupport.java index 3bee37be..6e02a22f 100644 --- a/src/com/android/providers/contacts/LegacyApiSupport.java +++ b/src/com/android/providers/contacts/LegacyApiSupport.java @@ -15,15 +15,15 @@ */ package com.android.providers.contacts; -import com.android.providers.contacts.OpenHelper.DataColumns; -import com.android.providers.contacts.OpenHelper.ExtensionsColumns; -import com.android.providers.contacts.OpenHelper.GroupsColumns; -import com.android.providers.contacts.OpenHelper.MimetypesColumns; -import com.android.providers.contacts.OpenHelper.PhoneColumns; -import com.android.providers.contacts.OpenHelper.PresenceColumns; -import com.android.providers.contacts.OpenHelper.RawContactsColumns; -import com.android.providers.contacts.OpenHelper.StatusUpdatesColumns; -import com.android.providers.contacts.OpenHelper.Tables; +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.PhoneColumns; +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 android.accounts.Account; import android.app.SearchManager; @@ -460,7 +460,7 @@ public class LegacyApiSupport { } private final Context mContext; - private final OpenHelper mOpenHelper; + private final ContactsDatabaseHelper mDbHelper; private final ContactsProvider2 mContactsProvider; private final NameSplitter mPhoneticNameSplitter; private final GlobalSearchSupport mGlobalSearchSupport; @@ -479,18 +479,18 @@ public class LegacyApiSupport { private long mMimetypeIm; private long mMimetypePostal; - public LegacyApiSupport(Context context, OpenHelper openHelper, + public LegacyApiSupport(Context context, ContactsDatabaseHelper contactsDatabaseHelper, ContactsProvider2 contactsProvider, GlobalSearchSupport globalSearchSupport) { mContext = context; mContactsProvider = contactsProvider; - mOpenHelper = openHelper; + mDbHelper = contactsDatabaseHelper; mGlobalSearchSupport = globalSearchSupport; mPhoneticNameSplitter = new NameSplitter("", "", "", context .getString(com.android.internal.R.string.common_name_conjunctions), Locale .getDefault()); - SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + SQLiteDatabase db = mDbHelper.getReadableDatabase(); mLastTimeContactedUpdate = db.compileStatement("UPDATE " + Tables.RAW_CONTACTS + " SET " + RawContacts.LAST_TIME_CONTACTED + "=? WHERE " + RawContacts._ID + "=?"); @@ -505,9 +505,9 @@ public class LegacyApiSupport { " FROM " + Tables.DATA + " WHERE " + Data._ID + "=?"); - mMimetypeEmail = mOpenHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE); - mMimetypeIm = mOpenHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE); - mMimetypePostal = mOpenHelper.getMimeTypeId(StructuredPostal.CONTENT_ITEM_TYPE); + mMimetypeEmail = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE); + mMimetypeIm = mDbHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE); + mMimetypePostal = mDbHelper.getMimeTypeId(StructuredPostal.CONTENT_ITEM_TYPE); } private void ensureDefaultAccount() { @@ -823,7 +823,7 @@ public class LegacyApiSupport { private long insertOrganization(ContentValues values) { parseOrganizationValues(values); - OpenHelper.copyLongValue(mValues, Data.RAW_CONTACT_ID, + ContactsDatabaseHelper.copyLongValue(mValues, Data.RAW_CONTACT_ID, values, android.provider.Contacts.Organizations.PERSON_ID); Uri uri = mContactsProvider.insertInTransaction(Data.CONTENT_URI, mValues); @@ -1089,7 +1089,7 @@ public class LegacyApiSupport { } long rawContactId = Long.parseLong(uri.getPathSegments().get(1)); - long contactId = mOpenHelper.getContactId(rawContactId); + long contactId = mDbHelper.getContactId(rawContactId); if (contactId != 0) { mContactsProvider.updateContactLastContactedTime(contactId, lastTimeContacted); } @@ -1156,13 +1156,13 @@ public class LegacyApiSupport { private void updateLegacyPhotoData(long rawContactId, long dataId, ContentValues values) { mValues.clear(); - OpenHelper.copyStringValue(mValues, LegacyPhotoData.LOCAL_VERSION, + ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.LOCAL_VERSION, values, android.provider.Contacts.Photos.LOCAL_VERSION); - OpenHelper.copyStringValue(mValues, LegacyPhotoData.DOWNLOAD_REQUIRED, + ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.DOWNLOAD_REQUIRED, values, android.provider.Contacts.Photos.DOWNLOAD_REQUIRED); - OpenHelper.copyStringValue(mValues, LegacyPhotoData.EXISTS_ON_SERVER, + ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.EXISTS_ON_SERVER, values, android.provider.Contacts.Photos.EXISTS_ON_SERVER); - OpenHelper.copyStringValue(mValues, LegacyPhotoData.SYNC_ERROR, + ContactsDatabaseHelper.copyStringValue(mValues, LegacyPhotoData.SYNC_ERROR, values, android.provider.Contacts.Photos.SYNC_ERROR); int updated = mContactsProvider.updateInTransaction(Data.CONTENT_URI, mValues, @@ -1182,22 +1182,22 @@ public class LegacyApiSupport { mValues2.clear(); mValues3.clear(); - OpenHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE, + ContactsDatabaseHelper.copyStringValue(mValues, RawContacts.CUSTOM_RINGTONE, values, People.CUSTOM_RINGTONE); - OpenHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL, + ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.SEND_TO_VOICEMAIL, values, People.SEND_TO_VOICEMAIL); - OpenHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED, + ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.LAST_TIME_CONTACTED, values, People.LAST_TIME_CONTACTED); - OpenHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED, + ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.TIMES_CONTACTED, values, People.TIMES_CONTACTED); - OpenHelper.copyLongValue(mValues, RawContacts.STARRED, + ContactsDatabaseHelper.copyLongValue(mValues, RawContacts.STARRED, values, People.STARRED); 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); - OpenHelper.copyStringValue(mValues2, StructuredName.DISPLAY_NAME, + ContactsDatabaseHelper.copyStringValue(mValues2, StructuredName.DISPLAY_NAME, values, People.NAME); if (values.containsKey(People.PHONETIC_NAME)) { String phoneticName = values.getAsString(People.PHONETIC_NAME); @@ -1211,7 +1211,7 @@ public class LegacyApiSupport { if (values.containsKey(People.NOTES)) { mValues3.put(Data.MIMETYPE, Note.CONTENT_ITEM_TYPE); - OpenHelper.copyStringValue(mValues3, Note.NOTE, values, People.NOTES); + ContactsDatabaseHelper.copyStringValue(mValues3, Note.NOTE, values, People.NOTES); } } @@ -1220,19 +1220,19 @@ public class LegacyApiSupport { mValues.put(Data.MIMETYPE, Organization.CONTENT_ITEM_TYPE); - OpenHelper.copyLongValue(mValues, Data.IS_PRIMARY, + ContactsDatabaseHelper.copyLongValue(mValues, Data.IS_PRIMARY, values, android.provider.Contacts.Organizations.ISPRIMARY); - OpenHelper.copyStringValue(mValues, Organization.COMPANY, + 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 - OpenHelper.copyLongValue(mValues, Organization.TYPE, + ContactsDatabaseHelper.copyLongValue(mValues, Organization.TYPE, values, android.provider.Contacts.Organizations.TYPE); - OpenHelper.copyStringValue(mValues, Organization.LABEL, + ContactsDatabaseHelper.copyStringValue(mValues, Organization.LABEL, values, android.provider.Contacts.Organizations.LABEL); - OpenHelper.copyStringValue(mValues, Organization.TITLE, + ContactsDatabaseHelper.copyStringValue(mValues, Organization.TITLE, values, android.provider.Contacts.Organizations.TITLE); } @@ -1241,30 +1241,32 @@ public class LegacyApiSupport { mValues.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); - OpenHelper.copyLongValue(mValues, Data.IS_PRIMARY, + ContactsDatabaseHelper.copyLongValue(mValues, Data.IS_PRIMARY, values, android.provider.Contacts.Phones.ISPRIMARY); - OpenHelper.copyStringValue(mValues, Phone.NUMBER, + 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 - OpenHelper.copyLongValue(mValues, Phone.TYPE, + ContactsDatabaseHelper.copyLongValue(mValues, Phone.TYPE, values, android.provider.Contacts.Phones.TYPE); - OpenHelper.copyStringValue(mValues, Phone.LABEL, + ContactsDatabaseHelper.copyStringValue(mValues, Phone.LABEL, values, android.provider.Contacts.Phones.LABEL); } private void parseContactMethodValues(int kind, ContentValues values) { mValues.clear(); - OpenHelper.copyLongValue(mValues, Data.IS_PRIMARY, values, ContactMethods.ISPRIMARY); + 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); - OpenHelper.copyStringValue(mValues, Email.DATA, values, ContactMethods.DATA); + ContactsDatabaseHelper.copyStringValue(mValues, Email.DATA, values, + ContactMethods.DATA); break; } @@ -1284,8 +1286,8 @@ public class LegacyApiSupport { case android.provider.Contacts.KIND_POSTAL: { copyCommonFields(values, StructuredPostal.CONTENT_ITEM_TYPE, StructuredPostal.TYPE, StructuredPostal.LABEL, Data.DATA14); - OpenHelper.copyStringValue(mValues, StructuredPostal.FORMATTED_ADDRESS, values, - ContactMethods.DATA); + ContactsDatabaseHelper.copyStringValue(mValues, StructuredPostal.FORMATTED_ADDRESS, + values, ContactMethods.DATA); break; } } @@ -1294,26 +1296,29 @@ public class LegacyApiSupport { private void copyCommonFields(ContentValues values, String mimeType, String typeColumn, String labelColumn, String auxDataColumn) { mValues.put(Data.MIMETYPE, mimeType); - OpenHelper.copyLongValue(mValues, typeColumn, values, ContactMethods.TYPE); - OpenHelper.copyStringValue(mValues, labelColumn, values, ContactMethods.LABEL); - OpenHelper.copyStringValue(mValues, auxDataColumn, values, ContactMethods.AUX_DATA); + 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(); - OpenHelper.copyStringValue(mValues, Groups.TITLE, + ContactsDatabaseHelper.copyStringValue(mValues, Groups.TITLE, values, android.provider.Contacts.Groups.NAME); - OpenHelper.copyStringValue(mValues, Groups.NOTES, + ContactsDatabaseHelper.copyStringValue(mValues, Groups.NOTES, values, android.provider.Contacts.Groups.NOTES); - OpenHelper.copyStringValue(mValues, Groups.SYSTEM_ID, + ContactsDatabaseHelper.copyStringValue(mValues, Groups.SYSTEM_ID, values, android.provider.Contacts.Groups.SYSTEM_ID); } private void parseExtensionValues(ContentValues values) { - OpenHelper.copyStringValue(mValues, ExtensionsColumns.NAME, + ContactsDatabaseHelper.copyStringValue(mValues, ExtensionsColumns.NAME, values, android.provider.Contacts.People.Extensions.NAME); - OpenHelper.copyStringValue(mValues, ExtensionsColumns.VALUE, + ContactsDatabaseHelper.copyStringValue(mValues, ExtensionsColumns.VALUE, values, android.provider.Contacts.People.Extensions.VALUE); } @@ -1420,7 +1425,7 @@ public class LegacyApiSupport { String sortOrder, String limit) { ensureDefaultAccount(); - final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + final SQLiteDatabase db = mDbHelper.getReadableDatabase(); SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); String groupBy = null; @@ -1536,7 +1541,7 @@ public class LegacyApiSupport { if (uri.getPathSegments().size() > 2) { String filterParam = uri.getLastPathSegment(); qb.appendWhere(" AND person ="); - qb.appendWhere(mOpenHelper.buildPhoneLookupAsNestedQuery(filterParam)); + qb.appendWhere(mDbHelper.buildPhoneLookupAsNestedQuery(filterParam)); } break; diff --git a/src/com/android/providers/contacts/LegacyContactImporter.java b/src/com/android/providers/contacts/LegacyContactImporter.java index 458096d9..47bc233e 100644 --- a/src/com/android/providers/contacts/LegacyContactImporter.java +++ b/src/com/android/providers/contacts/LegacyContactImporter.java @@ -16,11 +16,11 @@ package com.android.providers.contacts; -import com.android.providers.contacts.OpenHelper.DataColumns; -import com.android.providers.contacts.OpenHelper.PhoneColumns; -import com.android.providers.contacts.OpenHelper.PhoneLookupColumns; -import com.android.providers.contacts.OpenHelper.RawContactsColumns; -import com.android.providers.contacts.OpenHelper.Tables; +import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.PhoneColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.PhoneLookupColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.RawContactsColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.Tables; import android.content.ContentResolver; import android.content.ContentUris; @@ -65,7 +65,7 @@ public class LegacyContactImporter { private final Context mContext; private final ContactsProvider2 mContactsProvider; - private OpenHelper mOpenHelper; + private ContactsDatabaseHelper mDbHelper; private ContentValues mValues = new ContentValues(); private ContentResolver mResolver; private boolean mPhoneticNameAvailable = true; @@ -141,8 +141,8 @@ public class LegacyContactImporter { mPhoneticNameAvailable = false; } - mOpenHelper = (OpenHelper)mContactsProvider.getOpenHelper(); - mTargetDb = mOpenHelper.getWritableDatabase(); + mDbHelper = (ContactsDatabaseHelper)mContactsProvider.getDatabaseHelper(); + mTargetDb = mDbHelper.getWritableDatabase(); /* * At this point there should be no data in the contacts provider, but in case @@ -152,16 +152,16 @@ public class LegacyContactImporter { */ mContactsProvider.wipeData(); - mStructuredNameMimetypeId = mOpenHelper.getMimeTypeId(StructuredName.CONTENT_ITEM_TYPE); - mNoteMimetypeId = mOpenHelper.getMimeTypeId(Note.CONTENT_ITEM_TYPE); - mOrganizationMimetypeId = mOpenHelper.getMimeTypeId(Organization.CONTENT_ITEM_TYPE); - mPhoneMimetypeId = mOpenHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE); - mEmailMimetypeId = mOpenHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE); - mImMimetypeId = mOpenHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE); - mPostalMimetypeId = mOpenHelper.getMimeTypeId(StructuredPostal.CONTENT_ITEM_TYPE); - mPhotoMimetypeId = mOpenHelper.getMimeTypeId(Photo.CONTENT_ITEM_TYPE); + mStructuredNameMimetypeId = mDbHelper.getMimeTypeId(StructuredName.CONTENT_ITEM_TYPE); + mNoteMimetypeId = mDbHelper.getMimeTypeId(Note.CONTENT_ITEM_TYPE); + mOrganizationMimetypeId = mDbHelper.getMimeTypeId(Organization.CONTENT_ITEM_TYPE); + mPhoneMimetypeId = mDbHelper.getMimeTypeId(Phone.CONTENT_ITEM_TYPE); + mEmailMimetypeId = mDbHelper.getMimeTypeId(Email.CONTENT_ITEM_TYPE); + mImMimetypeId = mDbHelper.getMimeTypeId(Im.CONTENT_ITEM_TYPE); + mPostalMimetypeId = mDbHelper.getMimeTypeId(StructuredPostal.CONTENT_ITEM_TYPE); + mPhotoMimetypeId = mDbHelper.getMimeTypeId(Photo.CONTENT_ITEM_TYPE); mGroupMembershipMimetypeId = - mOpenHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE); + mDbHelper.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE); mNameSplitter = mContactsProvider.getNameSplitter(); @@ -179,7 +179,7 @@ public class LegacyContactImporter { // will be autoincremented importDeletedPeople(); - mOpenHelper.updateAllVisible(); + mDbHelper.updateAllVisible(); mTargetDb.setTransactionSuccessful(); mTargetDb.endTransaction(); diff --git a/src/com/android/providers/contacts/NameLookupBuilder.java b/src/com/android/providers/contacts/NameLookupBuilder.java index 9229a047..d1588b5d 100644 --- a/src/com/android/providers/contacts/NameLookupBuilder.java +++ b/src/com/android/providers/contacts/NameLookupBuilder.java @@ -16,7 +16,7 @@ package com.android.providers.contacts; -import com.android.providers.contacts.OpenHelper.NameLookupType; +import com.android.providers.contacts.ContactsDatabaseHelper.NameLookupType; import java.util.Arrays; import java.util.Comparator; diff --git a/src/com/android/providers/contacts/OpenHelper.java b/src/com/android/providers/contacts/OpenHelper.java deleted file mode 100644 index 755484c7..00000000 --- a/src/com/android/providers/contacts/OpenHelper.java +++ /dev/null @@ -1,1715 +0,0 @@ -/* - * 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 com.android.internal.content.SyncStateContentProviderHelper; - -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; -import android.database.DatabaseUtils; -import android.database.SQLException; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteDoneException; -import android.database.sqlite.SQLiteException; -import android.database.sqlite.SQLiteOpenHelper; -import android.database.sqlite.SQLiteQueryBuilder; -import android.database.sqlite.SQLiteStatement; -import android.os.Binder; -import android.os.Bundle; -import android.provider.BaseColumns; -import android.provider.ContactsContract; -import android.provider.CallLog.Calls; -import android.provider.Contacts.People; -import android.provider.ContactsContract.AggregationExceptions; -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.provider.ContactsContract.CommonDataKinds.GroupMembership; -import android.provider.ContactsContract.CommonDataKinds.Phone; -import android.provider.SocialContract.Activities; -import android.telephony.PhoneNumberUtils; -import android.util.Log; - -import java.util.HashMap; - -/** - * Database open helper for contacts and social activity data. Designed as a - * singleton to make sure that all {@link android.content.ContentProvider} users get the same - * reference. Provides handy methods for maintaining package and mime-type - * lookup tables. - */ -/* package */ class OpenHelper extends SQLiteOpenHelper { - private static final String TAG = "OpenHelper"; - - private static final int DATABASE_VERSION = 98; - - private static final String DATABASE_NAME = "contacts2.db"; - private static final String DATABASE_PRESENCE = "presence_db"; - - public interface Tables { - public static final String CONTACTS = "contacts"; - public static final String RAW_CONTACTS = "raw_contacts"; - public static final String PACKAGES = "packages"; - public static final String MIMETYPES = "mimetypes"; - public static final String PHONE_LOOKUP = "phone_lookup"; - public static final String NAME_LOOKUP = "name_lookup"; - public static final String AGGREGATION_EXCEPTIONS = "agg_exceptions"; - public static final String SETTINGS = "settings"; - public static final String DATA = "data"; - public static final String GROUPS = "groups"; - public static final String PRESENCE = "presence"; - public static final String AGGREGATED_PRESENCE = "agg_presence"; - public static final String NICKNAME_LOOKUP = "nickname_lookup"; - public static final String CALLS = "calls"; - public static final String CONTACT_ENTITIES = "contact_entities_view"; - public static final String CONTACT_ENTITIES_RESTRICTED = "contact_entities_view_restricted"; - public static final String STATUS_UPDATES = "status_updates"; - - public static final String DATA_JOIN_MIMETYPES = "data " - + "JOIN mimetypes ON (data.mimetype_id = mimetypes._id)"; - - public static final String DATA_JOIN_RAW_CONTACTS = "data " - + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id)"; - - public static final String DATA_JOIN_MIMETYPE_RAW_CONTACTS = "data " - + "JOIN mimetypes ON (data.mimetype_id = mimetypes._id) " - + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id)"; - - public static final String DATA_JOIN_RAW_CONTACTS_GROUPS = "data " - + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id)" - + "LEFT OUTER JOIN groups ON (groups._id = data." + GroupMembership.GROUP_ROW_ID - + ")"; - - public static final String DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS = "data " - + "JOIN mimetypes ON (data.mimetype_id = mimetypes._id) " - + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) " - + "LEFT OUTER JOIN packages ON (data.package_id = packages._id)"; - - public static final String DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_CONTACTS = "data " - + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) " - + "JOIN mimetypes ON (data.mimetype_id = mimetypes._id) " - + "LEFT OUTER JOIN packages ON (data.package_id = packages._id) " - + "LEFT OUTER JOIN contacts ON (raw_contacts.contact_id = contacts._id)"; - - public static final String RAW_CONTACTS_JOIN_CONTACTS = "raw_contacts " - + "LEFT OUTER JOIN contacts ON (raw_contacts.contact_id = contacts._id)"; - - // NOTE: This requires late binding of GroupMembership MIME-type - public static final String RAW_CONTACTS_JOIN_SETTINGS_DATA_GROUPS = "raw_contacts " - + "LEFT OUTER JOIN settings ON (" - + "raw_contacts.account_name = settings.account_name AND " - + "raw_contacts.account_type = settings.account_type) " - + "LEFT OUTER JOIN data ON (data.mimetype_id=? AND " - + "data.raw_contact_id = raw_contacts._id) " - + "LEFT OUTER JOIN groups ON (groups._id = data." + GroupMembership.GROUP_ROW_ID - + ")"; - - // NOTE: This requires late binding of GroupMembership MIME-type - public static final String SETTINGS_JOIN_RAW_CONTACTS_DATA_MIMETYPES_CONTACTS = "settings " - + "LEFT OUTER JOIN raw_contacts ON (" - + "raw_contacts.account_name = settings.account_name AND " - + "raw_contacts.account_type = settings.account_type) " - + "LEFT OUTER JOIN data ON (data.mimetype_id=? AND " - + "data.raw_contact_id = raw_contacts._id) " - + "LEFT OUTER JOIN contacts ON (raw_contacts.contact_id = contacts._id)"; - - public static final String DATA_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS = "data " - + "JOIN mimetypes ON (data.mimetype_id = mimetypes._id) " - + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) " - + "LEFT OUTER JOIN contacts ON (raw_contacts.contact_id = contacts._id)"; - - public static final String DATA_INNER_JOIN_MIMETYPES_RAW_CONTACTS_CONTACTS = "data " - + "JOIN mimetypes ON (data.mimetype_id = mimetypes._id) " - + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) " - + "JOIN contacts ON (raw_contacts.contact_id = contacts._id)"; - - public static final String DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_CONTACTS_GROUPS = - "data " - + "JOIN mimetypes ON (data.mimetype_id = mimetypes._id) " - + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) " - + "LEFT OUTER JOIN packages ON (data.package_id = packages._id) " - + "LEFT OUTER JOIN groups " - + " ON (mimetypes.mimetype='" + GroupMembership.CONTENT_ITEM_TYPE + "' " - + " AND groups._id = data." + GroupMembership.GROUP_ROW_ID + ") " - + "LEFT OUTER JOIN contacts ON (raw_contacts.contact_id = contacts._id)"; - - public static final String DATA_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_GROUPS = "data " - + "JOIN mimetypes ON (data.mimetype_id = mimetypes._id) " - + "JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) " - + "LEFT OUTER JOIN packages ON (data.package_id = packages._id) " - + "LEFT OUTER JOIN groups " - + " ON (mimetypes.mimetype='" + GroupMembership.CONTENT_ITEM_TYPE + "' " - + " AND groups._id = data." + GroupMembership.GROUP_ROW_ID + ") "; - - public static final String GROUPS_JOIN_PACKAGES = "groups " - + "LEFT OUTER JOIN packages ON (groups.package_id = packages._id)"; - - public static final String GROUPS_JOIN_PACKAGES_DATA_RAW_CONTACTS_CONTACTS = "groups " - + "LEFT OUTER JOIN packages ON (groups.package_id = packages._id) " - + "LEFT OUTER JOIN data " - + " ON (groups._id = data." + GroupMembership.GROUP_ROW_ID + ") " - + "LEFT OUTER JOIN raw_contacts ON (data.raw_contact_id = raw_contacts._id) " - + "LEFT OUTER JOIN contacts ON (raw_contacts.contact_id = contacts._id)"; - - public static final String ACTIVITIES = "activities"; - - public static final String ACTIVITIES_JOIN_MIMETYPES = "activities " - + "LEFT OUTER JOIN mimetypes ON (activities.mimetype_id = mimetypes._id)"; - - public static final String ACTIVITIES_JOIN_PACKAGES_MIMETYPES_RAW_CONTACTS_CONTACTS = - "activities " - + "LEFT OUTER JOIN packages ON (activities.package_id = packages._id) " - + "LEFT OUTER JOIN mimetypes ON (activities.mimetype_id = mimetypes._id) " - + "LEFT OUTER JOIN raw_contacts ON (activities.author_contact_id = " + - "raw_contacts._id) " - + "LEFT OUTER JOIN contacts ON (raw_contacts.contact_id = contacts._id)"; - - public static final String NAME_LOOKUP_JOIN_RAW_CONTACTS = "name_lookup " - + "INNER JOIN raw_contacts ON (name_lookup.raw_contact_id = raw_contacts._id)"; - } - - public interface Views { - public static final String DATA_ALL = "view_data"; - public static final String DATA_RESTRICTED = "view_data_restricted"; - - public static final String RAW_CONTACTS_ALL = "view_raw_contacts"; - public static final String RAW_CONTACTS_RESTRICTED = "view_raw_contacts_restricted"; - - public static final String CONTACTS_ALL = "view_contacts"; - public static final String CONTACTS_RESTRICTED = "view_contacts_restricted"; - - public static final String GROUPS_ALL = "view_groups"; - } - - public interface Clauses { - final String MIMETYPE_IS_GROUP_MEMBERSHIP = MimetypesColumns.CONCRETE_MIMETYPE + "='" - + GroupMembership.CONTENT_ITEM_TYPE + "'"; - - final String BELONGS_TO_GROUP = DataColumns.CONCRETE_GROUP_ID + "=" - + GroupsColumns.CONCRETE_ID; - - final String HAVING_NO_GROUPS = "COUNT(" + DataColumns.CONCRETE_GROUP_ID + ") == 0"; - - final String GROUP_BY_ACCOUNT_CONTACT_ID = SettingsColumns.CONCRETE_ACCOUNT_NAME + "," - + SettingsColumns.CONCRETE_ACCOUNT_TYPE + "," + RawContacts.CONTACT_ID; - - final String RAW_CONTACT_IS_LOCAL = RawContactsColumns.CONCRETE_ACCOUNT_NAME - + " IS NULL AND " + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " IS NULL"; - - final String ZERO_GROUP_MEMBERSHIPS = "COUNT(" + GroupsColumns.CONCRETE_ID + ")=0"; - - final String OUTER_RAW_CONTACTS = "outer_raw_contacts"; - final String OUTER_RAW_CONTACTS_ID = OUTER_RAW_CONTACTS + "." + RawContacts._ID; - - final String CONTACT_IS_VISIBLE = - "SELECT " + - "MAX((SELECT (CASE WHEN " + - "(CASE" + - " WHEN " + RAW_CONTACT_IS_LOCAL + - " THEN 1 " + - " WHEN " + ZERO_GROUP_MEMBERSHIPS + - " THEN " + Settings.UNGROUPED_VISIBLE + - " ELSE MAX(" + Groups.GROUP_VISIBLE + ")" + - "END)=1 THEN 1 ELSE 0 END)" + - " FROM " + Tables.RAW_CONTACTS_JOIN_SETTINGS_DATA_GROUPS + - " WHERE " + RawContactsColumns.CONCRETE_ID + "=" + OUTER_RAW_CONTACTS_ID + "))" + - " FROM " + Tables.RAW_CONTACTS + " AS " + OUTER_RAW_CONTACTS + - " WHERE " + RawContacts.CONTACT_ID + "=" + ContactsColumns.CONCRETE_ID + - " GROUP BY " + RawContacts.CONTACT_ID; - - final String GROUP_HAS_ACCOUNT_AND_SOURCE_ID = Groups.SOURCE_ID + "=? AND " - + Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=?"; - } - - public interface ContactsColumns { - /** - * This flag is set for a contact if it has only one constituent raw contact and - * it is restricted. - */ - public static final String SINGLE_IS_RESTRICTED = "single_is_restricted"; - - public static final String LAST_STATUS_UPDATE_ID = "status_update_id"; - - public static final String CONCRETE_ID = Tables.CONTACTS + "." + BaseColumns._ID; - public static final String CONCRETE_DISPLAY_NAME = Tables.CONTACTS + "." - + Contacts.DISPLAY_NAME; - - public static final String CONCRETE_TIMES_CONTACTED = Tables.CONTACTS + "." - + Contacts.TIMES_CONTACTED; - public static final String CONCRETE_LAST_TIME_CONTACTED = Tables.CONTACTS + "." - + Contacts.LAST_TIME_CONTACTED; - public static final String CONCRETE_STARRED = Tables.CONTACTS + "." + Contacts.STARRED; - public static final String CONCRETE_CUSTOM_RINGTONE = Tables.CONTACTS + "." - + Contacts.CUSTOM_RINGTONE; - public static final String CONCRETE_SEND_TO_VOICEMAIL = Tables.CONTACTS + "." - + Contacts.SEND_TO_VOICEMAIL; - } - - public interface RawContactsColumns { - public static final String CONCRETE_ID = - Tables.RAW_CONTACTS + "." + BaseColumns._ID; - public static final String CONCRETE_ACCOUNT_NAME = - Tables.RAW_CONTACTS + "." + RawContacts.ACCOUNT_NAME; - public static final String CONCRETE_ACCOUNT_TYPE = - Tables.RAW_CONTACTS + "." + RawContacts.ACCOUNT_TYPE; - public static final String CONCRETE_SOURCE_ID = - Tables.RAW_CONTACTS + "." + RawContacts.SOURCE_ID; - public static final String CONCRETE_VERSION = - Tables.RAW_CONTACTS + "." + RawContacts.VERSION; - public static final String CONCRETE_DIRTY = - Tables.RAW_CONTACTS + "." + RawContacts.DIRTY; - public static final String CONCRETE_DELETED = - Tables.RAW_CONTACTS + "." + RawContacts.DELETED; - public static final String CONCRETE_SYNC1 = - Tables.RAW_CONTACTS + "." + RawContacts.SYNC1; - public static final String CONCRETE_SYNC2 = - Tables.RAW_CONTACTS + "." + RawContacts.SYNC2; - public static final String CONCRETE_SYNC3 = - Tables.RAW_CONTACTS + "." + RawContacts.SYNC3; - public static final String CONCRETE_SYNC4 = - Tables.RAW_CONTACTS + "." + RawContacts.SYNC4; - public static final String CONCRETE_STARRED = - Tables.RAW_CONTACTS + "." + RawContacts.STARRED; - - public static final String DISPLAY_NAME = "display_name"; - public static final String DISPLAY_NAME_SOURCE = "display_name_source"; - public static final String AGGREGATION_NEEDED = "aggregation_needed"; - } - - /** - * Types of data used to produce the display name for a contact. Listed in the order - * of increasing priority. - */ - public interface DisplayNameSources { - int UNDEFINED = 0; - int EMAIL = 10; - int PHONE = 20; - int ORGANIZATION = 30; - int NICKNAME = 35; - int STRUCTURED_NAME = 40; - } - - public interface DataColumns { - public static final String PACKAGE_ID = "package_id"; - public static final String MIMETYPE_ID = "mimetype_id"; - - public static final String CONCRETE_ID = Tables.DATA + "." + BaseColumns._ID; - public static final String CONCRETE_MIMETYPE_ID = Tables.DATA + "." + MIMETYPE_ID; - public static final String CONCRETE_RAW_CONTACT_ID = Tables.DATA + "." - + Data.RAW_CONTACT_ID; - public static final String CONCRETE_GROUP_ID = Tables.DATA + "." - + GroupMembership.GROUP_ROW_ID; - - public static final String CONCRETE_DATA1 = Tables.DATA + "." + Data.DATA1; - public static final String CONCRETE_DATA2 = Tables.DATA + "." + Data.DATA2; - public static final String CONCRETE_DATA3 = Tables.DATA + "." + Data.DATA3; - public static final String CONCRETE_DATA4 = Tables.DATA + "." + Data.DATA4; - public static final String CONCRETE_DATA5 = Tables.DATA + "." + Data.DATA5; - public static final String CONCRETE_DATA6 = Tables.DATA + "." + Data.DATA6; - public static final String CONCRETE_DATA7 = Tables.DATA + "." + Data.DATA7; - public static final String CONCRETE_DATA8 = Tables.DATA + "." + Data.DATA8; - public static final String CONCRETE_DATA9 = Tables.DATA + "." + Data.DATA9; - public static final String CONCRETE_DATA10 = Tables.DATA + "." + Data.DATA10; - public static final String CONCRETE_DATA11 = Tables.DATA + "." + Data.DATA11; - public static final String CONCRETE_DATA12 = Tables.DATA + "." + Data.DATA12; - public static final String CONCRETE_DATA13 = Tables.DATA + "." + Data.DATA13; - public static final String CONCRETE_DATA14 = Tables.DATA + "." + Data.DATA14; - public static final String CONCRETE_DATA15 = Tables.DATA + "." + Data.DATA15; - public static final String CONCRETE_IS_PRIMARY = Tables.DATA + "." + Data.IS_PRIMARY; - public static final String CONCRETE_PACKAGE_ID = Tables.DATA + "." + PACKAGE_ID; - } - - // Used only for legacy API support - public interface ExtensionsColumns { - public static final String NAME = Data.DATA1; - public static final String VALUE = Data.DATA2; - } - - public interface GroupMembershipColumns { - public static final String RAW_CONTACT_ID = Data.RAW_CONTACT_ID; - public static final String GROUP_ROW_ID = GroupMembership.GROUP_ROW_ID; - } - - public interface PhoneColumns { - public static final String NORMALIZED_NUMBER = Data.DATA4; - public static final String CONCRETE_NORMALIZED_NUMBER = DataColumns.CONCRETE_DATA4; - } - - public interface GroupsColumns { - public static final String PACKAGE_ID = "package_id"; - - public static final String CONCRETE_ID = Tables.GROUPS + "." + BaseColumns._ID; - public static final String CONCRETE_SOURCE_ID = Tables.GROUPS + "." + Groups.SOURCE_ID; - public static final String CONCRETE_ACCOUNT_NAME = Tables.GROUPS + "." + Groups.ACCOUNT_NAME; - public static final String CONCRETE_ACCOUNT_TYPE = Tables.GROUPS + "." + Groups.ACCOUNT_TYPE; - } - - public interface ActivitiesColumns { - public static final String PACKAGE_ID = "package_id"; - public static final String MIMETYPE_ID = "mimetype_id"; - } - - public interface PhoneLookupColumns { - public static final String _ID = BaseColumns._ID; - public static final String DATA_ID = "data_id"; - public static final String RAW_CONTACT_ID = "raw_contact_id"; - public static final String NORMALIZED_NUMBER = "normalized_number"; - } - - public interface NameLookupColumns { - public static final String RAW_CONTACT_ID = "raw_contact_id"; - public static final String DATA_ID = "data_id"; - public static final String NORMALIZED_NAME = "normalized_name"; - public static final String NAME_TYPE = "name_type"; - } - - public final static class NameLookupType { - public static final int NAME_EXACT = 0; - public static final int NAME_VARIANT = 1; - public static final int NAME_COLLATION_KEY = 2; - public static final int NICKNAME = 3; - public static final int EMAIL_BASED_NICKNAME = 4; - public static final int ORGANIZATION = 5; - - // This is the highest name lookup type code plus one - public static final int TYPE_COUNT = 6; - - public static boolean isBasedOnStructuredName(int nameLookupType) { - return nameLookupType == NameLookupType.NAME_EXACT - || nameLookupType == NameLookupType.NAME_VARIANT - || nameLookupType == NameLookupType.NAME_COLLATION_KEY; - } - } - - public interface PackagesColumns { - public static final String _ID = BaseColumns._ID; - public static final String PACKAGE = "package"; - - public static final String CONCRETE_ID = Tables.PACKAGES + "." + _ID; - } - - public interface MimetypesColumns { - public static final String _ID = BaseColumns._ID; - public static final String MIMETYPE = "mimetype"; - - public static final String CONCRETE_ID = Tables.MIMETYPES + "." + BaseColumns._ID; - public static final String CONCRETE_MIMETYPE = Tables.MIMETYPES + "." + MIMETYPE; - } - - public interface AggregationExceptionColumns { - public static final String _ID = BaseColumns._ID; - } - - public interface NicknameLookupColumns { - public static final String NAME = "name"; - public static final String CLUSTER = "cluster"; - } - - public interface SettingsColumns { - public static final String CONCRETE_ACCOUNT_NAME = Tables.SETTINGS + "." - + Settings.ACCOUNT_NAME; - public static final String CONCRETE_ACCOUNT_TYPE = Tables.SETTINGS + "." - + Settings.ACCOUNT_TYPE; - } - - public interface PresenceColumns { - String RAW_CONTACT_ID = "presence_raw_contact_id"; - String CONTACT_ID = "presence_contact_id"; - } - - public interface AggregatedPresenceColumns { - String CONTACT_ID = "presence_contact_id"; - } - - public interface StatusUpdatesColumns { - String DATA_ID = "status_update_data_id"; - } - - /** In-memory cache of previously found mimetype mappings */ - private final HashMap mMimetypeCache = new HashMap(); - /** In-memory cache of previously found package name mappings */ - private final HashMap mPackageCache = new HashMap(); - - - /** Compiled statements for querying and inserting mappings */ - private SQLiteStatement mMimetypeQuery; - private SQLiteStatement mPackageQuery; - private SQLiteStatement mContactIdQuery; - private SQLiteStatement mAggregationModeQuery; - private SQLiteStatement mMimetypeInsert; - private SQLiteStatement mPackageInsert; - private SQLiteStatement mDataMimetypeQuery; - private SQLiteStatement mActivitiesMimetypeQuery; - - private final Context mContext; - private final SyncStateContentProviderHelper mSyncState; - - - /** Compiled statements for updating {@link Contacts#IN_VISIBLE_GROUP}. */ - private SQLiteStatement mVisibleUpdate; - private SQLiteStatement mVisibleSpecificUpdate; - - private boolean mReopenDatabase = false; - - private static OpenHelper sSingleton = null; - - private boolean mUseStrictPhoneNumberComparation; - - public static synchronized OpenHelper getInstance(Context context) { - if (sSingleton == null) { - sSingleton = new OpenHelper(context); - } - return sSingleton; - } - - /** - * Private constructor, callers except unit tests should obtain an instance through - * {@link #getInstance(android.content.Context)} instead. - */ - /* package */ OpenHelper(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); - Log.i(TAG, "Creating OpenHelper"); - - mContext = context; - mSyncState = new SyncStateContentProviderHelper(); - mUseStrictPhoneNumberComparation = - context.getResources().getBoolean( - com.android.internal.R.bool.config_use_strict_phone_number_comparation); - } - - @Override - public void onOpen(SQLiteDatabase db) { - mSyncState.onDatabaseOpened(db); - - // Create compiled statements for package and mimetype lookups - mMimetypeQuery = db.compileStatement("SELECT " + MimetypesColumns._ID + " FROM " - + Tables.MIMETYPES + " WHERE " + MimetypesColumns.MIMETYPE + "=?"); - mPackageQuery = db.compileStatement("SELECT " + PackagesColumns._ID + " FROM " - + Tables.PACKAGES + " WHERE " + PackagesColumns.PACKAGE + "=?"); - mContactIdQuery = db.compileStatement("SELECT " + RawContacts.CONTACT_ID + " FROM " - + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?"); - mAggregationModeQuery = db.compileStatement("SELECT " + RawContacts.AGGREGATION_MODE - + " FROM " + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=?"); - mMimetypeInsert = db.compileStatement("INSERT INTO " + Tables.MIMETYPES + "(" - + MimetypesColumns.MIMETYPE + ") VALUES (?)"); - mPackageInsert = db.compileStatement("INSERT INTO " + Tables.PACKAGES + "(" - + PackagesColumns.PACKAGE + ") VALUES (?)"); - - mDataMimetypeQuery = db.compileStatement("SELECT " + MimetypesColumns.MIMETYPE + " FROM " - + Tables.DATA_JOIN_MIMETYPES + " WHERE " + Tables.DATA + "." + Data._ID + "=?"); - mActivitiesMimetypeQuery = db.compileStatement("SELECT " + MimetypesColumns.MIMETYPE - + " FROM " + Tables.ACTIVITIES_JOIN_MIMETYPES + " WHERE " + Tables.ACTIVITIES + "." - + Activities._ID + "=?"); - - // Compile statements for updating visibility - final String visibleUpdate = "UPDATE " + Tables.CONTACTS + " SET " - + Contacts.IN_VISIBLE_GROUP + "=(" + Clauses.CONTACT_IS_VISIBLE + ")"; - - mVisibleUpdate = db.compileStatement(visibleUpdate); - mVisibleSpecificUpdate = db.compileStatement(visibleUpdate + " WHERE " - + ContactsColumns.CONCRETE_ID + "=?"); - - db.execSQL("ATTACH DATABASE ':memory:' AS " + DATABASE_PRESENCE + ";"); - db.execSQL("CREATE TABLE IF NOT EXISTS " + DATABASE_PRESENCE + "." + Tables.PRESENCE + " ("+ - StatusUpdates.DATA_ID + " INTEGER PRIMARY KEY REFERENCES data(_id)," + - StatusUpdates.PROTOCOL + " INTEGER NOT NULL," + - StatusUpdates.CUSTOM_PROTOCOL + " TEXT," + - StatusUpdates.IM_HANDLE + " TEXT," + - StatusUpdates.IM_ACCOUNT + " TEXT," + - PresenceColumns.CONTACT_ID + " INTEGER REFERENCES contacts(_id)," + - PresenceColumns.RAW_CONTACT_ID + " INTEGER REFERENCES raw_contacts(_id)," + - StatusUpdates.PRESENCE + " INTEGER," + - "UNIQUE(" + StatusUpdates.PROTOCOL + ", " + StatusUpdates.CUSTOM_PROTOCOL - + ", " + StatusUpdates.IM_HANDLE + ", " + StatusUpdates.IM_ACCOUNT + ")" + - ");"); - - db.execSQL("CREATE INDEX IF NOT EXISTS " + DATABASE_PRESENCE + ".presenceIndex" + " ON " - + Tables.PRESENCE + " (" + PresenceColumns.RAW_CONTACT_ID + ");"); - - db.execSQL("CREATE TABLE IF NOT EXISTS " - + DATABASE_PRESENCE + "." + Tables.AGGREGATED_PRESENCE + " ("+ - AggregatedPresenceColumns.CONTACT_ID - + " INTEGER PRIMARY KEY REFERENCES contacts(_id)," + - StatusUpdates.PRESENCE_STATUS + " INTEGER" + - ");"); - - - db.execSQL("CREATE TRIGGER " + DATABASE_PRESENCE + "." + Tables.PRESENCE + "_deleted" - + " BEFORE DELETE ON " + DATABASE_PRESENCE + "." + Tables.PRESENCE - + " BEGIN " - + " DELETE FROM " + Tables.AGGREGATED_PRESENCE - + " WHERE " + AggregatedPresenceColumns.CONTACT_ID + " = " + - "(SELECT " + PresenceColumns.CONTACT_ID + - " FROM " + Tables.PRESENCE + - " WHERE " + PresenceColumns.RAW_CONTACT_ID - + "=OLD." + PresenceColumns.RAW_CONTACT_ID + - " AND NOT EXISTS" + - "(SELECT " + PresenceColumns.RAW_CONTACT_ID + - " FROM " + Tables.PRESENCE + - " WHERE " + PresenceColumns.CONTACT_ID - + "=OLD." + PresenceColumns.CONTACT_ID + - " AND " + PresenceColumns.RAW_CONTACT_ID - + "!=OLD." + PresenceColumns.RAW_CONTACT_ID + "));" - + " END"); - - String replaceAggregatePresenceSql = - "INSERT OR REPLACE INTO " + Tables.AGGREGATED_PRESENCE + "(" - + AggregatedPresenceColumns.CONTACT_ID + ", " - + StatusUpdates.PRESENCE_STATUS + ")" + - " SELECT " + PresenceColumns.CONTACT_ID + "," - + "MAX(" + StatusUpdates.PRESENCE_STATUS + ")" + - " FROM " + Tables.PRESENCE + - " WHERE " + PresenceColumns.CONTACT_ID - + "=NEW." + PresenceColumns.CONTACT_ID + ";"; - - db.execSQL("CREATE TRIGGER " + DATABASE_PRESENCE + "." + Tables.PRESENCE + "_inserted" - + " AFTER INSERT ON " + DATABASE_PRESENCE + "." + Tables.PRESENCE - + " BEGIN " - + replaceAggregatePresenceSql - + " END"); - - db.execSQL("CREATE TRIGGER " + DATABASE_PRESENCE + "." + Tables.PRESENCE + "_updated" - + " AFTER UPDATE ON " + DATABASE_PRESENCE + "." + Tables.PRESENCE - + " BEGIN " - + replaceAggregatePresenceSql - + " END"); - } - - @Override - public void onCreate(SQLiteDatabase db) { - Log.i(TAG, "Bootstrapping database"); - - mSyncState.createDatabase(db); - - // One row per group of contacts corresponding to the same person - db.execSQL("CREATE TABLE " + Tables.CONTACTS + " (" + - BaseColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - Contacts.DISPLAY_NAME + " TEXT," + - Contacts.PHOTO_ID + " INTEGER REFERENCES data(_id)," + - Contacts.CUSTOM_RINGTONE + " TEXT," + - Contacts.SEND_TO_VOICEMAIL + " INTEGER NOT NULL DEFAULT 0," + - Contacts.TIMES_CONTACTED + " INTEGER NOT NULL DEFAULT 0," + - Contacts.LAST_TIME_CONTACTED + " INTEGER," + - Contacts.STARRED + " INTEGER NOT NULL DEFAULT 0," + - Contacts.IN_VISIBLE_GROUP + " INTEGER NOT NULL DEFAULT 1," + - Contacts.HAS_PHONE_NUMBER + " INTEGER NOT NULL DEFAULT 0," + - Contacts.LOOKUP_KEY + " TEXT," + - ContactsColumns.LAST_STATUS_UPDATE_ID + " INTEGER REFERENCES data(_id)," + - ContactsColumns.SINGLE_IS_RESTRICTED + " INTEGER NOT NULL DEFAULT 0" + - ");"); - - db.execSQL("CREATE TRIGGER contacts_times_contacted UPDATE OF " + - Contacts.LAST_TIME_CONTACTED + " ON " + Tables.CONTACTS + " " + - "BEGIN " + - "UPDATE " + Tables.CONTACTS + " SET " - + Contacts.TIMES_CONTACTED + " = " + "" + - "(new." + Contacts.TIMES_CONTACTED + " + 1)" - + " WHERE _id = new._id;" + - "END"); - - db.execSQL("CREATE INDEX contacts_visible_index ON " + Tables.CONTACTS + " (" + - Contacts.IN_VISIBLE_GROUP + "," + - Contacts.DISPLAY_NAME + " COLLATE LOCALIZED" + - ");"); - - db.execSQL("CREATE INDEX contacts_has_phone_index ON " + Tables.CONTACTS + " (" + - Contacts.HAS_PHONE_NUMBER + - ");"); - - db.execSQL("CREATE INDEX contacts_restricted_index ON " + Tables.CONTACTS + " (" + - ContactsColumns.SINGLE_IS_RESTRICTED + - ");"); - - // Contacts table - db.execSQL("CREATE TABLE " + Tables.RAW_CONTACTS + " (" + - RawContacts._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - RawContacts.IS_RESTRICTED + " INTEGER DEFAULT 0," + - RawContacts.ACCOUNT_NAME + " STRING DEFAULT NULL, " + - RawContacts.ACCOUNT_TYPE + " STRING DEFAULT NULL, " + - RawContacts.SOURCE_ID + " TEXT," + - RawContacts.VERSION + " INTEGER NOT NULL DEFAULT 1," + - RawContacts.DIRTY + " INTEGER NOT NULL DEFAULT 0," + - RawContacts.DELETED + " INTEGER NOT NULL DEFAULT 0," + - RawContacts.CONTACT_ID + " INTEGER REFERENCES contacts(_id)," + - RawContacts.AGGREGATION_MODE + " INTEGER NOT NULL DEFAULT " + - RawContacts.AGGREGATION_MODE_DEFAULT + "," + - RawContactsColumns.AGGREGATION_NEEDED + " INTEGER NOT NULL DEFAULT 1," + - RawContacts.CUSTOM_RINGTONE + " TEXT," + - RawContacts.SEND_TO_VOICEMAIL + " INTEGER NOT NULL DEFAULT 0," + - RawContacts.TIMES_CONTACTED + " INTEGER NOT NULL DEFAULT 0," + - RawContacts.LAST_TIME_CONTACTED + " INTEGER," + - RawContacts.STARRED + " INTEGER NOT NULL DEFAULT 0," + - RawContactsColumns.DISPLAY_NAME + " TEXT," + - RawContactsColumns.DISPLAY_NAME_SOURCE + " INTEGER NOT NULL DEFAULT " + - DisplayNameSources.UNDEFINED + "," + - RawContacts.SYNC1 + " TEXT, " + - RawContacts.SYNC2 + " TEXT, " + - RawContacts.SYNC3 + " TEXT, " + - RawContacts.SYNC4 + " TEXT " + - ");"); - - db.execSQL("CREATE TRIGGER raw_contacts_times_contacted UPDATE OF " + - RawContacts.LAST_TIME_CONTACTED + " ON " + Tables.RAW_CONTACTS + " " + - "BEGIN " + - "UPDATE " + Tables.RAW_CONTACTS + " SET " - + RawContacts.TIMES_CONTACTED + " = " + "" + - "(new." + RawContacts.TIMES_CONTACTED + " + 1)" - + " WHERE _id = new._id;" + - "END"); - - db.execSQL("CREATE INDEX raw_contacts_contact_id_index ON " + Tables.RAW_CONTACTS + " (" + - RawContacts.CONTACT_ID + - ");"); - - db.execSQL("CREATE INDEX raw_contacts_source_id_index ON " + Tables.RAW_CONTACTS + " (" + - RawContacts.SOURCE_ID + ", " + - RawContacts.ACCOUNT_TYPE + ", " + - RawContacts.ACCOUNT_NAME + - ");"); - - // TODO readd the index and investigate a controlled use of it -// db.execSQL("CREATE INDEX raw_contacts_agg_index ON " + Tables.RAW_CONTACTS + " (" + -// RawContactsColumns.AGGREGATION_NEEDED + -// ");"); - - // Package name mapping table - db.execSQL("CREATE TABLE " + Tables.PACKAGES + " (" + - PackagesColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - PackagesColumns.PACKAGE + " TEXT NOT NULL" + - ");"); - - // Mimetype mapping table - db.execSQL("CREATE TABLE " + Tables.MIMETYPES + " (" + - MimetypesColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - MimetypesColumns.MIMETYPE + " TEXT NOT NULL" + - ");"); - - // Public generic data table - db.execSQL("CREATE TABLE " + Tables.DATA + " (" + - Data._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - DataColumns.PACKAGE_ID + " INTEGER REFERENCES package(_id)," + - DataColumns.MIMETYPE_ID + " INTEGER REFERENCES mimetype(_id) NOT NULL," + - Data.RAW_CONTACT_ID + " INTEGER REFERENCES raw_contacts(_id) NOT NULL," + - Data.IS_PRIMARY + " INTEGER NOT NULL DEFAULT 0," + - Data.IS_SUPER_PRIMARY + " INTEGER NOT NULL DEFAULT 0," + - Data.DATA_VERSION + " INTEGER NOT NULL DEFAULT 0," + - Data.DATA1 + " TEXT," + - Data.DATA2 + " TEXT," + - Data.DATA3 + " TEXT," + - Data.DATA4 + " TEXT," + - Data.DATA5 + " TEXT," + - Data.DATA6 + " TEXT," + - Data.DATA7 + " TEXT," + - Data.DATA8 + " TEXT," + - Data.DATA9 + " TEXT," + - Data.DATA10 + " TEXT," + - Data.DATA11 + " TEXT," + - Data.DATA12 + " TEXT," + - Data.DATA13 + " TEXT," + - Data.DATA14 + " TEXT," + - Data.DATA15 + " TEXT," + - Data.SYNC1 + " TEXT, " + - Data.SYNC2 + " TEXT, " + - Data.SYNC3 + " TEXT, " + - Data.SYNC4 + " TEXT " + - ");"); - - db.execSQL("CREATE INDEX data_raw_contact_id ON " + Tables.DATA + " (" + - Data.RAW_CONTACT_ID + - ");"); - - /** - * For email lookup and similar queries. - */ - db.execSQL("CREATE INDEX data_mimetype_data1_index ON " + Tables.DATA + " (" + - DataColumns.MIMETYPE_ID + "," + - Data.DATA1 + - ");"); - - /** - * Automatically delete Data rows when a raw contact is deleted. - */ - db.execSQL("CREATE TRIGGER " + Tables.RAW_CONTACTS + "_deleted " - + " BEFORE DELETE ON " + Tables.RAW_CONTACTS - + " BEGIN " - + " DELETE FROM " + Tables.DATA - + " WHERE " + Data.RAW_CONTACT_ID - + "=OLD." + RawContacts._ID + ";" - + " DELETE FROM " + Tables.PHONE_LOOKUP - + " WHERE " + PhoneLookupColumns.RAW_CONTACT_ID - + "=OLD." + RawContacts._ID + ";" - + " DELETE FROM " + Tables.NAME_LOOKUP - + " WHERE " + NameLookupColumns.RAW_CONTACT_ID - + "=OLD." + RawContacts._ID + ";" - + " DELETE FROM " + Tables.AGGREGATION_EXCEPTIONS - + " WHERE " + AggregationExceptions.RAW_CONTACT_ID1 - + "=OLD." + RawContacts._ID - + " OR " + AggregationExceptions.RAW_CONTACT_ID2 - + "=OLD." + RawContacts._ID + ";" - + " DELETE FROM " + Tables.CONTACTS - + " WHERE " + Contacts._ID + "=OLD." + RawContacts.CONTACT_ID - + " AND (SELECT COUNT(*) FROM " + Tables.RAW_CONTACTS - + " WHERE " + RawContacts.CONTACT_ID + "=OLD." + RawContacts.CONTACT_ID - + " )=1;" - + " END"); - - /** - * Triggers that update {@link RawContacts#VERSION} when the contact is - * marked for deletion or any time a data row is inserted, updated or - * deleted. - */ - db.execSQL("CREATE TRIGGER " + Tables.RAW_CONTACTS + "_marked_deleted " - + " BEFORE UPDATE ON " + Tables.RAW_CONTACTS - + " BEGIN " - + " UPDATE " + Tables.RAW_CONTACTS - + " SET " - + RawContacts.VERSION + "=OLD." + RawContacts.VERSION + "+1 " - + " WHERE " + RawContacts._ID + "=OLD." + RawContacts._ID - + " AND NEW." + RawContacts.DELETED + "!= OLD." + RawContacts.DELETED + ";" - + " END"); - - db.execSQL("CREATE TRIGGER " + Tables.DATA + "_updated BEFORE UPDATE ON " + Tables.DATA - + " BEGIN " - + " UPDATE " + Tables.DATA - + " SET " + Data.DATA_VERSION + "=OLD." + Data.DATA_VERSION + "+1 " - + " WHERE " + Data._ID + "=OLD." + Data._ID + ";" - + " UPDATE " + Tables.RAW_CONTACTS - + " SET " + RawContacts.VERSION + "=" + RawContacts.VERSION + "+1 " - + " WHERE " + RawContacts._ID + "=OLD." + Data.RAW_CONTACT_ID + ";" - + " END"); - - db.execSQL("CREATE TRIGGER " + Tables.DATA + "_deleted BEFORE DELETE ON " + Tables.DATA - + " BEGIN " - + " UPDATE " + Tables.RAW_CONTACTS - + " SET " + RawContacts.VERSION + "=" + RawContacts.VERSION + "+1 " - + " WHERE " + RawContacts._ID + "=OLD." + Data.RAW_CONTACT_ID + ";" - + " DELETE FROM " + Tables.PHONE_LOOKUP - + " WHERE " + PhoneLookupColumns.DATA_ID + "=OLD." + Data._ID + ";" - + " END"); - - // Private phone numbers table used for lookup - db.execSQL("CREATE TABLE " + Tables.PHONE_LOOKUP + " (" + - PhoneLookupColumns.DATA_ID - + " INTEGER PRIMARY KEY REFERENCES data(_id) NOT NULL," + - PhoneLookupColumns.RAW_CONTACT_ID - + " INTEGER REFERENCES raw_contacts(_id) NOT NULL," + - PhoneLookupColumns.NORMALIZED_NUMBER + " TEXT NOT NULL" + - ");"); - - db.execSQL("CREATE INDEX phone_lookup_index ON " + Tables.PHONE_LOOKUP + " (" + - PhoneLookupColumns.NORMALIZED_NUMBER + "," + - PhoneLookupColumns.RAW_CONTACT_ID + "," + - PhoneLookupColumns.DATA_ID + - ");"); - - // Private name/nickname table used for lookup - db.execSQL("CREATE TABLE " + Tables.NAME_LOOKUP + " (" + - NameLookupColumns.DATA_ID - + " INTEGER REFERENCES data(_id) NOT NULL," + - NameLookupColumns.RAW_CONTACT_ID - + " INTEGER REFERENCES raw_contacts(_id) NOT NULL," + - NameLookupColumns.NORMALIZED_NAME + " TEXT NOT NULL," + - NameLookupColumns.NAME_TYPE + " INTEGER NOT NULL," + - "PRIMARY KEY (" - + NameLookupColumns.DATA_ID + ", " - + NameLookupColumns.NORMALIZED_NAME + ", " - + NameLookupColumns.NAME_TYPE + ")" + - ");"); - - db.execSQL("CREATE INDEX name_lookup_index ON " + Tables.NAME_LOOKUP + " (" + - NameLookupColumns.NORMALIZED_NAME + "," + - NameLookupColumns.NAME_TYPE + ", " + - NameLookupColumns.RAW_CONTACT_ID + - ");"); - - db.execSQL("CREATE INDEX name_lookup_raw_contact_id_index ON " + Tables.NAME_LOOKUP + " (" + - NameLookupColumns.RAW_CONTACT_ID + - ");"); - - db.execSQL("CREATE TABLE " + Tables.NICKNAME_LOOKUP + " (" + - NicknameLookupColumns.NAME + " TEXT," + - NicknameLookupColumns.CLUSTER + " TEXT" + - ");"); - - db.execSQL("CREATE UNIQUE INDEX nickname_lookup_index ON " + Tables.NICKNAME_LOOKUP + " (" + - NicknameLookupColumns.NAME + ", " + - NicknameLookupColumns.CLUSTER + - ");"); - - // Groups table - db.execSQL("CREATE TABLE " + Tables.GROUPS + " (" + - Groups._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - GroupsColumns.PACKAGE_ID + " INTEGER REFERENCES package(_id)," + - Groups.ACCOUNT_NAME + " STRING DEFAULT NULL, " + - Groups.ACCOUNT_TYPE + " STRING DEFAULT NULL, " + - Groups.SOURCE_ID + " TEXT," + - Groups.VERSION + " INTEGER NOT NULL DEFAULT 1," + - Groups.DIRTY + " INTEGER NOT NULL DEFAULT 0," + - Groups.TITLE + " TEXT," + - Groups.TITLE_RES + " INTEGER," + - Groups.NOTES + " TEXT," + - Groups.SYSTEM_ID + " TEXT," + - Groups.DELETED + " INTEGER NOT NULL DEFAULT 0," + - Groups.GROUP_VISIBLE + " INTEGER NOT NULL DEFAULT 0," + - Groups.SHOULD_SYNC + " INTEGER NOT NULL DEFAULT 1," + - Groups.SYNC1 + " TEXT, " + - Groups.SYNC2 + " TEXT, " + - Groups.SYNC3 + " TEXT, " + - Groups.SYNC4 + " TEXT " + - ");"); - - db.execSQL("CREATE INDEX groups_source_id_index ON " + Tables.GROUPS + " (" + - Groups.SOURCE_ID + ", " + - Groups.ACCOUNT_TYPE + ", " + - Groups.ACCOUNT_NAME + - ");"); - - db.execSQL("CREATE TRIGGER " + Tables.GROUPS + "_updated1 " - + " BEFORE UPDATE ON " + Tables.GROUPS - + " BEGIN " - + " UPDATE " + Tables.GROUPS - + " SET " - + Groups.VERSION + "=OLD." + Groups.VERSION + "+1" - + " WHERE " + Groups._ID + "=OLD." + Groups._ID + ";" - + " END"); - - db.execSQL("CREATE TABLE IF NOT EXISTS " + Tables.AGGREGATION_EXCEPTIONS + " (" + - AggregationExceptionColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - AggregationExceptions.TYPE + " INTEGER NOT NULL, " + - AggregationExceptions.RAW_CONTACT_ID1 - + " INTEGER REFERENCES raw_contacts(_id), " + - AggregationExceptions.RAW_CONTACT_ID2 - + " INTEGER REFERENCES raw_contacts(_id)" + - ");"); - - db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS aggregation_exception_index1 ON " + - Tables.AGGREGATION_EXCEPTIONS + " (" + - AggregationExceptions.RAW_CONTACT_ID1 + ", " + - AggregationExceptions.RAW_CONTACT_ID2 + - ");"); - - db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS aggregation_exception_index2 ON " + - Tables.AGGREGATION_EXCEPTIONS + " (" + - AggregationExceptions.RAW_CONTACT_ID2 + ", " + - AggregationExceptions.RAW_CONTACT_ID1 + - ");"); - - db.execSQL("CREATE TABLE IF NOT EXISTS " + Tables.SETTINGS + " (" + - Settings.ACCOUNT_NAME + " STRING NOT NULL," + - Settings.ACCOUNT_TYPE + " STRING NOT NULL," + - Settings.UNGROUPED_VISIBLE + " INTEGER NOT NULL DEFAULT 0," + - Settings.SHOULD_SYNC + " INTEGER NOT NULL DEFAULT 1, " + - "PRIMARY KEY (" + Settings.ACCOUNT_NAME + ", " + - Settings.ACCOUNT_TYPE + ") ON CONFLICT REPLACE" + - ");"); - - // The table for recent calls is here so we can do table joins - // on people, phones, and calls all in one place. - db.execSQL("CREATE TABLE " + Tables.CALLS + " (" + - Calls._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - Calls.NUMBER + " TEXT," + - Calls.DATE + " INTEGER," + - Calls.DURATION + " INTEGER," + - Calls.TYPE + " INTEGER," + - Calls.NEW + " INTEGER," + - Calls.CACHED_NAME + " TEXT," + - Calls.CACHED_NUMBER_TYPE + " INTEGER," + - Calls.CACHED_NUMBER_LABEL + " TEXT" + - ");"); - - // Activities table - db.execSQL("CREATE TABLE " + Tables.ACTIVITIES + " (" + - Activities._ID + " INTEGER PRIMARY KEY AUTOINCREMENT," + - ActivitiesColumns.PACKAGE_ID + " INTEGER REFERENCES package(_id)," + - ActivitiesColumns.MIMETYPE_ID + " INTEGER REFERENCES mimetype(_id) NOT NULL," + - Activities.RAW_ID + " TEXT," + - Activities.IN_REPLY_TO + " TEXT," + - Activities.AUTHOR_CONTACT_ID + " INTEGER REFERENCES raw_contacts(_id)," + - Activities.TARGET_CONTACT_ID + " INTEGER REFERENCES raw_contacts(_id)," + - Activities.PUBLISHED + " INTEGER NOT NULL," + - Activities.THREAD_PUBLISHED + " INTEGER NOT NULL," + - Activities.TITLE + " TEXT NOT NULL," + - Activities.SUMMARY + " TEXT," + - Activities.LINK + " TEXT, " + - Activities.THUMBNAIL + " BLOB" + - ");"); - - db.execSQL("CREATE TABLE " + Tables.STATUS_UPDATES + " (" + - StatusUpdatesColumns.DATA_ID + " INTEGER PRIMARY KEY REFERENCES data(_id)," + - StatusUpdates.STATUS + " TEXT," + - StatusUpdates.STATUS_TIMESTAMP + " INTEGER," + - StatusUpdates.STATUS_RES_PACKAGE + " TEXT, " + - StatusUpdates.STATUS_LABEL + " INTEGER, " + - StatusUpdates.STATUS_ICON + " INTEGER" + - ");"); - - String contactEntitiesSelect = "SELECT " - + RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AS " + RawContacts.ACCOUNT_NAME + "," - + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AS " + RawContacts.ACCOUNT_TYPE + "," - + RawContactsColumns.CONCRETE_SOURCE_ID + " AS " + RawContacts.SOURCE_ID + "," - + RawContactsColumns.CONCRETE_VERSION + " AS " + RawContacts.VERSION + "," - + RawContactsColumns.CONCRETE_DIRTY + " AS " + RawContacts.DIRTY + "," - + RawContactsColumns.CONCRETE_DELETED + " AS " + RawContacts.DELETED + "," - + PackagesColumns.PACKAGE + " AS " + Data.RES_PACKAGE + "," - + RawContacts.CONTACT_ID + ", " - + RawContactsColumns.CONCRETE_SYNC1 + " AS " + RawContacts.SYNC1 + ", " - + RawContactsColumns.CONCRETE_SYNC2 + " AS " + RawContacts.SYNC2 + ", " - + RawContactsColumns.CONCRETE_SYNC3 + " AS " + RawContacts.SYNC3 + ", " - + RawContactsColumns.CONCRETE_SYNC4 + " AS " + RawContacts.SYNC4 + ", " - + Data.MIMETYPE + ", " - + Data.DATA1 + ", " - + Data.DATA2 + ", " - + Data.DATA3 + ", " - + Data.DATA4 + ", " - + Data.DATA5 + ", " - + Data.DATA6 + ", " - + Data.DATA7 + ", " - + Data.DATA8 + ", " - + Data.DATA9 + ", " - + Data.DATA10 + ", " - + Data.DATA11 + ", " - + Data.DATA12 + ", " - + Data.DATA13 + ", " - + Data.DATA14 + ", " - + Data.DATA15 + ", " - + Data.SYNC1 + ", " - + Data.SYNC2 + ", " - + Data.SYNC3 + ", " - + Data.SYNC4 + ", " - + RawContactsColumns.CONCRETE_ID + " AS " + Data.RAW_CONTACT_ID + ", " - + Data.IS_PRIMARY + ", " - + Data.IS_SUPER_PRIMARY + ", " - + Data.DATA_VERSION + ", " - + DataColumns.CONCRETE_ID + " AS " + RawContacts._ID + "," - + RawContactsColumns.CONCRETE_STARRED + " AS " + RawContacts.STARRED + "," - + Tables.GROUPS + "." + Groups.SOURCE_ID + " AS " + GroupMembership.GROUP_SOURCE_ID - + " FROM " + Tables.RAW_CONTACTS - + " LEFT OUTER JOIN " + Tables.DATA + " ON (" - + DataColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID + ")" - + " LEFT OUTER JOIN " + Tables.PACKAGES + " ON (" - + DataColumns.CONCRETE_PACKAGE_ID + "=" + PackagesColumns.CONCRETE_ID + ")" - + " LEFT OUTER JOIN " + Tables.MIMETYPES + " ON (" - + DataColumns.CONCRETE_MIMETYPE_ID + "=" + MimetypesColumns.CONCRETE_ID + ")" - + " LEFT OUTER JOIN " + Tables.GROUPS + " ON (" - + MimetypesColumns.CONCRETE_MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE - + "' AND " + GroupsColumns.CONCRETE_ID + "=" - + Tables.DATA + "." + GroupMembership.GROUP_ROW_ID + ")"; - - db.execSQL("CREATE VIEW " + Tables.CONTACT_ENTITIES + " AS " - + contactEntitiesSelect); - db.execSQL("CREATE VIEW " + Tables.CONTACT_ENTITIES_RESTRICTED + " AS " - + contactEntitiesSelect + " WHERE " + RawContacts.IS_RESTRICTED + "=0"); - - String dataColumns = - Data.IS_PRIMARY + ", " - + Data.IS_SUPER_PRIMARY + ", " - + Data.DATA_VERSION + ", " - + PackagesColumns.PACKAGE + " AS " + Data.RES_PACKAGE + "," - + MimetypesColumns.MIMETYPE + " AS " + Data.MIMETYPE + ", " - + Data.DATA1 + ", " - + Data.DATA2 + ", " - + Data.DATA3 + ", " - + Data.DATA4 + ", " - + Data.DATA5 + ", " - + Data.DATA6 + ", " - + Data.DATA7 + ", " - + Data.DATA8 + ", " - + Data.DATA9 + ", " - + Data.DATA10 + ", " - + Data.DATA11 + ", " - + Data.DATA12 + ", " - + Data.DATA13 + ", " - + Data.DATA14 + ", " - + Data.DATA15 + ", " - + Data.SYNC1 + ", " - + Data.SYNC2 + ", " - + Data.SYNC3 + ", " - + Data.SYNC4; - - String syncColumns = - RawContactsColumns.CONCRETE_ACCOUNT_NAME + " AS " + RawContacts.ACCOUNT_NAME + "," - + RawContactsColumns.CONCRETE_ACCOUNT_TYPE + " AS " + RawContacts.ACCOUNT_TYPE + "," - + RawContactsColumns.CONCRETE_SOURCE_ID + " AS " + RawContacts.SOURCE_ID + "," - + RawContactsColumns.CONCRETE_VERSION + " AS " + RawContacts.VERSION + "," - + RawContactsColumns.CONCRETE_DIRTY + " AS " + RawContacts.DIRTY + "," - + RawContactsColumns.CONCRETE_SYNC1 + " AS " + RawContacts.SYNC1 + "," - + RawContactsColumns.CONCRETE_SYNC2 + " AS " + RawContacts.SYNC2 + "," - + RawContactsColumns.CONCRETE_SYNC3 + " AS " + RawContacts.SYNC3 + "," - + RawContactsColumns.CONCRETE_SYNC4 + " AS " + RawContacts.SYNC4; - - String contactOptionColumns = - ContactsColumns.CONCRETE_CUSTOM_RINGTONE - + " AS " + RawContacts.CUSTOM_RINGTONE + "," - + ContactsColumns.CONCRETE_SEND_TO_VOICEMAIL - + " AS " + RawContacts.SEND_TO_VOICEMAIL + "," - + ContactsColumns.CONCRETE_LAST_TIME_CONTACTED - + " AS " + RawContacts.LAST_TIME_CONTACTED + "," - + ContactsColumns.CONCRETE_TIMES_CONTACTED - + " AS " + RawContacts.TIMES_CONTACTED + "," - + ContactsColumns.CONCRETE_STARRED - + " AS " + RawContacts.STARRED; - - String dataSelect = "SELECT " - + DataColumns.CONCRETE_ID + " AS " + Data._ID + "," - + Data.RAW_CONTACT_ID + ", " - + RawContacts.CONTACT_ID + ", " - + syncColumns + ", " - + dataColumns + ", " - + contactOptionColumns + ", " - + ContactsColumns.CONCRETE_DISPLAY_NAME + " AS " + Contacts.DISPLAY_NAME + ", " - + Contacts.LOOKUP_KEY + ", " - + Contacts.PHOTO_ID + ", " - + ContactsColumns.LAST_STATUS_UPDATE_ID + ", " - + Tables.GROUPS + "." + Groups.SOURCE_ID + " AS " + GroupMembership.GROUP_SOURCE_ID - + " FROM " + Tables.DATA - + " LEFT OUTER JOIN " + Tables.PACKAGES + " ON (" - + DataColumns.CONCRETE_PACKAGE_ID + "=" + PackagesColumns.CONCRETE_ID + ")" - + " LEFT OUTER JOIN " + Tables.MIMETYPES + " ON (" - + DataColumns.CONCRETE_MIMETYPE_ID + "=" + MimetypesColumns.CONCRETE_ID + ")" - + " LEFT OUTER JOIN " + Tables.RAW_CONTACTS + " ON (" - + DataColumns.CONCRETE_RAW_CONTACT_ID + "=" + RawContactsColumns.CONCRETE_ID + ")" - + " LEFT OUTER JOIN " + Tables.GROUPS + " ON (" - + MimetypesColumns.CONCRETE_MIMETYPE + "='" + GroupMembership.CONTENT_ITEM_TYPE - + "' AND " + GroupsColumns.CONCRETE_ID + "=" - + Tables.DATA + "." + GroupMembership.GROUP_ROW_ID + ")" - + " LEFT OUTER JOIN " + Tables.CONTACTS + " ON (" - + RawContacts.CONTACT_ID + "=" + Tables.CONTACTS + "." + Contacts._ID + ")"; - - db.execSQL("CREATE VIEW " + Views.DATA_ALL + " AS " + dataSelect); - db.execSQL("CREATE VIEW " + Views.DATA_RESTRICTED + " AS " + dataSelect + " WHERE " - + RawContacts.IS_RESTRICTED + "=0"); - - String rawContactOptionColumns = - RawContacts.CUSTOM_RINGTONE + "," - + RawContacts.SEND_TO_VOICEMAIL + "," - + RawContacts.LAST_TIME_CONTACTED + "," - + RawContacts.TIMES_CONTACTED + "," - + RawContacts.STARRED; - - String rawContactsSelect = "SELECT " - + RawContactsColumns.CONCRETE_ID + " AS " + RawContacts._ID + "," - + RawContacts.CONTACT_ID + ", " - + RawContacts.AGGREGATION_MODE + ", " - + RawContacts.DELETED + ", " - + rawContactOptionColumns + ", " - + syncColumns - + " FROM " + Tables.RAW_CONTACTS; - - db.execSQL("CREATE VIEW " + Views.RAW_CONTACTS_ALL + " AS " + rawContactsSelect); - db.execSQL("CREATE VIEW " + Views.RAW_CONTACTS_RESTRICTED + " AS " + rawContactsSelect - + " WHERE " + RawContacts.IS_RESTRICTED + "=0"); - - String contactsColumns = - ContactsColumns.CONCRETE_CUSTOM_RINGTONE - + " AS " + Contacts.CUSTOM_RINGTONE + ", " - + ContactsColumns.CONCRETE_DISPLAY_NAME - + " AS " + Contacts.DISPLAY_NAME + ", " - + Contacts.IN_VISIBLE_GROUP + ", " - + Contacts.HAS_PHONE_NUMBER + ", " - + Contacts.LOOKUP_KEY + ", " - + Contacts.PHOTO_ID + ", " - + ContactsColumns.CONCRETE_LAST_TIME_CONTACTED - + " AS " + Contacts.LAST_TIME_CONTACTED + ", " - + ContactsColumns.CONCRETE_SEND_TO_VOICEMAIL - + " AS " + Contacts.SEND_TO_VOICEMAIL + ", " - + ContactsColumns.CONCRETE_STARRED - + " AS " + Contacts.STARRED + ", " - + ContactsColumns.CONCRETE_TIMES_CONTACTED - + " AS " + Contacts.TIMES_CONTACTED + ", " - + ContactsColumns.LAST_STATUS_UPDATE_ID; - - String contactsSelect = "SELECT " - + ContactsColumns.CONCRETE_ID + " AS " + Contacts._ID + "," - + contactsColumns - + " FROM " + Tables.CONTACTS; - - String restrictedContactsSelect = "SELECT " - + ContactsColumns.CONCRETE_ID + " AS " + Contacts._ID + "," - + contactsColumns - + " FROM " + Tables.CONTACTS - + " WHERE " + ContactsColumns.SINGLE_IS_RESTRICTED + "=0"; - - db.execSQL("CREATE VIEW " + Views.CONTACTS_ALL + " AS " + contactsSelect); - db.execSQL("CREATE VIEW " + Views.CONTACTS_RESTRICTED + " AS " + restrictedContactsSelect); - - String groupsColumns = - Groups.ACCOUNT_NAME + "," - + Groups.ACCOUNT_TYPE + "," - + Groups.SOURCE_ID + "," - + Groups.VERSION + "," - + Groups.DIRTY + "," - + Groups.TITLE + "," - + Groups.TITLE_RES + "," - + Groups.NOTES + "," - + Groups.SYSTEM_ID + "," - + Groups.DELETED + "," - + Groups.GROUP_VISIBLE + "," - + Groups.SHOULD_SYNC + "," - + Groups.SYNC1 + "," - + Groups.SYNC2 + "," - + Groups.SYNC3 + "," - + Groups.SYNC4 + "," - + PackagesColumns.PACKAGE + " AS " + Groups.RES_PACKAGE; - - String groupsSelect = "SELECT " - + GroupsColumns.CONCRETE_ID + " AS " + Groups._ID + "," - + groupsColumns - + " FROM " + Tables.GROUPS_JOIN_PACKAGES; - - db.execSQL("CREATE VIEW " + Views.GROUPS_ALL + " AS " + groupsSelect); - - loadNicknameLookupTable(db); - - // Add the legacy API support views, etc - LegacyApiSupport.createDatabase(db); - - // This will create a sqlite_stat1 table that is used for query optimization - db.execSQL("ANALYZE;"); - - updateSqliteStats(db); - - // We need to close and reopen the database connection so that the stats are - // taken into account. Make a note of it and do the actual reopening in the - // getWritableDatabase method. - mReopenDatabase = true; - - ContentResolver.requestSync(null /* all accounts */, - ContactsContract.AUTHORITY, new Bundle()); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - - Log.i(TAG, "Upgrading from version " + oldVersion + " to " + newVersion - + ", data will be lost!"); - - db.execSQL("DROP TABLE IF EXISTS " + Tables.CONTACTS + ";"); - db.execSQL("DROP TABLE IF EXISTS " + Tables.RAW_CONTACTS + ";"); - db.execSQL("DROP TABLE IF EXISTS " + Tables.PACKAGES + ";"); - db.execSQL("DROP TABLE IF EXISTS " + Tables.MIMETYPES + ";"); - db.execSQL("DROP TABLE IF EXISTS " + Tables.DATA + ";"); - db.execSQL("DROP TABLE IF EXISTS " + Tables.PHONE_LOOKUP + ";"); - db.execSQL("DROP TABLE IF EXISTS " + Tables.NAME_LOOKUP + ";"); - db.execSQL("DROP TABLE IF EXISTS " + Tables.NICKNAME_LOOKUP + ";"); - db.execSQL("DROP TABLE IF EXISTS " + Tables.GROUPS + ";"); - db.execSQL("DROP TABLE IF EXISTS " + Tables.ACTIVITIES + ";"); - db.execSQL("DROP TABLE IF EXISTS " + Tables.CALLS + ";"); - db.execSQL("DROP TABLE IF EXISTS " + Tables.SETTINGS + ";"); - db.execSQL("DROP TABLE IF EXISTS " + Tables.STATUS_UPDATES + ";"); - - db.execSQL("DROP VIEW IF EXISTS " + Tables.CONTACT_ENTITIES + ";"); - db.execSQL("DROP VIEW IF EXISTS " + Tables.CONTACT_ENTITIES_RESTRICTED + ";"); - db.execSQL("DROP VIEW IF EXISTS " + Views.CONTACTS_ALL + ";"); - db.execSQL("DROP VIEW IF EXISTS " + Views.CONTACTS_RESTRICTED + ";"); - db.execSQL("DROP VIEW IF EXISTS " + Views.DATA_ALL + ";"); - db.execSQL("DROP VIEW IF EXISTS " + Views.DATA_RESTRICTED + ";"); - db.execSQL("DROP VIEW IF EXISTS " + Views.RAW_CONTACTS_ALL + ";"); - db.execSQL("DROP VIEW IF EXISTS " + Views.RAW_CONTACTS_RESTRICTED + ";"); - db.execSQL("DROP VIEW IF EXISTS " + Views.GROUPS_ALL + ";"); - - // TODO: we should not be dropping agg_exceptions and contact_options. In case that table's - // schema changes, we should try to preserve the data, because it was entered by the user - // and has never been synched to the server. - db.execSQL("DROP TABLE IF EXISTS " + Tables.AGGREGATION_EXCEPTIONS + ";"); - - onCreate(db); - } - - /** - * Adds index stats into the SQLite database to force it to always use the lookup indexes. - */ - private void updateSqliteStats(SQLiteDatabase db) { - - // Specific stats strings are based on an actual large database after running ANALYZE - try { - updateIndexStats(db, Tables.CONTACTS, - "contacts_restricted_index", "10000 9000"); - updateIndexStats(db, Tables.CONTACTS, - "contacts_has_phone_index", "10000 500"); - updateIndexStats(db, Tables.CONTACTS, - "contacts_visible_index", "10000 500 1"); - - updateIndexStats(db, Tables.RAW_CONTACTS, - "raw_contacts_source_id_index", "10000 1 1 1"); - updateIndexStats(db, Tables.RAW_CONTACTS, - "raw_contacts_contact_id_index", "10000 2"); - - updateIndexStats(db, Tables.NAME_LOOKUP, - "name_lookup_raw_contact_id_index", "10000 3"); - updateIndexStats(db, Tables.NAME_LOOKUP, - "name_lookup_index", "10000 3 2 2"); - updateIndexStats(db, Tables.NAME_LOOKUP, - "sqlite_autoindex_name_lookup_1", "10000 3 2 1"); - - updateIndexStats(db, Tables.PHONE_LOOKUP, - "phone_lookup_index", "10000 2 2 1"); - - updateIndexStats(db, Tables.DATA, - "data_mimetype_data1_index", "60000 5000 2"); - updateIndexStats(db, Tables.DATA, - "data_raw_contact_id", "60000 10"); - - updateIndexStats(db, Tables.GROUPS, - "groups_source_id_index", "50 1 1 1"); - - updateIndexStats(db, Tables.NICKNAME_LOOKUP, - "sqlite_autoindex_name_lookup_1", "500 2 1"); - - } catch (SQLException e) { - Log.e(TAG, "Could not update index stats", e); - } - } - - /** - * Stores statistics for a given index. - * - * @param stats has the following structure: the first index is the expected size of - * the table. The following integer(s) are the expected number of records selected with the - * index. There should be one integer per indexed column. - */ - private void updateIndexStats(SQLiteDatabase db, String table, String index, String stats) { - db.execSQL("DELETE FROM sqlite_stat1 WHERE tbl='" + table + "' AND idx='" + index + "';"); - db.execSQL("INSERT INTO sqlite_stat1 (tbl,idx,stat)" - + " VALUES ('" + table + "','" + index + "','" + stats + "');"); - } - - @Override - public synchronized SQLiteDatabase getWritableDatabase() { - SQLiteDatabase db = super.getWritableDatabase(); - if (mReopenDatabase) { - mReopenDatabase = false; - close(); - db = super.getWritableDatabase(); - } - return db; - } - - /** - * Wipes all data except mime type and package lookup tables. - */ - public void wipeData() { - SQLiteDatabase db = getWritableDatabase(); - - db.execSQL("DELETE FROM " + Tables.CONTACTS + ";"); - db.execSQL("DELETE FROM " + Tables.RAW_CONTACTS + ";"); - db.execSQL("DELETE FROM " + Tables.DATA + ";"); - db.execSQL("DELETE FROM " + Tables.PHONE_LOOKUP + ";"); - db.execSQL("DELETE FROM " + Tables.NAME_LOOKUP + ";"); - db.execSQL("DELETE FROM " + Tables.GROUPS + ";"); - db.execSQL("DELETE FROM " + Tables.AGGREGATION_EXCEPTIONS + ";"); - db.execSQL("DELETE FROM " + Tables.SETTINGS + ";"); - db.execSQL("DELETE FROM " + Tables.ACTIVITIES + ";"); - db.execSQL("DELETE FROM " + Tables.CALLS + ";"); - - // Note: we are not removing reference data from Tables.NICKNAME_LOOKUP - - db.execSQL("VACUUM;"); - } - - /** - * Return the {@link ApplicationInfo#uid} for the given package name. - */ - public static int getUidForPackageName(PackageManager pm, String packageName) { - try { - ApplicationInfo clientInfo = pm.getApplicationInfo(packageName, 0 /* no flags */); - return clientInfo.uid; - } catch (NameNotFoundException e) { - throw new RuntimeException(e); - } - } - - /** - * Perform an internal string-to-integer lookup using the compiled - * {@link SQLiteStatement} provided, using the in-memory cache to speed up - * lookups. If a mapping isn't found in cache or database, it will be - * created. All new, uncached answers are added to the cache automatically. - * - * @param query Compiled statement used to query for the mapping. - * @param insert Compiled statement used to insert a new mapping when no - * existing one is found in cache or from query. - * @param value Value to find mapping for. - * @param cache In-memory cache of previous answers. - * @return An unique integer mapping for the given value. - */ - private synchronized long getCachedId(SQLiteStatement query, SQLiteStatement insert, - String value, HashMap cache) { - // Try an in-memory cache lookup - if (cache.containsKey(value)) { - return cache.get(value); - } - - long id = -1; - try { - // Try searching database for mapping - DatabaseUtils.bindObjectToProgram(query, 1, value); - id = query.simpleQueryForLong(); - } catch (SQLiteDoneException e) { - // Nothing found, so try inserting new mapping - DatabaseUtils.bindObjectToProgram(insert, 1, value); - id = insert.executeInsert(); - } - - if (id != -1) { - // Cache and return the new answer - cache.put(value, id); - return id; - } else { - // Otherwise throw if no mapping found or created - throw new IllegalStateException("Couldn't find or create internal " - + "lookup table entry for value " + value); - } - } - - /** - * Convert a package name into an integer, using {@link Tables#PACKAGES} for - * lookups and possible allocation of new IDs as needed. - */ - public long getPackageId(String packageName) { - // Make sure compiled statements are ready by opening database - getReadableDatabase(); - return getCachedId(mPackageQuery, mPackageInsert, packageName, mPackageCache); - } - - /** - * Convert a mimetype into an integer, using {@link Tables#MIMETYPES} for - * lookups and possible allocation of new IDs as needed. - */ - public long getMimeTypeId(String mimetype) { - // Make sure compiled statements are ready by opening database - getReadableDatabase(); - return getCachedId(mMimetypeQuery, mMimetypeInsert, mimetype, mMimetypeCache); - } - - /** - * Find the mimetype for the given {@link Data#_ID}. - */ - public String getDataMimeType(long dataId) { - // Make sure compiled statements are ready by opening database - getReadableDatabase(); - try { - // Try database query to find mimetype - DatabaseUtils.bindObjectToProgram(mDataMimetypeQuery, 1, dataId); - String mimetype = mDataMimetypeQuery.simpleQueryForString(); - return mimetype; - } catch (SQLiteDoneException e) { - // No valid mapping found, so return null - return null; - } - } - - /** - * Find the mime-type for the given {@link Activities#_ID}. - */ - public String getActivityMimeType(long activityId) { - // Make sure compiled statements are ready by opening database - getReadableDatabase(); - try { - // Try database query to find mimetype - DatabaseUtils.bindObjectToProgram(mActivitiesMimetypeQuery, 1, activityId); - String mimetype = mActivitiesMimetypeQuery.simpleQueryForString(); - return mimetype; - } catch (SQLiteDoneException e) { - // No valid mapping found, so return null - return null; - } - } - - /** - * Update {@link Contacts#IN_VISIBLE_GROUP} for all contacts. - */ - public void updateAllVisible() { - final long groupMembershipMimetypeId = getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE); - mVisibleUpdate.bindLong(1, groupMembershipMimetypeId); - mVisibleUpdate.execute(); - } - - /** - * Update {@link Contacts#IN_VISIBLE_GROUP} for a specific contact. - */ - public void updateContactVisible(long aggId) { - final long groupMembershipMimetypeId = getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE); - mVisibleSpecificUpdate.bindLong(1, groupMembershipMimetypeId); - mVisibleSpecificUpdate.bindLong(2, aggId); - mVisibleSpecificUpdate.execute(); - } - - /** - * Returns contact ID for the given contact or zero if it is NULL. - */ - public long getContactId(long rawContactId) { - getReadableDatabase(); - try { - DatabaseUtils.bindObjectToProgram(mContactIdQuery, 1, rawContactId); - return mContactIdQuery.simpleQueryForLong(); - } catch (SQLiteDoneException e) { - // No valid mapping found, so return 0 - return 0; - } - } - - public int getAggregationMode(long rawContactId) { - getReadableDatabase(); - try { - DatabaseUtils.bindObjectToProgram(mAggregationModeQuery, 1, rawContactId); - return (int)mAggregationModeQuery.simpleQueryForLong(); - } catch (SQLiteDoneException e) { - // No valid row found, so return "disabled" - return RawContacts.AGGREGATION_MODE_DISABLED; - } - } - - public void buildPhoneLookupAndRawContactQuery(SQLiteQueryBuilder qb, String number) { - String normalizedNumber = PhoneNumberUtils.toCallerIDMinMatch(number); - qb.setTables(Tables.DATA_JOIN_RAW_CONTACTS + - " JOIN " + Tables.PHONE_LOOKUP - + " ON(" + DataColumns.CONCRETE_ID + "=" + PhoneLookupColumns.DATA_ID + ")"); - - StringBuilder sb = new StringBuilder(); - sb.append(PhoneLookupColumns.NORMALIZED_NUMBER + " GLOB '"); - sb.append(normalizedNumber); - sb.append("*' AND PHONE_NUMBERS_EQUAL(data." + Phone.NUMBER + ", "); - DatabaseUtils.appendEscapedSQLString(sb, number); - sb.append(mUseStrictPhoneNumberComparation ? ", 1)" : ", 0)"); - - qb.appendWhere(sb.toString()); - } - - public void buildPhoneLookupAndContactQuery(SQLiteQueryBuilder qb, String number) { - String normalizedNumber = PhoneNumberUtils.toCallerIDMinMatch(number); - StringBuilder sb = new StringBuilder(); - appendPhoneLookupTables(sb, normalizedNumber, true); - qb.setTables(sb.toString()); - - sb = new StringBuilder(); - appendPhoneLookupSelection(sb, number); - qb.appendWhere(sb.toString()); - } - - public String buildPhoneLookupAsNestedQuery(String number) { - StringBuilder sb = new StringBuilder(); - final String normalizedNumber = PhoneNumberUtils.toCallerIDMinMatch(number); - sb.append("(SELECT DISTINCT raw_contact_id" + " FROM "); - appendPhoneLookupTables(sb, normalizedNumber, false); - sb.append(" WHERE "); - appendPhoneLookupSelection(sb, number); - sb.append(")"); - return sb.toString(); - } - - private void appendPhoneLookupTables(StringBuilder sb, final String normalizedNumber, - boolean joinContacts) { - sb.append(Tables.RAW_CONTACTS); - if (joinContacts) { - sb.append(" JOIN " + getContactView() + " contacts" - + " ON (contacts._id = raw_contacts.contact_id)"); - } - sb.append(", (SELECT data_id FROM phone_lookup " - + "WHERE (phone_lookup.normalized_number GLOB '"); - sb.append(normalizedNumber); - sb.append("*')) AS lookup, " + Tables.DATA); - } - - private void appendPhoneLookupSelection(StringBuilder sb, String number) { - sb.append("lookup.data_id=data._id AND data.raw_contact_id=raw_contacts._id" - + " AND PHONE_NUMBERS_EQUAL(data." + Phone.NUMBER + ", "); - DatabaseUtils.appendEscapedSQLString(sb, number); - sb.append(mUseStrictPhoneNumberComparation ? ", 1)" : ", 0)"); - } - - /** - * Loads common nickname mappings into the database. - */ - private void loadNicknameLookupTable(SQLiteDatabase db) { - String[] strings = mContext.getResources().getStringArray( - com.android.internal.R.array.common_nicknames); - if (strings == null || strings.length == 0) { - return; - } - - SQLiteStatement nicknameLookupInsert = db.compileStatement("INSERT INTO " - + Tables.NICKNAME_LOOKUP + "(" + NicknameLookupColumns.NAME + "," - + NicknameLookupColumns.CLUSTER + ") VALUES (?,?)"); - - for (int clusterId = 0; clusterId < strings.length; clusterId++) { - String[] names = strings[clusterId].split(","); - for (int j = 0; j < names.length; j++) { - String name = NameNormalizer.normalize(names[j]); - try { - DatabaseUtils.bindObjectToProgram(nicknameLookupInsert, 1, name); - DatabaseUtils.bindObjectToProgram(nicknameLookupInsert, 2, - String.valueOf(clusterId)); - nicknameLookupInsert.executeInsert(); - } catch (SQLiteException e) { - - // Print the exception and keep going - this is not a fatal error - Log.e(TAG, "Cannot insert nickname: " + names[j], e); - } - } - } - } - - public static void copyStringValue(ContentValues toValues, String toKey, - ContentValues fromValues, String fromKey) { - if (fromValues.containsKey(fromKey)) { - toValues.put(toKey, fromValues.getAsString(fromKey)); - } - } - - public static void copyLongValue(ContentValues toValues, String toKey, - ContentValues fromValues, String fromKey) { - if (fromValues.containsKey(fromKey)) { - long longValue; - Object value = fromValues.get(fromKey); - if (value instanceof Boolean) { - if ((Boolean)value) { - longValue = 1; - } else { - longValue = 0; - } - } else if (value instanceof String) { - longValue = Long.parseLong((String)value); - } else { - longValue = ((Number)value).longValue(); - } - toValues.put(toKey, longValue); - } - } - - public SyncStateContentProviderHelper getSyncState() { - return mSyncState; - } - - /** - * Delete the aggregate contact if it has no constituent raw contacts other - * than the supplied one. - */ - public void removeContactIfSingleton(long rawContactId) { - SQLiteDatabase db = getWritableDatabase(); - - // Obtain contact ID from the supplied raw contact ID - String contactIdFromRawContactId = "(SELECT " + RawContacts.CONTACT_ID + " FROM " - + Tables.RAW_CONTACTS + " WHERE " + RawContacts._ID + "=" + rawContactId + ")"; - - // Find other raw contacts in the same aggregate contact - String otherRawContacts = "(SELECT contacts1." + RawContacts._ID + " FROM " - + Tables.RAW_CONTACTS + " contacts1 JOIN " + Tables.RAW_CONTACTS + " contacts2 ON (" - + "contacts1." + RawContacts.CONTACT_ID + "=contacts2." + RawContacts.CONTACT_ID - + ") WHERE contacts1." + RawContacts._ID + "!=" + rawContactId + "" - + " AND contacts2." + RawContacts._ID + "=" + rawContactId + ")"; - - db.execSQL("DELETE FROM " + Tables.CONTACTS - + " WHERE " + Contacts._ID + "=" + contactIdFromRawContactId - + " AND NOT EXISTS " + otherRawContacts + ";"); - } - - /** - * List of package names with access to {@link RawContacts#IS_RESTRICTED} data. - */ - static final String[] sAllowedPackages = new String[] { - "com.android.contacts", - "com.facebook.katana", - }; - - /** - * Check if {@link Binder#getCallingUid()} should be allowed access to - * {@link RawContacts#IS_RESTRICTED} data. - */ - boolean hasRestrictedAccess() { - final PackageManager pm = mContext.getPackageManager(); - final String[] callerPackages = pm.getPackagesForUid(Binder.getCallingUid()); - - // Has restricted access if caller matches any packages - for (String callerPackage : callerPackages) { - for (String allowedPackage : sAllowedPackages) { - if (allowedPackage.equals(callerPackage)) { - return true; - } - } - } - return false; - } - - public String getDataView() { - return getDataView(false); - } - - public String getDataView(boolean requireRestrictedView) { - return (hasRestrictedAccess() && !requireRestrictedView) ? - Views.DATA_ALL : Views.DATA_RESTRICTED; - } - - public String getRawContactView() { - return hasRestrictedAccess() ? Views.RAW_CONTACTS_ALL - : Views.RAW_CONTACTS_RESTRICTED; - } - - public String getContactView() { - return hasRestrictedAccess() ? Views.CONTACTS_ALL : Views.CONTACTS_RESTRICTED; - } - - public String getRestrictedContactView() { - return Views.CONTACTS_RESTRICTED; - } - - public String getGroupView() { - return Views.GROUPS_ALL; - } - - public String getContactEntitiesView() { - return getContactEntitiesView(false); - } - - public String getContactEntitiesView(boolean requireRestrictedView) { - return (hasRestrictedAccess() && !requireRestrictedView) ? - Tables.CONTACT_ENTITIES : Tables.CONTACT_ENTITIES_RESTRICTED; - } - - /** - * Test if any of the columns appear in the given projection. - */ - public boolean isInProjection(String[] projection, String... columns) { - if (projection == null) { - return true; - } - - // Optimized for a single-column test - if (columns.length == 1) { - String column = columns[0]; - for (String test : projection) { - if (column.equals(test)) { - return true; - } - } - } else { - for (String test : projection) { - for (String column : columns) { - if (column.equals(test)) { - return true; - } - } - } - } - return false; - } -} diff --git a/src/com/android/providers/contacts/SQLiteContentProvider.java b/src/com/android/providers/contacts/SQLiteContentProvider.java index 5608dcc2..4d1ec901 100644 --- a/src/com/android/providers/contacts/SQLiteContentProvider.java +++ b/src/com/android/providers/contacts/SQLiteContentProvider.java @@ -47,11 +47,11 @@ public abstract class SQLiteContentProvider extends ContentProvider @Override public boolean onCreate() { Context context = getContext(); - mOpenHelper = getOpenHelper(context); + mOpenHelper = getDatabaseHelper(context); return true; } - protected abstract SQLiteOpenHelper getOpenHelper(Context context); + protected abstract SQLiteOpenHelper getDatabaseHelper(Context context); /** * The equivalent of the {@link #insert} method, but invoked within a transaction. @@ -71,7 +71,7 @@ public abstract class SQLiteContentProvider extends ContentProvider protected abstract void notifyChange(); - protected SQLiteOpenHelper getOpenHelper() { + protected SQLiteOpenHelper getDatabaseHelper() { return mOpenHelper; } diff --git a/src/com/android/providers/contacts/SocialProvider.java b/src/com/android/providers/contacts/SocialProvider.java index a4e68d75..349e1fc5 100644 --- a/src/com/android/providers/contacts/SocialProvider.java +++ b/src/com/android/providers/contacts/SocialProvider.java @@ -16,10 +16,10 @@ package com.android.providers.contacts; -import com.android.providers.contacts.OpenHelper.ActivitiesColumns; -import com.android.providers.contacts.OpenHelper.ContactsColumns; -import com.android.providers.contacts.OpenHelper.PackagesColumns; -import com.android.providers.contacts.OpenHelper.Tables; +import com.android.providers.contacts.ContactsDatabaseHelper.ActivitiesColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.ContactsColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.PackagesColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.Tables; import android.content.ContentProvider; import android.content.ContentUris; @@ -121,16 +121,16 @@ public class SocialProvider extends ContentProvider { } - private OpenHelper mOpenHelper; + private ContactsDatabaseHelper mDbHelper; /** {@inheritDoc} */ @Override public boolean onCreate() { final Context context = getContext(); - mOpenHelper = OpenHelper.getInstance(context); + mDbHelper = ContactsDatabaseHelper.getInstance(context); // TODO remove this, it's here to force opening the database on boot for testing - mOpenHelper.getReadableDatabase(); + mDbHelper.getReadableDatabase(); return true; } @@ -180,7 +180,7 @@ public class SocialProvider extends ContentProvider { // TODO verify that IN_REPLY_TO != RAW_ID - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + final SQLiteDatabase db = mDbHelper.getWritableDatabase(); long id = 0; db.beginTransaction(); try { @@ -190,12 +190,12 @@ public class SocialProvider extends ContentProvider { // Replace package name and mime-type with internal mappings final String packageName = values.getAsString(Activities.RES_PACKAGE); if (packageName != null) { - values.put(ActivitiesColumns.PACKAGE_ID, mOpenHelper.getPackageId(packageName)); + values.put(ActivitiesColumns.PACKAGE_ID, mDbHelper.getPackageId(packageName)); } values.remove(Activities.RES_PACKAGE); final String mimeType = values.getAsString(Activities.MIMETYPE); - values.put(ActivitiesColumns.MIMETYPE_ID, mOpenHelper.getMimeTypeId(mimeType)); + values.put(ActivitiesColumns.MIMETYPE_ID, mDbHelper.getMimeTypeId(mimeType)); values.remove(Activities.MIMETYPE); long published = values.getAsLong(Activities.PUBLISHED); @@ -296,7 +296,7 @@ public class SocialProvider extends ContentProvider { /** {@inheritDoc} */ @Override public int delete(Uri uri, String selection, String[] selectionArgs) { - final SQLiteDatabase db = mOpenHelper.getWritableDatabase(); + final SQLiteDatabase db = mDbHelper.getWritableDatabase(); final int match = sUriMatcher.match(uri); switch (match) { @@ -325,7 +325,7 @@ public class SocialProvider extends ContentProvider { @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { - final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + final SQLiteDatabase db = mDbHelper.getReadableDatabase(); final SQLiteQueryBuilder qb = new SQLiteQueryBuilder(); String limit = null; @@ -395,9 +395,9 @@ public class SocialProvider extends ContentProvider { case ACTIVITIES_AUTHORED_BY: return Activities.CONTENT_TYPE; case ACTIVITIES_ID: - final SQLiteDatabase db = mOpenHelper.getReadableDatabase(); + final SQLiteDatabase db = mDbHelper.getReadableDatabase(); long activityId = ContentUris.parseId(uri); - return mOpenHelper.getActivityMimeType(activityId); + return mDbHelper.getActivityMimeType(activityId); case CONTACT_STATUS_ID: return Contacts.CONTENT_ITEM_TYPE; } diff --git a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java index b2ee8a53..7c9f9d8d 100644 --- a/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java +++ b/tests/src/com/android/providers/contacts/BaseContactsProvider2Test.java @@ -96,7 +96,7 @@ public abstract class BaseContactsProvider2Test extends AndroidTestCase { mResolver = mActor.resolver; if (mActor.provider instanceof SynchronousContactsProvider2) { ((SynchronousContactsProvider2) mActor.provider) - .getOpenHelper(mActor.context).wipeData(); + .getDatabaseHelper(mActor.context).wipeData(); } } diff --git a/tests/src/com/android/providers/contacts/CallLogProviderTest.java b/tests/src/com/android/providers/contacts/CallLogProviderTest.java index a46dbee9..bc38c8b1 100644 --- a/tests/src/com/android/providers/contacts/CallLogProviderTest.java +++ b/tests/src/com/android/providers/contacts/CallLogProviderTest.java @@ -154,14 +154,14 @@ public class CallLogProviderTest extends BaseContactsProvider2Test { } public static class TestCallLogProvider extends CallLogProvider { - private static OpenHelper mOpenHelper; + private static ContactsDatabaseHelper mDbHelper; @Override - protected OpenHelper getOpenHelper(final Context context) { - if (mOpenHelper == null) { - mOpenHelper = new OpenHelper(context); + protected ContactsDatabaseHelper getDatabaseHelper(final Context context) { + if (mDbHelper == null) { + mDbHelper = new ContactsDatabaseHelper(context); } - return mOpenHelper; + return mDbHelper; } } } diff --git a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java index b9d9ca99..27b786cf 100644 --- a/tests/src/com/android/providers/contacts/ContactsProvider2Test.java +++ b/tests/src/com/android/providers/contacts/ContactsProvider2Test.java @@ -16,7 +16,7 @@ package com.android.providers.contacts; import com.android.internal.util.ArrayUtils; -import com.android.providers.contacts.OpenHelper.PresenceColumns; +import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns; import android.accounts.Account; import android.content.ContentUris; diff --git a/tests/src/com/android/providers/contacts/LegacyContactImporterPerformanceTest.java b/tests/src/com/android/providers/contacts/LegacyContactImporterPerformanceTest.java index 4cc6911b..f5bc354b 100644 --- a/tests/src/com/android/providers/contacts/LegacyContactImporterPerformanceTest.java +++ b/tests/src/com/android/providers/contacts/LegacyContactImporterPerformanceTest.java @@ -92,14 +92,14 @@ public class LegacyContactImporterPerformanceTest extends AndroidTestCase { } public static class TestCallLogProvider extends CallLogProvider { - private static OpenHelper mOpenHelper; + private static ContactsDatabaseHelper mDbHelper; @Override - protected OpenHelper getOpenHelper(final Context context) { - if (mOpenHelper == null) { - mOpenHelper = new OpenHelper(context); + protected ContactsDatabaseHelper getDatabaseHelper(final Context context) { + if (mDbHelper == null) { + mDbHelper = new ContactsDatabaseHelper(context); } - return mOpenHelper; + return mDbHelper; } } diff --git a/tests/src/com/android/providers/contacts/LegacyContactImporterTest.java b/tests/src/com/android/providers/contacts/LegacyContactImporterTest.java index cab463d9..89887201 100644 --- a/tests/src/com/android/providers/contacts/LegacyContactImporterTest.java +++ b/tests/src/com/android/providers/contacts/LegacyContactImporterTest.java @@ -235,14 +235,14 @@ public class LegacyContactImporterTest extends BaseContactsProvider2Test { public static class TestCallLogProvider extends CallLogProvider { - private static OpenHelper mOpenHelper; + private static ContactsDatabaseHelper mDbHelper; @Override - protected OpenHelper getOpenHelper(final Context context) { - if (mOpenHelper == null) { - mOpenHelper = new OpenHelper(context); + protected ContactsDatabaseHelper getDatabaseHelper(final Context context) { + if (mDbHelper == null) { + mDbHelper = new ContactsDatabaseHelper(context); } - return mOpenHelper; + return mDbHelper; } } } diff --git a/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java b/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java index a175a54a..59859f97 100644 --- a/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java +++ b/tests/src/com/android/providers/contacts/SynchronousContactsProvider2.java @@ -26,7 +26,7 @@ import android.database.sqlite.SQLiteDatabase; */ public class SynchronousContactsProvider2 extends ContactsProvider2 { private static Boolean sDataWiped = false; - private static OpenHelper mOpenHelper; + private static ContactsDatabaseHelper mDbHelper; private boolean mDataWipeEnabled = true; private Account mAccount; private boolean mNetworkNotified; @@ -40,15 +40,15 @@ public class SynchronousContactsProvider2 extends ContactsProvider2 { } @Override - protected OpenHelper getOpenHelper(final Context context) { - if (mOpenHelper == null) { - mOpenHelper = new OpenHelper(context); + protected ContactsDatabaseHelper getDatabaseHelper(final Context context) { + if (mDbHelper == null) { + mDbHelper = new ContactsDatabaseHelper(context); } - return mOpenHelper; + return mDbHelper; } public static void resetOpenHelper() { - mOpenHelper = null; + mDbHelper = null; } public void setDataWipeEnabled(boolean flag) { @@ -97,7 +97,7 @@ public class SynchronousContactsProvider2 extends ContactsProvider2 { } public void prepareForFullAggregation(int maxContact) { - SQLiteDatabase db = getOpenHelper().getWritableDatabase(); + SQLiteDatabase db = getDatabaseHelper().getWritableDatabase(); db.execSQL("UPDATE raw_contacts SET aggregation_mode=0,aggregation_needed=1;"); long rowId = db.compileStatement("SELECT _id FROM raw_contacts LIMIT 1 OFFSET " + maxContact) @@ -106,12 +106,12 @@ public class SynchronousContactsProvider2 extends ContactsProvider2 { } public long getRawContactCount() { - SQLiteDatabase db = getOpenHelper().getReadableDatabase(); + SQLiteDatabase db = getDatabaseHelper().getReadableDatabase(); return db.compileStatement("SELECT COUNT(*) FROM raw_contacts").simpleQueryForLong(); } public long getContactCount() { - SQLiteDatabase db = getOpenHelper().getReadableDatabase(); + SQLiteDatabase db = getDatabaseHelper().getReadableDatabase(); return db.compileStatement("SELECT COUNT(*) FROM contacts").simpleQueryForLong(); } -- cgit v1.2.3