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.AbstractContactAggregator;
     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             AbstractContactAggregator 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         fixRawContactDisplayName(db, txContext, rawContactId);
     60         triggerAggregation(txContext, rawContactId);
     61         return dataId;
     62     }
     63 
     64     @Override
     65     public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
     66             Cursor c, boolean callerIsSyncAdapter, boolean callerIsMetadataSyncAdapter) {
     67         final long dataId = c.getLong(DataUpdateQuery._ID);
     68         final long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
     69 
     70         final ContentValues augmented = getAugmentedValues(db, dataId, values);
     71         if (augmented == null) {  // No change
     72             return false;
     73         }
     74 
     75         fixStructuredNameComponents(augmented, values);
     76 
     77         super.update(db, txContext, values, c, callerIsSyncAdapter, callerIsMetadataSyncAdapter);
     78         if (values.containsKey(StructuredName.DISPLAY_NAME)) {
     79             augmented.putAll(values);
     80             String name = augmented.getAsString(StructuredName.DISPLAY_NAME);
     81             mDbHelper.deleteNameLookup(dataId);
     82             Integer fullNameStyle = augmented.getAsInteger(StructuredName.FULL_NAME_STYLE);
     83             mNameLookupBuilder.insertNameLookup(rawContactId, dataId, name,
     84                     fullNameStyle != null
     85                             ? mSplitter.getAdjustedFullNameStyle(fullNameStyle)
     86                             : FullNameStyle.UNDEFINED);
     87         }
     88         fixRawContactDisplayName(db, txContext, rawContactId);
     89         triggerAggregation(txContext, rawContactId);
     90         return true;
     91     }
     92 
     93     @Override
     94     public int delete(SQLiteDatabase db, TransactionContext txContext, Cursor c) {
     95         long dataId = c.getLong(DataDeleteQuery._ID);
     96         long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
     97 
     98         int count = super.delete(db, txContext, c);
     99 
    100         mDbHelper.deleteNameLookup(dataId);
    101         fixRawContactDisplayName(db, txContext, rawContactId);
    102         triggerAggregation(txContext, rawContactId);
    103         return count;
    104     }
    105 
    106     /**
    107      * Specific list of structured fields.
    108      */
    109     private final String[] STRUCTURED_FIELDS = new String[] {
    110             StructuredName.PREFIX, StructuredName.GIVEN_NAME, StructuredName.MIDDLE_NAME,
    111             StructuredName.FAMILY_NAME, StructuredName.SUFFIX
    112     };
    113 
    114     /**
    115      * Parses the supplied display name, but only if the incoming values do
    116      * not already contain structured name parts. Also, if the display name
    117      * is not provided, generate one by concatenating first name and last
    118      * name.
    119      */
    120     public void fixStructuredNameComponents(ContentValues augmented, ContentValues update) {
    121         final String unstruct = update.getAsString(StructuredName.DISPLAY_NAME);
    122 
    123         final boolean touchedUnstruct = !TextUtils.isEmpty(unstruct);
    124         final boolean touchedStruct = !areAllEmpty(update, STRUCTURED_FIELDS);
    125 
    126         if (touchedUnstruct && !touchedStruct) {
    127             NameSplitter.Name name = new NameSplitter.Name();
    128             mSplitter.split(name, unstruct);
    129             name.toValues(update);
    130         } else if (!touchedUnstruct
    131                 && (touchedStruct || areAnySpecified(update, STRUCTURED_FIELDS))) {
    132             // We need to update the display name when any structured components
    133             // are specified, even when they are null, which is why we are checking
    134             // areAnySpecified.  The touchedStruct in the condition is an optimization:
    135             // if there are non-null values, we know for a fact that some values are present.
    136             NameSplitter.Name name = new NameSplitter.Name();
    137             name.fromValues(augmented);
    138             // As the name could be changed, let's guess the name style again.
    139             name.fullNameStyle = FullNameStyle.UNDEFINED;
    140             name.phoneticNameStyle = PhoneticNameStyle.UNDEFINED;
    141             mSplitter.guessNameStyle(name);
    142             int unadjustedFullNameStyle = name.fullNameStyle;
    143             name.fullNameStyle = mSplitter.getAdjustedFullNameStyle(name.fullNameStyle);
    144             final String joined = mSplitter.join(name, true, true);
    145             update.put(StructuredName.DISPLAY_NAME, joined);
    146 
    147             update.put(StructuredName.FULL_NAME_STYLE, unadjustedFullNameStyle);
    148             update.put(StructuredName.PHONETIC_NAME_STYLE, name.phoneticNameStyle);
    149         } else if (touchedUnstruct && touchedStruct){
    150             if (!update.containsKey(StructuredName.FULL_NAME_STYLE)) {
    151                 update.put(StructuredName.FULL_NAME_STYLE,
    152                         mSplitter.guessFullNameStyle(unstruct));
    153             }
    154             if (!update.containsKey(StructuredName.PHONETIC_NAME_STYLE)) {
    155                 NameSplitter.Name name = new NameSplitter.Name();
    156                 name.fromValues(update);
    157                 name.phoneticNameStyle = PhoneticNameStyle.UNDEFINED;
    158                 mSplitter.guessNameStyle(name);
    159                 update.put(StructuredName.PHONETIC_NAME_STYLE, name.phoneticNameStyle);
    160             }
    161         }
    162     }
    163 
    164     @Override
    165     public boolean hasSearchableData() {
    166         return true;
    167     }
    168 
    169     @Override
    170     public boolean containsSearchableColumns(ContentValues values) {
    171         return values.containsKey(StructuredName.FAMILY_NAME)
    172                 || values.containsKey(StructuredName.GIVEN_NAME)
    173                 || values.containsKey(StructuredName.MIDDLE_NAME)
    174                 || values.containsKey(StructuredName.PHONETIC_FAMILY_NAME)
    175                 || values.containsKey(StructuredName.PHONETIC_GIVEN_NAME)
    176                 || values.containsKey(StructuredName.PHONETIC_MIDDLE_NAME)
    177                 || values.containsKey(StructuredName.PREFIX)
    178                 || values.containsKey(StructuredName.SUFFIX);
    179     }
    180 
    181     @Override
    182     public void appendSearchableData(IndexBuilder builder) {
    183         String name = builder.getString(StructuredName.DISPLAY_NAME);
    184         Integer fullNameStyle = builder.getInt(StructuredName.FULL_NAME_STYLE);
    185 
    186         mNameLookupBuilder.appendToSearchIndex(builder, name, fullNameStyle != null
    187                         ? mSplitter.getAdjustedFullNameStyle(fullNameStyle)
    188                         : FullNameStyle.UNDEFINED);
    189 
    190         String phoneticFamily = builder.getString(StructuredName.PHONETIC_FAMILY_NAME);
    191         String phoneticMiddle = builder.getString(StructuredName.PHONETIC_MIDDLE_NAME);
    192         String phoneticGiven = builder.getString(StructuredName.PHONETIC_GIVEN_NAME);
    193 
    194         // Phonetic name is often spelled without spaces
    195         if (!TextUtils.isEmpty(phoneticFamily) || !TextUtils.isEmpty(phoneticMiddle)
    196                 || !TextUtils.isEmpty(phoneticGiven)) {
    197             mSb.setLength(0);
    198             if (!TextUtils.isEmpty(phoneticFamily)) {
    199                 builder.appendName(phoneticFamily);
    200                 mSb.append(phoneticFamily);
    201             }
    202             if (!TextUtils.isEmpty(phoneticMiddle)) {
    203                 builder.appendName(phoneticMiddle);
    204                 mSb.append(phoneticMiddle);
    205             }
    206             if (!TextUtils.isEmpty(phoneticGiven)) {
    207                 builder.appendName(phoneticGiven);
    208                 mSb.append(phoneticGiven);
    209             }
    210             final String phoneticName = mSb.toString().trim();
    211             int phoneticNameStyle = builder.getInt(StructuredName.PHONETIC_NAME_STYLE);
    212             if (phoneticNameStyle == PhoneticNameStyle.UNDEFINED) {
    213                 phoneticNameStyle = mSplitter.guessPhoneticNameStyle(phoneticName);
    214             }
    215             builder.appendName(phoneticName);
    216             mNameLookupBuilder.appendNameShorthandLookup(builder, phoneticName,
    217                     phoneticNameStyle);
    218         }
    219     }
    220 }
    221