summaryrefslogtreecommitdiff
path: root/src/com/android/dialer/database/DialerDatabaseHelper.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/dialer/database/DialerDatabaseHelper.java')
-rw-r--r--src/com/android/dialer/database/DialerDatabaseHelper.java1169
1 files changed, 0 insertions, 1169 deletions
diff --git a/src/com/android/dialer/database/DialerDatabaseHelper.java b/src/com/android/dialer/database/DialerDatabaseHelper.java
deleted file mode 100644
index 5edfb270d..000000000
--- a/src/com/android/dialer/database/DialerDatabaseHelper.java
+++ /dev/null
@@ -1,1169 +0,0 @@
-/*
- * Copyright (C) 2013 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.dialer.database;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteOpenHelper;
-import android.database.sqlite.SQLiteStatement;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.provider.BaseColumns;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.Directory;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.contacts.common.util.PermissionsUtil;
-import com.android.contacts.common.util.StopWatch;
-import com.android.dialer.database.FilteredNumberContract.FilteredNumberColumns;
-import com.android.dialer.database.VoicemailArchiveContract.VoicemailArchive;
-import com.android.dialer.R;
-import com.android.dialer.dialpad.SmartDialNameMatcher;
-import com.android.dialer.dialpad.SmartDialPrefix;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Lists;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/**
- * Database helper for smart dial. Designed as a singleton to make sure there is
- * only one access point to the database. Provides methods to maintain, update,
- * and query the database.
- */
-public class DialerDatabaseHelper extends SQLiteOpenHelper {
- private static final String TAG = "DialerDatabaseHelper";
- private static final boolean DEBUG = false;
- private boolean mIsTestInstance = false;
-
- private static DialerDatabaseHelper sSingleton = null;
-
- private static final Object mLock = new Object();
- private static final AtomicBoolean sInUpdate = new AtomicBoolean(false);
- private final Context mContext;
-
- /**
- * SmartDial DB version ranges:
- * <pre>
- * 0-98 KitKat
- * </pre>
- */
- public static final int DATABASE_VERSION = 9;
- public static final String DATABASE_NAME = "dialer.db";
-
- /**
- * Saves the last update time of smart dial databases to shared preferences.
- */
- private static final String DATABASE_LAST_CREATED_SHARED_PREF = "com.android.dialer";
- private static final String LAST_UPDATED_MILLIS = "last_updated_millis";
- private static final String DATABASE_VERSION_PROPERTY = "database_version";
-
- private static final int MAX_ENTRIES = 20;
-
- public interface Tables {
- /** Saves a list of numbers to be blocked.*/
- static final String FILTERED_NUMBER_TABLE = "filtered_numbers_table";
- /** Saves the necessary smart dial information of all contacts. */
- static final String SMARTDIAL_TABLE = "smartdial_table";
- /** Saves all possible prefixes to refer to a contacts.*/
- static final String PREFIX_TABLE = "prefix_table";
- /** Saves all archived voicemail information. */
- static final String VOICEMAIL_ARCHIVE_TABLE = "voicemail_archive_table";
- /** Database properties for internal use */
- static final String PROPERTIES = "properties";
- }
-
- public static final Uri SMART_DIAL_UPDATED_URI =
- Uri.parse("content://com.android.dialer/smart_dial_updated");
-
- public interface SmartDialDbColumns {
- static final String _ID = "id";
- static final String DATA_ID = "data_id";
- static final String NUMBER = "phone_number";
- static final String CONTACT_ID = "contact_id";
- static final String LOOKUP_KEY = "lookup_key";
- static final String DISPLAY_NAME_PRIMARY = "display_name";
- static final String PHOTO_ID = "photo_id";
- static final String LAST_TIME_USED = "last_time_used";
- static final String TIMES_USED = "times_used";
- static final String STARRED = "starred";
- static final String IS_SUPER_PRIMARY = "is_super_primary";
- static final String IN_VISIBLE_GROUP = "in_visible_group";
- static final String IS_PRIMARY = "is_primary";
- static final String CARRIER_PRESENCE = "carrier_presence";
- static final String LAST_SMARTDIAL_UPDATE_TIME = "last_smartdial_update_time";
- }
-
- public static interface PrefixColumns extends BaseColumns {
- static final String PREFIX = "prefix";
- static final String CONTACT_ID = "contact_id";
- }
-
- public interface PropertiesColumns {
- String PROPERTY_KEY = "property_key";
- String PROPERTY_VALUE = "property_value";
- }
-
- /** Query options for querying the contact database.*/
- public static interface PhoneQuery {
- static final Uri URI = Phone.CONTENT_URI.buildUpon().
- appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
- String.valueOf(Directory.DEFAULT)).
- appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true").
- build();
-
- static final String[] PROJECTION = new String[] {
- Phone._ID, // 0
- Phone.TYPE, // 1
- Phone.LABEL, // 2
- Phone.NUMBER, // 3
- Phone.CONTACT_ID, // 4
- Phone.LOOKUP_KEY, // 5
- Phone.DISPLAY_NAME_PRIMARY, // 6
- Phone.PHOTO_ID, // 7
- Data.LAST_TIME_USED, // 8
- Data.TIMES_USED, // 9
- Contacts.STARRED, // 10
- Data.IS_SUPER_PRIMARY, // 11
- Contacts.IN_VISIBLE_GROUP, // 12
- Data.IS_PRIMARY, // 13
- Data.CARRIER_PRESENCE, // 14
- };
-
- static final int PHONE_ID = 0;
- static final int PHONE_TYPE = 1;
- static final int PHONE_LABEL = 2;
- static final int PHONE_NUMBER = 3;
- static final int PHONE_CONTACT_ID = 4;
- static final int PHONE_LOOKUP_KEY = 5;
- static final int PHONE_DISPLAY_NAME = 6;
- static final int PHONE_PHOTO_ID = 7;
- static final int PHONE_LAST_TIME_USED = 8;
- static final int PHONE_TIMES_USED = 9;
- static final int PHONE_STARRED = 10;
- static final int PHONE_IS_SUPER_PRIMARY = 11;
- static final int PHONE_IN_VISIBLE_GROUP = 12;
- static final int PHONE_IS_PRIMARY = 13;
- static final int PHONE_CARRIER_PRESENCE = 14;
-
- /** Selects only rows that have been updated after a certain time stamp.*/
- static final String SELECT_UPDATED_CLAUSE =
- Phone.CONTACT_LAST_UPDATED_TIMESTAMP + " > ?";
-
- /** Ignores contacts that have an unreasonably long lookup key. These are likely to be
- * the result of multiple (> 50) merged raw contacts, and are likely to cause
- * OutOfMemoryExceptions within SQLite, or cause memory allocation problems later on
- * when iterating through the cursor set (see b/13133579)
- */
- static final String SELECT_IGNORE_LOOKUP_KEY_TOO_LONG_CLAUSE =
- "length(" + Phone.LOOKUP_KEY + ") < 1000";
-
- static final String SELECTION = SELECT_UPDATED_CLAUSE + " AND " +
- SELECT_IGNORE_LOOKUP_KEY_TOO_LONG_CLAUSE;
- }
-
- /**
- * Query for all contacts that have been updated since the last time the smart dial database
- * was updated.
- */
- public static interface UpdatedContactQuery {
- static final Uri URI = ContactsContract.Contacts.CONTENT_URI;
-
- static final String[] PROJECTION = new String[] {
- ContactsContract.Contacts._ID // 0
- };
-
- static final int UPDATED_CONTACT_ID = 0;
-
- static final String SELECT_UPDATED_CLAUSE =
- ContactsContract.Contacts.CONTACT_LAST_UPDATED_TIMESTAMP + " > ?";
- }
-
- /** Query options for querying the deleted contact database.*/
- public static interface DeleteContactQuery {
- static final Uri URI = ContactsContract.DeletedContacts.CONTENT_URI;
-
- static final String[] PROJECTION = new String[] {
- ContactsContract.DeletedContacts.CONTACT_ID, // 0
- ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP, // 1
- };
-
- static final int DELETED_CONTACT_ID = 0;
- static final int DELECTED_TIMESTAMP = 1;
-
- /** Selects only rows that have been deleted after a certain time stamp.*/
- public static final String SELECT_UPDATED_CLAUSE =
- ContactsContract.DeletedContacts.CONTACT_DELETED_TIMESTAMP + " > ?";
- }
-
- /**
- * Gets the sorting order for the smartdial table. This computes a SQL "ORDER BY" argument by
- * composing contact status and recent contact details together.
- */
- private static interface SmartDialSortingOrder {
- /** Current contacts - those contacted within the last 3 days (in milliseconds) */
- static final long LAST_TIME_USED_CURRENT_MS = 3L * 24 * 60 * 60 * 1000;
- /** Recent contacts - those contacted within the last 30 days (in milliseconds) */
- static final long LAST_TIME_USED_RECENT_MS = 30L * 24 * 60 * 60 * 1000;
-
- /** Time since last contact. */
- static final String TIME_SINCE_LAST_USED_MS = "( ?1 - " +
- Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.LAST_TIME_USED + ")";
-
- /** Contacts that have been used in the past 3 days rank higher than contacts that have
- * been used in the past 30 days, which rank higher than contacts that have not been used
- * in recent 30 days.
- */
- static final String SORT_BY_DATA_USAGE =
- "(CASE WHEN " + TIME_SINCE_LAST_USED_MS + " < " + LAST_TIME_USED_CURRENT_MS +
- " THEN 0 " +
- " WHEN " + TIME_SINCE_LAST_USED_MS + " < " + LAST_TIME_USED_RECENT_MS +
- " THEN 1 " +
- " ELSE 2 END)";
-
- /** This sort order is similar to that used by the ContactsProvider when returning a list
- * of frequently called contacts.
- */
- static final String SORT_ORDER =
- Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.STARRED + " DESC, "
- + Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.IS_SUPER_PRIMARY + " DESC, "
- + SORT_BY_DATA_USAGE + ", "
- + Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.TIMES_USED + " DESC, "
- + Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.IN_VISIBLE_GROUP + " DESC, "
- + Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", "
- + Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.CONTACT_ID + ", "
- + Tables.SMARTDIAL_TABLE + "." + SmartDialDbColumns.IS_PRIMARY + " DESC";
- }
-
- /**
- * Simple data format for a contact, containing only information needed for showing up in
- * smart dial interface.
- */
- public static class ContactNumber {
- public final long id;
- public final long dataId;
- public final String displayName;
- public final String phoneNumber;
- public final String lookupKey;
- public final long photoId;
- public final int carrierPresence;
-
- public ContactNumber(long id, long dataID, String displayName, String phoneNumber,
- String lookupKey, long photoId, int carrierPresence) {
- this.dataId = dataID;
- this.id = id;
- this.displayName = displayName;
- this.phoneNumber = phoneNumber;
- this.lookupKey = lookupKey;
- this.photoId = photoId;
- this.carrierPresence = carrierPresence;
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(id, dataId, displayName, phoneNumber, lookupKey, photoId,
- carrierPresence);
- }
-
- @Override
- public boolean equals(Object object) {
- if (this == object) {
- return true;
- }
- if (object instanceof ContactNumber) {
- final ContactNumber that = (ContactNumber) object;
- return Objects.equal(this.id, that.id)
- && Objects.equal(this.dataId, that.dataId)
- && Objects.equal(this.displayName, that.displayName)
- && Objects.equal(this.phoneNumber, that.phoneNumber)
- && Objects.equal(this.lookupKey, that.lookupKey)
- && Objects.equal(this.photoId, that.photoId)
- && Objects.equal(this.carrierPresence, that.carrierPresence);
- }
- return false;
- }
- }
-
- /**
- * Data format for finding duplicated contacts.
- */
- private class ContactMatch {
- private final String lookupKey;
- private final long id;
-
- public ContactMatch(String lookupKey, long id) {
- this.lookupKey = lookupKey;
- this.id = id;
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(lookupKey, id);
- }
-
- @Override
- public boolean equals(Object object) {
- if (this == object) {
- return true;
- }
- if (object instanceof ContactMatch) {
- final ContactMatch that = (ContactMatch) object;
- return Objects.equal(this.lookupKey, that.lookupKey)
- && Objects.equal(this.id, that.id);
- }
- return false;
- }
- }
-
- /**
- * Access function to get the singleton instance of DialerDatabaseHelper.
- */
- public static synchronized DialerDatabaseHelper getInstance(Context context) {
- if (DEBUG) {
- Log.v(TAG, "Getting Instance");
- }
- if (sSingleton == null) {
- // Use application context instead of activity context because this is a singleton,
- // and we don't want to leak the activity if the activity is not running but the
- // dialer database helper is still doing work.
- sSingleton = new DialerDatabaseHelper(context.getApplicationContext(),
- DATABASE_NAME);
- }
- return sSingleton;
- }
-
- /**
- * Returns a new instance for unit tests. The database will be created in memory.
- */
- @VisibleForTesting
- static DialerDatabaseHelper getNewInstanceForTest(Context context) {
- return new DialerDatabaseHelper(context, null, true);
- }
-
- protected DialerDatabaseHelper(Context context, String databaseName, boolean isTestInstance) {
- this(context, databaseName, DATABASE_VERSION);
- mIsTestInstance = isTestInstance;
- }
-
- protected DialerDatabaseHelper(Context context, String databaseName) {
- this(context, databaseName, DATABASE_VERSION);
- }
-
- protected DialerDatabaseHelper(Context context, String databaseName, int dbVersion) {
- super(context, databaseName, null, dbVersion);
- mContext = Preconditions.checkNotNull(context, "Context must not be null");
- }
-
- /**
- * Creates tables in the database when database is created for the first time.
- *
- * @param db The database.
- */
- @Override
- public void onCreate(SQLiteDatabase db) {
- setupTables(db);
- }
-
- private void setupTables(SQLiteDatabase db) {
- dropTables(db);
- db.execSQL("CREATE TABLE " + Tables.SMARTDIAL_TABLE + " ("
- + SmartDialDbColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
- + SmartDialDbColumns.DATA_ID + " INTEGER, "
- + SmartDialDbColumns.NUMBER + " TEXT,"
- + SmartDialDbColumns.CONTACT_ID + " INTEGER,"
- + SmartDialDbColumns.LOOKUP_KEY + " TEXT,"
- + SmartDialDbColumns.DISPLAY_NAME_PRIMARY + " TEXT, "
- + SmartDialDbColumns.PHOTO_ID + " INTEGER, "
- + SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + " LONG, "
- + SmartDialDbColumns.LAST_TIME_USED + " LONG, "
- + SmartDialDbColumns.TIMES_USED + " INTEGER, "
- + SmartDialDbColumns.STARRED + " INTEGER, "
- + SmartDialDbColumns.IS_SUPER_PRIMARY + " INTEGER, "
- + SmartDialDbColumns.IN_VISIBLE_GROUP + " INTEGER, "
- + SmartDialDbColumns.IS_PRIMARY + " INTEGER, "
- + SmartDialDbColumns.CARRIER_PRESENCE + " INTEGER NOT NULL DEFAULT 0"
- + ");");
-
- db.execSQL("CREATE TABLE " + Tables.PREFIX_TABLE + " ("
- + PrefixColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
- + PrefixColumns.PREFIX + " TEXT COLLATE NOCASE, "
- + PrefixColumns.CONTACT_ID + " INTEGER"
- + ");");
-
- db.execSQL("CREATE TABLE " + Tables.PROPERTIES + " ("
- + PropertiesColumns.PROPERTY_KEY + " TEXT PRIMARY KEY, "
- + PropertiesColumns.PROPERTY_VALUE + " TEXT "
- + ");");
-
- // This will need to also be updated in setupTablesForFilteredNumberTest and onUpgrade.
- // Hardcoded so we know on glance what columns are updated in setupTables,
- // and to be able to guarantee the state of the DB at each upgrade step.
- db.execSQL("CREATE TABLE " + Tables.FILTERED_NUMBER_TABLE + " ("
- + FilteredNumberColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
- + FilteredNumberColumns.NORMALIZED_NUMBER + " TEXT UNIQUE,"
- + FilteredNumberColumns.NUMBER + " TEXT,"
- + FilteredNumberColumns.COUNTRY_ISO + " TEXT,"
- + FilteredNumberColumns.TIMES_FILTERED + " INTEGER,"
- + FilteredNumberColumns.LAST_TIME_FILTERED + " LONG,"
- + FilteredNumberColumns.CREATION_TIME + " LONG,"
- + FilteredNumberColumns.TYPE + " INTEGER,"
- + FilteredNumberColumns.SOURCE + " INTEGER"
- + ");");
-
- createVoicemailArchiveTable(db);
- setProperty(db, DATABASE_VERSION_PROPERTY, String.valueOf(DATABASE_VERSION));
- if (!mIsTestInstance) {
- resetSmartDialLastUpdatedTime();
- }
- }
-
- public void dropTables(SQLiteDatabase db) {
- db.execSQL("DROP TABLE IF EXISTS " + Tables.PREFIX_TABLE);
- db.execSQL("DROP TABLE IF EXISTS " + Tables.SMARTDIAL_TABLE);
- db.execSQL("DROP TABLE IF EXISTS " + Tables.PROPERTIES);
- db.execSQL("DROP TABLE IF EXISTS " + Tables.FILTERED_NUMBER_TABLE);
- db.execSQL("DROP TABLE IF EXISTS " + Tables.VOICEMAIL_ARCHIVE_TABLE);
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldNumber, int newNumber) {
- // Disregard the old version and new versions provided by SQLiteOpenHelper, we will read
- // our own from the database.
-
- int oldVersion;
-
- oldVersion = getPropertyAsInt(db, DATABASE_VERSION_PROPERTY, 0);
-
- if (oldVersion == 0) {
- Log.e(TAG, "Malformed database version..recreating database");
- }
-
- if (oldVersion < 4) {
- setupTables(db);
- return;
- }
-
- if (oldVersion < 7) {
- db.execSQL("DROP TABLE IF EXISTS " + Tables.FILTERED_NUMBER_TABLE);
- db.execSQL("CREATE TABLE " + Tables.FILTERED_NUMBER_TABLE + " ("
- + FilteredNumberColumns._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
- + FilteredNumberColumns.NORMALIZED_NUMBER + " TEXT UNIQUE,"
- + FilteredNumberColumns.NUMBER + " TEXT,"
- + FilteredNumberColumns.COUNTRY_ISO + " TEXT,"
- + FilteredNumberColumns.TIMES_FILTERED + " INTEGER,"
- + FilteredNumberColumns.LAST_TIME_FILTERED + " LONG,"
- + FilteredNumberColumns.CREATION_TIME + " LONG,"
- + FilteredNumberColumns.TYPE + " INTEGER,"
- + FilteredNumberColumns.SOURCE + " INTEGER"
- + ");");
- oldVersion = 7;
- }
-
- if (oldVersion < 8) {
- upgradeToVersion8(db);
- oldVersion = 8;
- }
-
- if (oldVersion < 9) {
- db.execSQL("DROP TABLE IF EXISTS " + Tables.VOICEMAIL_ARCHIVE_TABLE);
- createVoicemailArchiveTable(db);
- oldVersion = 9;
- }
-
- if (oldVersion != DATABASE_VERSION) {
- throw new IllegalStateException(
- "error upgrading the database to version " + DATABASE_VERSION);
- }
-
- setProperty(db, DATABASE_VERSION_PROPERTY, String.valueOf(DATABASE_VERSION));
- }
-
- public void upgradeToVersion8(SQLiteDatabase db) {
- db.execSQL("ALTER TABLE smartdial_table ADD carrier_presence INTEGER NOT NULL DEFAULT 0");
- }
-
- /**
- * Stores a key-value pair in the {@link Tables#PROPERTIES} table.
- */
- public void setProperty(String key, String value) {
- setProperty(getWritableDatabase(), key, value);
- }
-
- public void setProperty(SQLiteDatabase db, String key, String value) {
- final ContentValues values = new ContentValues();
- values.put(PropertiesColumns.PROPERTY_KEY, key);
- values.put(PropertiesColumns.PROPERTY_VALUE, value);
- db.replace(Tables.PROPERTIES, null, values);
- }
-
- /**
- * Returns the value from the {@link Tables#PROPERTIES} table.
- */
- public String getProperty(String key, String defaultValue) {
- return getProperty(getReadableDatabase(), key, defaultValue);
- }
-
- public String getProperty(SQLiteDatabase db, String key, String defaultValue) {
- try {
- String value = null;
- final Cursor cursor = db.query(Tables.PROPERTIES,
- new String[] {PropertiesColumns.PROPERTY_VALUE},
- PropertiesColumns.PROPERTY_KEY + "=?",
- new String[] {key}, null, null, null);
- if (cursor != null) {
- try {
- if (cursor.moveToFirst()) {
- value = cursor.getString(0);
- }
- } finally {
- cursor.close();
- }
- }
- return value != null ? value : defaultValue;
- } catch (SQLiteException e) {
- return defaultValue;
- }
- }
-
- public int getPropertyAsInt(SQLiteDatabase db, String key, int defaultValue) {
- final String stored = getProperty(db, key, "");
- try {
- return Integer.parseInt(stored);
- } catch (NumberFormatException e) {
- return defaultValue;
- }
- }
-
- private void resetSmartDialLastUpdatedTime() {
- final SharedPreferences databaseLastUpdateSharedPref = mContext.getSharedPreferences(
- DATABASE_LAST_CREATED_SHARED_PREF, Context.MODE_PRIVATE);
- final SharedPreferences.Editor editor = databaseLastUpdateSharedPref.edit();
- editor.putLong(LAST_UPDATED_MILLIS, 0);
- editor.commit();
- }
-
- /**
- * Starts the database upgrade process in the background.
- */
- public void startSmartDialUpdateThread() {
- if (PermissionsUtil.hasContactsPermissions(mContext)) {
- new SmartDialUpdateAsyncTask().execute();
- }
- }
-
- private class SmartDialUpdateAsyncTask extends AsyncTask {
- @Override
- protected Object doInBackground(Object[] objects) {
- if (DEBUG) {
- Log.v(TAG, "Updating database");
- }
- updateSmartDialDatabase();
- return null;
- }
-
- @Override
- protected void onCancelled() {
- if (DEBUG) {
- Log.v(TAG, "Updating Cancelled");
- }
- super.onCancelled();
- }
-
- @Override
- protected void onPostExecute(Object o) {
- if (DEBUG) {
- Log.v(TAG, "Updating Finished");
- }
- super.onPostExecute(o);
- }
- }
- /**
- * Removes rows in the smartdial database that matches the contacts that have been deleted
- * by other apps since last update.
- *
- * @param db Database to operate on.
- * @param deletedContactCursor Cursor containing rows of deleted contacts
- */
- @VisibleForTesting
- void removeDeletedContacts(SQLiteDatabase db, Cursor deletedContactCursor) {
- if (deletedContactCursor == null) {
- return;
- }
-
- db.beginTransaction();
- try {
- while (deletedContactCursor.moveToNext()) {
- final Long deleteContactId =
- deletedContactCursor.getLong(DeleteContactQuery.DELETED_CONTACT_ID);
- db.delete(Tables.SMARTDIAL_TABLE,
- SmartDialDbColumns.CONTACT_ID + "=" + deleteContactId, null);
- db.delete(Tables.PREFIX_TABLE,
- PrefixColumns.CONTACT_ID + "=" + deleteContactId, null);
- }
-
- db.setTransactionSuccessful();
- } finally {
- deletedContactCursor.close();
- db.endTransaction();
- }
- }
-
- private Cursor getDeletedContactCursor(String lastUpdateMillis) {
- return mContext.getContentResolver().query(
- DeleteContactQuery.URI,
- DeleteContactQuery.PROJECTION,
- DeleteContactQuery.SELECT_UPDATED_CLAUSE,
- new String[] {lastUpdateMillis},
- null);
- }
-
- /**
- * Removes potentially corrupted entries in the database. These contacts may be added before
- * the previous instance of the dialer was destroyed for some reason. For data integrity, we
- * delete all of them.
-
- * @param db Database pointer to the dialer database.
- * @param last_update_time Time stamp of last successful update of the dialer database.
- */
- private void removePotentiallyCorruptedContacts(SQLiteDatabase db, String last_update_time) {
- db.delete(Tables.PREFIX_TABLE,
- PrefixColumns.CONTACT_ID + " IN " +
- "(SELECT " + SmartDialDbColumns.CONTACT_ID + " FROM " + Tables.SMARTDIAL_TABLE +
- " WHERE " + SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + " > " +
- last_update_time + ")",
- null);
- db.delete(Tables.SMARTDIAL_TABLE,
- SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + " > " + last_update_time, null);
- }
-
- /**
- * All columns excluding MIME_TYPE, _DATA, ARCHIVED, SERVER_ID, are the same as
- * the columns in the {@link android.provider.CallLog.Calls} table.
- *
- * @param db Database pointer to the dialer database.
- */
- private void createVoicemailArchiveTable(SQLiteDatabase db) {
- db.execSQL("CREATE TABLE " + Tables.VOICEMAIL_ARCHIVE_TABLE + " ("
- + VoicemailArchive._ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
- + VoicemailArchive.NUMBER + " TEXT,"
- + VoicemailArchive.DATE + " LONG,"
- + VoicemailArchive.DURATION + " LONG,"
- + VoicemailArchive.MIME_TYPE + " TEXT,"
- + VoicemailArchive.COUNTRY_ISO + " TEXT,"
- + VoicemailArchive._DATA + " TEXT,"
- + VoicemailArchive.GEOCODED_LOCATION + " TEXT,"
- + VoicemailArchive.CACHED_NAME + " TEXT,"
- + VoicemailArchive.CACHED_NUMBER_TYPE + " INTEGER,"
- + VoicemailArchive.CACHED_NUMBER_LABEL + " TEXT,"
- + VoicemailArchive.CACHED_LOOKUP_URI + " TEXT,"
- + VoicemailArchive.CACHED_MATCHED_NUMBER + " TEXT,"
- + VoicemailArchive.CACHED_NORMALIZED_NUMBER + " TEXT,"
- + VoicemailArchive.CACHED_PHOTO_ID + " LONG,"
- + VoicemailArchive.CACHED_FORMATTED_NUMBER + " TEXT,"
- + VoicemailArchive.ARCHIVED + " INTEGER,"
- + VoicemailArchive.NUMBER_PRESENTATION + " INTEGER,"
- + VoicemailArchive.ACCOUNT_COMPONENT_NAME + " TEXT,"
- + VoicemailArchive.ACCOUNT_ID + " TEXT,"
- + VoicemailArchive.FEATURES + " INTEGER,"
- + VoicemailArchive.SERVER_ID + " INTEGER,"
- + VoicemailArchive.TRANSCRIPTION + " TEXT,"
- + VoicemailArchive.CACHED_PHOTO_URI + " TEXT"
- + ");");
- }
-
- /**
- * Removes all entries in the smartdial contact database.
- */
- @VisibleForTesting
- void removeAllContacts(SQLiteDatabase db) {
- db.delete(Tables.SMARTDIAL_TABLE, null, null);
- db.delete(Tables.PREFIX_TABLE, null, null);
- }
-
- /**
- * Counts number of rows of the prefix table.
- */
- @VisibleForTesting
- int countPrefixTableRows(SQLiteDatabase db) {
- return (int)DatabaseUtils.longForQuery(db, "SELECT COUNT(1) FROM " + Tables.PREFIX_TABLE,
- null);
- }
-
- /**
- * Removes rows in the smartdial database that matches updated contacts.
- *
- * @param db Database pointer to the smartdial database
- * @param updatedContactCursor Cursor pointing to the list of recently updated contacts.
- */
- @VisibleForTesting
- void removeUpdatedContacts(SQLiteDatabase db, Cursor updatedContactCursor) {
- db.beginTransaction();
- try {
- updatedContactCursor.moveToPosition(-1);
- while (updatedContactCursor.moveToNext()) {
- final Long contactId =
- updatedContactCursor.getLong(UpdatedContactQuery.UPDATED_CONTACT_ID);
-
- db.delete(Tables.SMARTDIAL_TABLE, SmartDialDbColumns.CONTACT_ID + "=" +
- contactId, null);
- db.delete(Tables.PREFIX_TABLE, PrefixColumns.CONTACT_ID + "=" +
- contactId, null);
- }
-
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
-
- /**
- * Inserts updated contacts as rows to the smartdial table.
- *
- * @param db Database pointer to the smartdial database.
- * @param updatedContactCursor Cursor pointing to the list of recently updated contacts.
- * @param currentMillis Current time to be recorded in the smartdial table as update timestamp.
- */
- @VisibleForTesting
- protected void insertUpdatedContactsAndNumberPrefix(SQLiteDatabase db,
- Cursor updatedContactCursor, Long currentMillis) {
- db.beginTransaction();
- try {
- final String sqlInsert = "INSERT INTO " + Tables.SMARTDIAL_TABLE + " (" +
- SmartDialDbColumns.DATA_ID + ", " +
- SmartDialDbColumns.NUMBER + ", " +
- SmartDialDbColumns.CONTACT_ID + ", " +
- SmartDialDbColumns.LOOKUP_KEY + ", " +
- SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", " +
- SmartDialDbColumns.PHOTO_ID + ", " +
- SmartDialDbColumns.LAST_TIME_USED + ", " +
- SmartDialDbColumns.TIMES_USED + ", " +
- SmartDialDbColumns.STARRED + ", " +
- SmartDialDbColumns.IS_SUPER_PRIMARY + ", " +
- SmartDialDbColumns.IN_VISIBLE_GROUP+ ", " +
- SmartDialDbColumns.IS_PRIMARY + ", " +
- SmartDialDbColumns.CARRIER_PRESENCE + ", " +
- SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + ") " +
- " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
- final SQLiteStatement insert = db.compileStatement(sqlInsert);
-
- final String numberSqlInsert = "INSERT INTO " + Tables.PREFIX_TABLE + " (" +
- PrefixColumns.CONTACT_ID + ", " +
- PrefixColumns.PREFIX + ") " +
- " VALUES (?, ?)";
- final SQLiteStatement numberInsert = db.compileStatement(numberSqlInsert);
-
- updatedContactCursor.moveToPosition(-1);
- while (updatedContactCursor.moveToNext()) {
- insert.clearBindings();
-
- // Handle string columns which can possibly be null first. In the case of certain
- // null columns (due to malformed rows possibly inserted by third-party apps
- // or sync adapters), skip the phone number row.
- final String number = updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER);
- if (TextUtils.isEmpty(number)) {
- continue;
- } else {
- insert.bindString(2, number);
- }
-
- final String lookupKey = updatedContactCursor.getString(
- PhoneQuery.PHONE_LOOKUP_KEY);
- if (TextUtils.isEmpty(lookupKey)) {
- continue;
- } else {
- insert.bindString(4, lookupKey);
- }
-
- final String displayName = updatedContactCursor.getString(
- PhoneQuery.PHONE_DISPLAY_NAME);
- if (displayName == null) {
- insert.bindString(5, mContext.getResources().getString(R.string.missing_name));
- } else {
- insert.bindString(5, displayName);
- }
- insert.bindLong(1, updatedContactCursor.getLong(PhoneQuery.PHONE_ID));
- insert.bindLong(3, updatedContactCursor.getLong(PhoneQuery.PHONE_CONTACT_ID));
- insert.bindLong(6, updatedContactCursor.getLong(PhoneQuery.PHONE_PHOTO_ID));
- insert.bindLong(7, updatedContactCursor.getLong(PhoneQuery.PHONE_LAST_TIME_USED));
- insert.bindLong(8, updatedContactCursor.getInt(PhoneQuery.PHONE_TIMES_USED));
- insert.bindLong(9, updatedContactCursor.getInt(PhoneQuery.PHONE_STARRED));
- insert.bindLong(10, updatedContactCursor.getInt(PhoneQuery.PHONE_IS_SUPER_PRIMARY));
- insert.bindLong(11, updatedContactCursor.getInt(PhoneQuery.PHONE_IN_VISIBLE_GROUP));
- insert.bindLong(12, updatedContactCursor.getInt(PhoneQuery.PHONE_IS_PRIMARY));
- insert.bindLong(13, updatedContactCursor.getInt(PhoneQuery.PHONE_CARRIER_PRESENCE));
- insert.bindLong(14, currentMillis);
- insert.executeInsert();
- final String contactPhoneNumber =
- updatedContactCursor.getString(PhoneQuery.PHONE_NUMBER);
- final ArrayList<String> numberPrefixes =
- SmartDialPrefix.parseToNumberTokens(contactPhoneNumber);
-
- for (String numberPrefix : numberPrefixes) {
- numberInsert.bindLong(1, updatedContactCursor.getLong(
- PhoneQuery.PHONE_CONTACT_ID));
- numberInsert.bindString(2, numberPrefix);
- numberInsert.executeInsert();
- numberInsert.clearBindings();
- }
- }
-
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
-
- /**
- * Inserts prefixes of contact names to the prefix table.
- *
- * @param db Database pointer to the smartdial database.
- * @param nameCursor Cursor pointing to the list of distinct updated contacts.
- */
- @VisibleForTesting
- void insertNamePrefixes(SQLiteDatabase db, Cursor nameCursor) {
- final int columnIndexName = nameCursor.getColumnIndex(
- SmartDialDbColumns.DISPLAY_NAME_PRIMARY);
- final int columnIndexContactId = nameCursor.getColumnIndex(SmartDialDbColumns.CONTACT_ID);
-
- db.beginTransaction();
- try {
- final String sqlInsert = "INSERT INTO " + Tables.PREFIX_TABLE + " (" +
- PrefixColumns.CONTACT_ID + ", " +
- PrefixColumns.PREFIX + ") " +
- " VALUES (?, ?)";
- final SQLiteStatement insert = db.compileStatement(sqlInsert);
-
- while (nameCursor.moveToNext()) {
- /** Computes a list of prefixes of a given contact name. */
- final ArrayList<String> namePrefixes =
- SmartDialPrefix.generateNamePrefixes(nameCursor.getString(columnIndexName));
-
- for (String namePrefix : namePrefixes) {
- insert.bindLong(1, nameCursor.getLong(columnIndexContactId));
- insert.bindString(2, namePrefix);
- insert.executeInsert();
- insert.clearBindings();
- }
- }
-
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
-
- /**
- * Updates the smart dial and prefix database.
- * This method queries the Delta API to get changed contacts since last update, and updates the
- * records in smartdial database and prefix database accordingly.
- * It also queries the deleted contact database to remove newly deleted contacts since last
- * update.
- */
- public void updateSmartDialDatabase() {
- final SQLiteDatabase db = getWritableDatabase();
-
- synchronized(mLock) {
- if (DEBUG) {
- Log.v(TAG, "Starting to update database");
- }
- final StopWatch stopWatch = DEBUG ? StopWatch.start("Updating databases") : null;
-
- /** Gets the last update time on the database. */
- final SharedPreferences databaseLastUpdateSharedPref = mContext.getSharedPreferences(
- DATABASE_LAST_CREATED_SHARED_PREF, Context.MODE_PRIVATE);
- final String lastUpdateMillis = String.valueOf(
- databaseLastUpdateSharedPref.getLong(LAST_UPDATED_MILLIS, 0));
-
- if (DEBUG) {
- Log.v(TAG, "Last updated at " + lastUpdateMillis);
- }
-
- /** Sets the time after querying the database as the current update time. */
- final Long currentMillis = System.currentTimeMillis();
-
- if (DEBUG) {
- stopWatch.lap("Queried the Contacts database");
- }
-
- /** Prevents the app from reading the dialer database when updating. */
- sInUpdate.getAndSet(true);
-
- /** Removes contacts that have been deleted. */
- removeDeletedContacts(db, getDeletedContactCursor(lastUpdateMillis));
- removePotentiallyCorruptedContacts(db, lastUpdateMillis);
-
- if (DEBUG) {
- stopWatch.lap("Finished deleting deleted entries");
- }
-
- /** If the database did not exist before, jump through deletion as there is nothing
- * to delete.
- */
- if (!lastUpdateMillis.equals("0")) {
- /** Removes contacts that have been updated. Updated contact information will be
- * inserted later. Note that this has to use a separate result set from
- * updatePhoneCursor, since it is possible for a contact to be updated (e.g.
- * phone number deleted), but have no results show up in updatedPhoneCursor (since
- * all of its phone numbers have been deleted).
- */
- final Cursor updatedContactCursor = mContext.getContentResolver().query(
- UpdatedContactQuery.URI,
- UpdatedContactQuery.PROJECTION,
- UpdatedContactQuery.SELECT_UPDATED_CLAUSE,
- new String[] {lastUpdateMillis},
- null
- );
- if (updatedContactCursor == null) {
- Log.e(TAG, "SmartDial query received null for cursor");
- return;
- }
- try {
- removeUpdatedContacts(db, updatedContactCursor);
- } finally {
- updatedContactCursor.close();
- }
- if (DEBUG) {
- stopWatch.lap("Finished deleting entries belonging to updated contacts");
- }
- }
-
- /** Queries the contact database to get all phone numbers that have been updated since the last
- * update time.
- */
- final Cursor updatedPhoneCursor = mContext.getContentResolver().query(PhoneQuery.URI,
- PhoneQuery.PROJECTION, PhoneQuery.SELECTION,
- new String[]{lastUpdateMillis}, null);
- if (updatedPhoneCursor == null) {
- Log.e(TAG, "SmartDial query received null for cursor");
- return;
- }
-
- try {
- /** Inserts recently updated phone numbers to the smartdial database.*/
- insertUpdatedContactsAndNumberPrefix(db, updatedPhoneCursor, currentMillis);
- if (DEBUG) {
- stopWatch.lap("Finished building the smart dial table");
- }
- } finally {
- updatedPhoneCursor.close();
- }
-
- /** Gets a list of distinct contacts which have been updated, and adds the name prefixes
- * of these contacts to the prefix table.
- */
- final Cursor nameCursor = db.rawQuery(
- "SELECT DISTINCT " +
- SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", " + SmartDialDbColumns.CONTACT_ID +
- " FROM " + Tables.SMARTDIAL_TABLE +
- " WHERE " + SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME +
- " = " + Long.toString(currentMillis),
- new String[] {});
- if (nameCursor != null) {
- try {
- if (DEBUG) {
- stopWatch.lap("Queried the smart dial table for contact names");
- }
-
- /** Inserts prefixes of names into the prefix table.*/
- insertNamePrefixes(db, nameCursor);
- if (DEBUG) {
- stopWatch.lap("Finished building the name prefix table");
- }
- } finally {
- nameCursor.close();
- }
- }
-
- /** Creates index on contact_id for fast JOIN operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS smartdial_contact_id_index ON " +
- Tables.SMARTDIAL_TABLE + " (" + SmartDialDbColumns.CONTACT_ID + ");");
- /** Creates index on last_smartdial_update_time for fast SELECT operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS smartdial_last_update_index ON " +
- Tables.SMARTDIAL_TABLE + " (" +
- SmartDialDbColumns.LAST_SMARTDIAL_UPDATE_TIME + ");");
- /** Creates index on sorting fields for fast sort operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS smartdial_sort_index ON " +
- Tables.SMARTDIAL_TABLE + " (" +
- SmartDialDbColumns.STARRED + ", " +
- SmartDialDbColumns.IS_SUPER_PRIMARY + ", " +
- SmartDialDbColumns.LAST_TIME_USED + ", " +
- SmartDialDbColumns.TIMES_USED + ", " +
- SmartDialDbColumns.IN_VISIBLE_GROUP + ", " +
- SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", " +
- SmartDialDbColumns.CONTACT_ID + ", " +
- SmartDialDbColumns.IS_PRIMARY +
- ");");
- /** Creates index on prefix for fast SELECT operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS nameprefix_index ON " +
- Tables.PREFIX_TABLE + " (" + PrefixColumns.PREFIX + ");");
- /** Creates index on contact_id for fast JOIN operation. */
- db.execSQL("CREATE INDEX IF NOT EXISTS nameprefix_contact_id_index ON " +
- Tables.PREFIX_TABLE + " (" + PrefixColumns.CONTACT_ID + ");");
-
- if (DEBUG) {
- stopWatch.lap(TAG + "Finished recreating index");
- }
-
- /** Updates the database index statistics.*/
- db.execSQL("ANALYZE " + Tables.SMARTDIAL_TABLE);
- db.execSQL("ANALYZE " + Tables.PREFIX_TABLE);
- db.execSQL("ANALYZE smartdial_contact_id_index");
- db.execSQL("ANALYZE smartdial_last_update_index");
- db.execSQL("ANALYZE nameprefix_index");
- db.execSQL("ANALYZE nameprefix_contact_id_index");
- if (DEBUG) {
- stopWatch.stopAndLog(TAG + "Finished updating index stats", 0);
- }
-
- sInUpdate.getAndSet(false);
-
- final SharedPreferences.Editor editor = databaseLastUpdateSharedPref.edit();
- editor.putLong(LAST_UPDATED_MILLIS, currentMillis);
- editor.commit();
-
- // Notify content observers that smart dial database has been updated.
- mContext.getContentResolver().notifyChange(SMART_DIAL_UPDATED_URI, null, false);
- }
- }
-
- /**
- * Returns a list of candidate contacts where the query is a prefix of the dialpad index of
- * the contact's name or phone number.
- *
- * @param query The prefix of a contact's dialpad index.
- * @return A list of top candidate contacts that will be suggested to user to match their input.
- */
- public ArrayList<ContactNumber> getLooseMatches(String query,
- SmartDialNameMatcher nameMatcher) {
- final boolean inUpdate = sInUpdate.get();
- if (inUpdate) {
- return Lists.newArrayList();
- }
-
- final SQLiteDatabase db = getReadableDatabase();
-
- /** Uses SQL query wildcard '%' to represent prefix matching.*/
- final String looseQuery = query + "%";
-
- final ArrayList<ContactNumber> result = Lists.newArrayList();
-
- final StopWatch stopWatch = DEBUG ? StopWatch.start(":Name Prefix query") : null;
-
- final String currentTimeStamp = Long.toString(System.currentTimeMillis());
-
- /** Queries the database to find contacts that have an index matching the query prefix. */
- final Cursor cursor = db.rawQuery("SELECT " +
- SmartDialDbColumns.DATA_ID + ", " +
- SmartDialDbColumns.DISPLAY_NAME_PRIMARY + ", " +
- SmartDialDbColumns.PHOTO_ID + ", " +
- SmartDialDbColumns.NUMBER + ", " +
- SmartDialDbColumns.CONTACT_ID + ", " +
- SmartDialDbColumns.LOOKUP_KEY + ", " +
- SmartDialDbColumns.CARRIER_PRESENCE +
- " FROM " + Tables.SMARTDIAL_TABLE + " WHERE " +
- SmartDialDbColumns.CONTACT_ID + " IN " +
- " (SELECT " + PrefixColumns.CONTACT_ID +
- " FROM " + Tables.PREFIX_TABLE +
- " WHERE " + Tables.PREFIX_TABLE + "." + PrefixColumns.PREFIX +
- " LIKE '" + looseQuery + "')" +
- " ORDER BY " + SmartDialSortingOrder.SORT_ORDER,
- new String[] {currentTimeStamp});
- if (cursor == null) {
- return result;
- }
- try {
- if (DEBUG) {
- stopWatch.lap("Prefix query completed");
- }
-
- /** Gets the column ID from the cursor.*/
- final int columnDataId = 0;
- final int columnDisplayNamePrimary = 1;
- final int columnPhotoId = 2;
- final int columnNumber = 3;
- final int columnId = 4;
- final int columnLookupKey = 5;
- final int columnCarrierPresence = 6;
- if (DEBUG) {
- stopWatch.lap("Found column IDs");
- }
-
- final Set<ContactMatch> duplicates = new HashSet<ContactMatch>();
- int counter = 0;
- if (DEBUG) {
- stopWatch.lap("Moved cursor to start");
- }
- /** Iterates the cursor to find top contact suggestions without duplication.*/
- while ((cursor.moveToNext()) && (counter < MAX_ENTRIES)) {
- final long dataID = cursor.getLong(columnDataId);
- final String displayName = cursor.getString(columnDisplayNamePrimary);
- final String phoneNumber = cursor.getString(columnNumber);
- final long id = cursor.getLong(columnId);
- final long photoId = cursor.getLong(columnPhotoId);
- final String lookupKey = cursor.getString(columnLookupKey);
- final int carrierPresence = cursor.getInt(columnCarrierPresence);
-
- /** If a contact already exists and another phone number of the contact is being
- * processed, skip the second instance.
- */
- final ContactMatch contactMatch = new ContactMatch(lookupKey, id);
- if (duplicates.contains(contactMatch)) {
- continue;
- }
-
- /**
- * If the contact has either the name or number that matches the query, add to the
- * result.
- */
- final boolean nameMatches = nameMatcher.matches(displayName);
- final boolean numberMatches =
- (nameMatcher.matchesNumber(phoneNumber, query) != null);
- if (nameMatches || numberMatches) {
- /** If a contact has not been added, add it to the result and the hash set.*/
- duplicates.add(contactMatch);
- result.add(new ContactNumber(id, dataID, displayName, phoneNumber, lookupKey,
- photoId, carrierPresence));
- counter++;
- if (DEBUG) {
- stopWatch.lap("Added one result: Name: " + displayName);
- }
- }
- }
-
- if (DEBUG) {
- stopWatch.stopAndLog(TAG + "Finished loading cursor", 0);
- }
- } finally {
- cursor.close();
- }
- return result;
- }
-}