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