DataRowHandlerForStructuredName.java revision 92ddc5cdc4d89ee2c6e861ae7b3a3a913ffa0100
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 */
16package com.android.providers.contacts;
17
18import com.android.providers.contacts.SearchIndexManager.IndexBuilder;
19
20import android.content.ContentValues;
21import android.content.Context;
22import android.database.Cursor;
23import android.database.sqlite.SQLiteDatabase;
24import android.provider.ContactsContract.CommonDataKinds.StructuredName;
25import android.provider.ContactsContract.FullNameStyle;
26import android.text.TextUtils;
27
28/**
29 * Handler for email address data rows.
30 */
31public 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);
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.appendToken(phoneticFamily);
211                mSb.append(phoneticFamily);
212            }
213            if (!TextUtils.isEmpty(phoneticMiddle)) {
214                builder.appendToken(phoneticMiddle);
215                mSb.append(phoneticMiddle);
216            }
217            if (!TextUtils.isEmpty(phoneticGiven)) {
218                builder.appendToken(phoneticGiven);
219                mSb.append(phoneticGiven);
220            }
221            builder.appendToken(mSb.toString().trim());
222        }
223    }
224}
225