Home | History | Annotate | Download | only in contacts
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
      5  * use this file except in compliance with the License. You may obtain a copy of
      6  * the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License
     15  */
     16 package com.android.providers.contacts;
     17 
     18 import android.content.ContentValues;
     19 import android.content.Context;
     20 import android.database.Cursor;
     21 import android.database.sqlite.SQLiteDatabase;
     22 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
     23 import android.provider.ContactsContract.FullNameStyle;
     24 import android.provider.ContactsContract.PhoneticNameStyle;
     25 import android.text.TextUtils;
     26 
     27 import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
     28 import com.android.providers.contacts.aggregation.ContactAggregator;
     29 
     30 /**
     31  * Handler for email address data rows.
     32  */
     33 public class DataRowHandlerForStructuredName extends DataRowHandler {
     34     private final NameSplitter mSplitter;
     35     private final NameLookupBuilder mNameLookupBuilder;
     36     private final StringBuilder mSb = new StringBuilder();
     37 
     38     public DataRowHandlerForStructuredName(Context context, ContactsDatabaseHelper dbHelper,
     39             ContactAggregator aggregator, NameSplitter splitter,
     40             NameLookupBuilder nameLookupBuilder) {
     41         super(context, dbHelper, aggregator, StructuredName.CONTENT_ITEM_TYPE);
     42         mSplitter = splitter;
     43         mNameLookupBuilder = nameLookupBuilder;
     44     }
     45 
     46     @Override
     47     public long insert(SQLiteDatabase db, TransactionContext txContext, long rawContactId,
     48             ContentValues values) {
     49         fixStructuredNameComponents(values, values);
     50 
     51         long dataId = super.insert(db, txContext, rawContactId, values);
     52 
     53         String name = values.getAsString(StructuredName.DISPLAY_NAME);
     54         Integer fullNameStyle = values.getAsInteger(StructuredName.FULL_NAME_STYLE);
     55         mNameLookupBuilder.insertNameLookup(rawContactId, dataId, name,
     56                 fullNameStyle != null
     57                         ? mSplitter.getAdjustedFullNameStyle(fullNameStyle)
     58                         : FullNameStyle.UNDEFINED);
     59         insertNameLookupForPhoneticName(rawContactId, dataId, values);
     60         fixRawContactDisplayName(db, txContext, rawContactId);
     61         triggerAggregation(txContext, rawContactId);
     62         return dataId;
     63     }
     64 
     65     @Override
     66     public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
     67             Cursor c, boolean callerIsSyncAdapter) {
     68         final long dataId = c.getLong(DataUpdateQuery._ID);
     69         final long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
     70 
     71         final ContentValues augmented = getAugmentedValues(db, dataId, values);
     72         if (augmented == null) {  // No change
     73             return false;
     74         }
     75 
     76         fixStructuredNameComponents(augmented, values);
     77 
     78         super.update(db, txContext, values, c, callerIsSyncAdapter);
     79         if (values.containsKey(StructuredName.DISPLAY_NAME) ||
     80                 values.containsKey(StructuredName.PHONETIC_FAMILY_NAME) ||
     81                 values.containsKey(StructuredName.PHONETIC_MIDDLE_NAME) ||
     82                 values.containsKey(StructuredName.PHONETIC_GIVEN_NAME)) {
     83             augmented.putAll(values);
     84             String name = augmented.getAsString(StructuredName.DISPLAY_NAME);
     85             mDbHelper.deleteNameLookup(dataId);
     86             Integer fullNameStyle = augmented.getAsInteger(StructuredName.FULL_NAME_STYLE);
     87             mNameLookupBuilder.insertNameLookup(rawContactId, dataId, name,
     88                     fullNameStyle != null
     89                             ? mSplitter.getAdjustedFullNameStyle(fullNameStyle)
     90                             : FullNameStyle.UNDEFINED);
     91             insertNameLookupForPhoneticName(rawContactId, dataId, augmented);
     92         }
     93         fixRawContactDisplayName(db, txContext, rawContactId);
     94         triggerAggregation(txContext, rawContactId);
     95         return true;
     96     }
     97 
     98     @Override
     99     public int delete(SQLiteDatabase db, TransactionContext txContext, Cursor c) {
    100         long dataId = c.getLong(DataDeleteQuery._ID);
    101         long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
    102 
    103         int count = super.delete(db, txContext, c);
    104 
    105         mDbHelper.deleteNameLookup(dataId);
    106         fixRawContactDisplayName(db, txContext, rawContactId);
    107         triggerAggregation(txContext, rawContactId);
    108         return count;
    109     }
    110 
    111     /**
    112      * Specific list of structured fields.
    113      */
    114     private final String[] STRUCTURED_FIELDS = new String[] {
    115             StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME,
    116             StructuredName.FAMILY_NAME, StructuredName.SUFFIX
    117     };
    118 
    119     /**
    120      * Parses the supplied display name, but only if the incoming values do
    121      * not already contain structured name parts. Also, if the display name
    122      * is not provided, generate one by concatenating first name and last
    123      * name.
    124      */
    125     public void fixStructuredNameComponents(ContentValues augmented, ContentValues update) {
    126         final String unstruct = update.getAsString(StructuredName.DISPLAY_NAME);
    127 
    128         final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
    129         final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
    130 
    131         if (touchedUnstruct && !touchedStruct) {
    132             NameSplitter.Name name = new NameSplitter.Name();
    133             mSplitter.split(name, unstruct);
    134             name.toValues(update);
    135         } else if (!touchedUnstruct
    136                 && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
    137             // We need to update the display name when any structured components
    138             // are specified, even when they are null, which is why we are checking
    139             // areAnySpecified.  The touchedStruct in the condition is an optimization:
    140             // if there are non-null values, we know for a fact that some values are present.
    141             NameSplitter.Name name = new NameSplitter.Name();
    142             name.fromValues(augmented);
    143             // As the name could be changed, let's guess the name style again.
    144             name.fullNameStyle = FullNameStyle.UNDEFINED;
    145             name.phoneticNameStyle = PhoneticNameStyle.UNDEFINED;
    146             mSplitter.guessNameStyle(name);
    147             int unadjustedFullNameStyle = name.fullNameStyle;
    148             name.fullNameStyle = mSplitter.getAdjustedFullNameStyle(name.fullNameStyle);
    149             final String joined = mSplitter.join(name, true, true);
    150             update.put(StructuredName.DISPLAY_NAME, joined);
    151 
    152             update.put(StructuredName.FULL_NAME_STYLE, unadjustedFullNameStyle);
    153             update.put(StructuredName.PHONETIC_NAME_STYLE, name.phoneticNameStyle);
    154         } else if (touchedUnstruct && touchedStruct){
    155             if (!update.containsKey(StructuredName.FULL_NAME_STYLE)) {
    156                 update.put(StructuredName.FULL_NAME_STYLE,
    157                         mSplitter.guessFullNameStyle(unstruct));
    158             }
    159             if (!update.containsKey(StructuredName.PHONETIC_NAME_STYLE)) {
    160                 NameSplitter.Name name = new NameSplitter.Name();
    161                 name.fromValues(update);
    162                 name.phoneticNameStyle = PhoneticNameStyle.UNDEFINED;
    163                 mSplitter.guessNameStyle(name);
    164                 update.put(StructuredName.PHONETIC_NAME_STYLE, name.phoneticNameStyle);
    165             }
    166         }
    167     }
    168 
    169     public void insertNameLookupForPhoneticName(long rawContactId, long dataId,
    170             ContentValues values) {
    171         if (values.containsKey(StructuredName.PHONETIC_FAMILY_NAME)
    172                 || values.containsKey(StructuredName.PHONETIC_GIVEN_NAME)
    173                 || values.containsKey(StructuredName.PHONETIC_MIDDLE_NAME)) {
    174             mDbHelper.insertNameLookupForPhoneticName(rawContactId, dataId,
    175                     values.getAsString(StructuredName.PHONETIC_FAMILY_NAME),
    176                     values.getAsString(StructuredName.PHONETIC_MIDDLE_NAME),
    177                     values.getAsString(StructuredName.PHONETIC_GIVEN_NAME));
    178         }
    179     }
    180 
    181     @Override
    182     public boolean hasSearchableData() {
    183         return true;
    184     }
    185 
    186     @Override
    187     public boolean containsSearchableColumns(ContentValues values) {
    188         return values.containsKey(StructuredName.FAMILY_NAME)
    189                 || values.containsKey(StructuredName.GIVEN_NAME)
    190                 || values.containsKey(StructuredName.MIDDLE_NAME)
    191                 || values.containsKey(StructuredName.PHONETIC_FAMILY_NAME)
    192                 || values.containsKey(StructuredName.PHONETIC_GIVEN_NAME)
    193                 || values.containsKey(StructuredName.PHONETIC_MIDDLE_NAME)
    194                 || values.containsKey(StructuredName.PREFIX)
    195                 || values.containsKey(StructuredName.SUFFIX);
    196     }
    197 
    198     @Override
    199     public void appendSearchableData(IndexBuilder builder) {
    200         String name = builder.getString(StructuredName.DISPLAY_NAME);
    201         Integer fullNameStyle = builder.getInt(StructuredName.FULL_NAME_STYLE);
    202 
    203         mNameLookupBuilder.appendToSearchIndex(builder, name, fullNameStyle != null
    204                         ? mSplitter.getAdjustedFullNameStyle(fullNameStyle)
    205                         : FullNameStyle.UNDEFINED);
    206 
    207         String phoneticFamily = builder.getString(StructuredName.PHONETIC_FAMILY_NAME);
    208         String phoneticMiddle = builder.getString(StructuredName.PHONETIC_MIDDLE_NAME);
    209         String phoneticGiven = builder.getString(StructuredName.PHONETIC_GIVEN_NAME);
    210 
    211         // Phonetic name is often spelled without spaces
    212         if (!TextUtils.isEmpty(phoneticFamily) || !TextUtils.isEmpty(phoneticMiddle)
    213                 || !TextUtils.isEmpty(phoneticGiven)) {
    214             mSb.setLength(0);
    215             if (!TextUtils.isEmpty(phoneticFamily)) {
    216                 builder.appendName(phoneticFamily);
    217                 mSb.append(phoneticFamily);
    218             }
    219             if (!TextUtils.isEmpty(phoneticMiddle)) {
    220                 builder.appendName(phoneticMiddle);
    221                 mSb.append(phoneticMiddle);
    222             }
    223             if (!TextUtils.isEmpty(phoneticGiven)) {
    224                 builder.appendName(phoneticGiven);
    225                 mSb.append(phoneticGiven);
    226             }
    227             final String phoneticName = mSb.toString().trim();
    228             int phoneticNameStyle = builder.getInt(StructuredName.PHONETIC_NAME_STYLE);
    229             if (phoneticNameStyle == PhoneticNameStyle.UNDEFINED) {
    230                 phoneticNameStyle = mSplitter.guessPhoneticNameStyle(phoneticName);
    231             }
    232             builder.appendName(phoneticName);
    233             mNameLookupBuilder.appendNameShorthandLookup(builder, phoneticName,
    234                     phoneticNameStyle);
    235         }
    236     }
    237 }
    238