/src/com/android/providers/contacts/DataRowHandler.java
Java | 376 lines | 271 code | 44 blank | 61 comment | 73 complexity | 06fdf5f44b19209e1cf415394d53cc82 MD5 | raw file
- /*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License
- */
- package com.android.providers.contacts;
- import android.content.ContentValues;
- import android.content.Context;
- import android.database.Cursor;
- import android.database.sqlite.SQLiteDatabase;
- import android.provider.ContactsContract.CommonDataKinds.Email;
- import android.provider.ContactsContract.CommonDataKinds.Nickname;
- import android.provider.ContactsContract.CommonDataKinds.Organization;
- import android.provider.ContactsContract.CommonDataKinds.Phone;
- import android.provider.ContactsContract.CommonDataKinds.StructuredName;
- import android.provider.ContactsContract.Data;
- import android.text.TextUtils;
- import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
- import com.android.providers.contacts.ContactsDatabaseHelper.MimetypesColumns;
- import com.android.providers.contacts.ContactsDatabaseHelper.PresenceColumns;
- import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
- import com.android.providers.contacts.aggregation.AbstractContactAggregator;
- /**
- * Handles inserts and update for a specific Data type.
- */
- public abstract class DataRowHandler {
- public interface DataDeleteQuery {
- public static final String TABLE = Tables.DATA_JOIN_MIMETYPES;
- public static final String[] CONCRETE_COLUMNS = new String[] {
- DataColumns.CONCRETE_ID,
- MimetypesColumns.MIMETYPE,
- Data.RAW_CONTACT_ID,
- Data.IS_PRIMARY,
- Data.DATA1,
- };
- public static final String[] COLUMNS = new String[] {
- Data._ID,
- MimetypesColumns.MIMETYPE,
- Data.RAW_CONTACT_ID,
- Data.IS_PRIMARY,
- Data.DATA1,
- };
- public static final int _ID = 0;
- public static final int MIMETYPE = 1;
- public static final int RAW_CONTACT_ID = 2;
- public static final int IS_PRIMARY = 3;
- public static final int DATA1 = 4;
- }
- public interface DataUpdateQuery {
- String[] COLUMNS = { Data._ID, Data.RAW_CONTACT_ID, Data.MIMETYPE };
- int _ID = 0;
- int RAW_CONTACT_ID = 1;
- int MIMETYPE = 2;
- }
- protected final Context mContext;
- protected final ContactsDatabaseHelper mDbHelper;
- protected final AbstractContactAggregator mContactAggregator;
- protected String[] mSelectionArgs1 = new String[1];
- protected final String mMimetype;
- protected long mMimetypeId;
- @SuppressWarnings("all")
- public DataRowHandler(Context context, ContactsDatabaseHelper dbHelper,
- AbstractContactAggregator aggregator, String mimetype) {
- mContext = context;
- mDbHelper = dbHelper;
- mContactAggregator = aggregator;
- mMimetype = mimetype;
- // To ensure the data column position. This is dead code if properly configured.
- if (StructuredName.DISPLAY_NAME != Data.DATA1 || Nickname.NAME != Data.DATA1
- || Organization.COMPANY != Data.DATA1 || Phone.NUMBER != Data.DATA1
- || Email.DATA != Data.DATA1) {
- throw new AssertionError("Some of ContactsContract.CommonDataKinds class primary"
- + " data is not in DATA1 column");
- }
- }
- protected long getMimeTypeId() {
- if (mMimetypeId == 0) {
- mMimetypeId = mDbHelper.getMimeTypeId(mMimetype);
- }
- return mMimetypeId;
- }
- /**
- * Inserts a row into the {@link Data} table.
- */
- public long insert(SQLiteDatabase db, TransactionContext txContext, long rawContactId,
- ContentValues values) {
- final long dataId = db.insert(Tables.DATA, null, values);
- final Integer primary = values.getAsInteger(Data.IS_PRIMARY);
- final Integer superPrimary = values.getAsInteger(Data.IS_SUPER_PRIMARY);
- if ((primary != null && primary != 0) || (superPrimary != null && superPrimary != 0)) {
- final long mimeTypeId = getMimeTypeId();
- mDbHelper.setIsPrimary(rawContactId, dataId, mimeTypeId);
- // We also have to make sure that no other data item on this raw_contact is
- // configured super primary
- if (superPrimary != null) {
- if (superPrimary != 0) {
- mDbHelper.setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
- } else {
- mDbHelper.clearSuperPrimary(rawContactId, mimeTypeId);
- }
- } else {
- // if there is already another data item configured as super-primary,
- // take over the flag (which will automatically remove it from the other item)
- if (mDbHelper.rawContactHasSuperPrimary(rawContactId, mimeTypeId)) {
- mDbHelper.setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
- }
- }
- }
- if (containsSearchableColumns(values)) {
- txContext.invalidateSearchIndexForRawContact(rawContactId);
- }
- return dataId;
- }
- /**
- * Validates data and updates a {@link Data} row using the cursor, which contains
- * the current data.
- *
- * @return true if update changed something
- */
- public boolean update(SQLiteDatabase db, TransactionContext txContext,
- ContentValues values, Cursor c, boolean callerIsSyncAdapter) {
- long dataId = c.getLong(DataUpdateQuery._ID);
- long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
- handlePrimaryAndSuperPrimary(values, dataId, rawContactId);
- if (values.size() > 0) {
- mSelectionArgs1[0] = String.valueOf(dataId);
- db.update(Tables.DATA, values, Data._ID + " =?", mSelectionArgs1);
- }
- if (containsSearchableColumns(values)) {
- txContext.invalidateSearchIndexForRawContact(rawContactId);
- }
- txContext.markRawContactDirtyAndChanged(rawContactId, callerIsSyncAdapter);
- return true;
- }
- public boolean hasSearchableData() {
- return false;
- }
- public boolean containsSearchableColumns(ContentValues values) {
- return false;
- }
- public void appendSearchableData(SearchIndexManager.IndexBuilder builder) {
- }
- /**
- * Ensures that all super-primary and primary flags of this raw_contact are
- * configured correctly
- */
- private void handlePrimaryAndSuperPrimary(ContentValues values, long dataId,
- long rawContactId) {
- final boolean hasPrimary = values.getAsInteger(Data.IS_PRIMARY) != null;
- final boolean hasSuperPrimary = values.getAsInteger(Data.IS_SUPER_PRIMARY) != null;
- // Nothing to do? Bail out early
- if (!hasPrimary && !hasSuperPrimary) return;
- final long mimeTypeId = getMimeTypeId();
- // Check if we want to clear values
- final boolean clearPrimary = hasPrimary &&
- values.getAsInteger(Data.IS_PRIMARY) == 0;
- final boolean clearSuperPrimary = hasSuperPrimary &&
- values.getAsInteger(Data.IS_SUPER_PRIMARY) == 0;
- if (clearPrimary || clearSuperPrimary) {
- // Test whether these values are currently set
- mSelectionArgs1[0] = String.valueOf(dataId);
- final String[] cols = new String[] { Data.IS_PRIMARY, Data.IS_SUPER_PRIMARY };
- final Cursor c = mDbHelper.getReadableDatabase().query(Tables.DATA,
- cols, Data._ID + "=?", mSelectionArgs1, null, null, null);
- try {
- if (c.moveToFirst()) {
- final boolean isPrimary = c.getInt(0) != 0;
- final boolean isSuperPrimary = c.getInt(1) != 0;
- // Clear values if they are currently set
- if (isSuperPrimary) {
- mDbHelper.clearSuperPrimary(rawContactId, mimeTypeId);
- }
- if (clearPrimary && isPrimary) {
- mDbHelper.setIsPrimary(rawContactId, -1, mimeTypeId);
- }
- }
- } finally {
- c.close();
- }
- } else {
- // Check if we want to set values
- final boolean setPrimary = hasPrimary &&
- values.getAsInteger(Data.IS_PRIMARY) != 0;
- final boolean setSuperPrimary = hasSuperPrimary &&
- values.getAsInteger(Data.IS_SUPER_PRIMARY) != 0;
- if (setSuperPrimary) {
- // Set both super primary and primary
- mDbHelper.setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
- mDbHelper.setIsPrimary(rawContactId, dataId, mimeTypeId);
- } else if (setPrimary) {
- // Primary was explicitly set, but super-primary was not.
- // In this case we set super-primary on this data item, if
- // any data item of the same raw-contact already is super-primary
- if (mDbHelper.rawContactHasSuperPrimary(rawContactId, mimeTypeId)) {
- mDbHelper.setIsSuperPrimary(rawContactId, dataId, mimeTypeId);
- }
- mDbHelper.setIsPrimary(rawContactId, dataId, mimeTypeId);
- }
- }
- // Now that we've taken care of clearing this, remove it from "values".
- values.remove(Data.IS_SUPER_PRIMARY);
- values.remove(Data.IS_PRIMARY);
- }
- public int delete(SQLiteDatabase db, TransactionContext txContext, Cursor c) {
- long dataId = c.getLong(DataDeleteQuery._ID);
- long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
- boolean primary = c.getInt(DataDeleteQuery.IS_PRIMARY) != 0;
- mSelectionArgs1[0] = String.valueOf(dataId);
- int count = db.delete(Tables.DATA, Data._ID + "=?", mSelectionArgs1);
- mSelectionArgs1[0] = String.valueOf(rawContactId);
- db.delete(Tables.PRESENCE, PresenceColumns.RAW_CONTACT_ID + "=?", mSelectionArgs1);
- if (count != 0 && primary) {
- fixPrimary(db, rawContactId);
- }
- if (hasSearchableData()) {
- txContext.invalidateSearchIndexForRawContact(rawContactId);
- }
- return count;
- }
- private void fixPrimary(SQLiteDatabase db, long rawContactId) {
- long mimeTypeId = getMimeTypeId();
- long primaryId = -1;
- int primaryType = -1;
- mSelectionArgs1[0] = String.valueOf(rawContactId);
- Cursor c = db.query(DataDeleteQuery.TABLE,
- DataDeleteQuery.CONCRETE_COLUMNS,
- Data.RAW_CONTACT_ID + "=?" +
- " AND " + DataColumns.MIMETYPE_ID + "=" + mimeTypeId,
- mSelectionArgs1, null, null, null);
- try {
- while (c.moveToNext()) {
- long dataId = c.getLong(DataDeleteQuery._ID);
- int type = c.getInt(DataDeleteQuery.DATA1);
- if (primaryType == -1 || getTypeRank(type) < getTypeRank(primaryType)) {
- primaryId = dataId;
- primaryType = type;
- }
- }
- } finally {
- c.close();
- }
- if (primaryId != -1) {
- mDbHelper.setIsPrimary(rawContactId, primaryId, mimeTypeId);
- }
- }
- /**
- * Returns the rank of a specific record type to be used in determining the primary
- * row. Lower number represents higher priority.
- */
- protected int getTypeRank(int type) {
- return 0;
- }
- protected void fixRawContactDisplayName(SQLiteDatabase db, TransactionContext txContext,
- long rawContactId) {
- if (!isNewRawContact(txContext, rawContactId)) {
- mDbHelper.updateRawContactDisplayName(db, rawContactId);
- mContactAggregator.updateDisplayNameForRawContact(db, rawContactId);
- }
- }
- private boolean isNewRawContact(TransactionContext txContext, long rawContactId) {
- return txContext.isNewRawContact(rawContactId);
- }
- /**
- * Return set of values, using current values at given {@link Data#_ID}
- * as baseline, but augmented with any updates. Returns null if there is
- * no change.
- */
- public ContentValues getAugmentedValues(SQLiteDatabase db, long dataId,
- ContentValues update) {
- boolean changing = false;
- final ContentValues values = new ContentValues();
- mSelectionArgs1[0] = String.valueOf(dataId);
- final Cursor cursor = db.query(Tables.DATA, null, Data._ID + "=?",
- mSelectionArgs1, null, null, null);
- try {
- if (cursor.moveToFirst()) {
- for (int i = 0; i < cursor.getColumnCount(); i++) {
- final String key = cursor.getColumnName(i);
- final String value = cursor.getString(i);
- if (!changing && update.containsKey(key)) {
- Object newValue = update.get(key);
- String newString = newValue == null ? null : newValue.toString();
- changing |= !TextUtils.equals(newString, value);
- }
- values.put(key, value);
- }
- }
- } finally {
- cursor.close();
- }
- if (!changing) {
- return null;
- }
- values.putAll(update);
- return values;
- }
- public void triggerAggregation(TransactionContext txContext, long rawContactId) {
- mContactAggregator.triggerAggregation(txContext, rawContactId);
- }
- /**
- * Test all against {@link TextUtils#isEmpty(CharSequence)}.
- */
- public boolean areAllEmpty(ContentValues values, String[] keys) {
- for (String key : keys) {
- if (!TextUtils.isEmpty(values.getAsString(key))) {
- return false;
- }
- }
- return true;
- }
- /**
- * Returns true if a value (possibly null) is specified for at least one of the supplied keys.
- */
- public boolean areAnySpecified(ContentValues values, String[] keys) {
- for (String key : keys) {
- if (values.containsKey(key)) {
- return true;
- }
- }
- return false;
- }
- }