12ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey/*
22ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Copyright (C) 2009 The Android Open Source Project
32ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey *
42ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
52ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * you may not use this file except in compliance with the License.
62ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * You may obtain a copy of the License at
72ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey *
82ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
92ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey *
102ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Unless required by applicable law or agreed to in writing, software
112ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
122ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * See the License for the specific language governing permissions and
142ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * limitations under the License.
152ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */
162ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
172ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeypackage com.android.contacts.model;
182ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
192ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeyimport android.content.ContentValues;
20802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkeyimport android.content.Context;
21802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkeyimport android.database.Cursor;
22c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.net.Uri;
23d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.os.Bundle;
24c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract;
257f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.BaseTypes;
26d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Email;
27d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Event;
28c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
29d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Im;
30d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Nickname;
3116c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Note;
3216c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Organization;
33d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Phone;
34a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintanaimport android.provider.ContactsContract.CommonDataKinds.Photo;
35d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Relation;
36d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.SipAddress;
37d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
38d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
39d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Website;
40c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract.Data;
41c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract.Intents;
42d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.Intents.Insert;
43c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
44d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.text.TextUtils;
456f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkeyimport android.util.Log;
467b1255eab0729fd43a8aae9933bd79afa50a53a9Daniel Lehmannimport android.util.SparseArray;
4707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport android.util.SparseIntArray;
482ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
49e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.ContactsUtils;
50e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.editor.EventFieldEditorView;
51e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.editor.PhoneticNameEditorView;
52851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.RawContactDelta.ValuesDelta;
53851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.account.AccountType;
54851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.account.AccountType.EditField;
55851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.account.AccountType.EditType;
56851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.account.AccountType.EventEditType;
57851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.account.GoogleAccountType;
58851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.dataitem.DataKind;
59851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.dataitem.StructuredNameDataItem;
60e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.util.DateUtils;
61e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.util.NameConverter;
62e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Cheng
63d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.text.ParsePosition;
6407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport java.util.ArrayList;
65d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Arrays;
66d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Calendar;
67d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Date;
68d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.HashSet;
6907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport java.util.Iterator;
702ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeyimport java.util.List;
71d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Locale;
72d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Set;
732ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
742ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey/**
75851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu * Helper methods for modifying an {@link RawContactDelta}, such as inserting
764597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov * new rows, or enforcing {@link AccountType}.
772ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */
78851222a96b5d68602fb361ea3527101e893f67e3Maurice Chupublic class RawContactModifier {
79851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    private static final String TAG = RawContactModifier.class.getSimpleName();
806f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
81a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan    /** Set to true in order to view logs on entity operations */
82a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan    private static final boolean DEBUG = false;
83a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan
842ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    /**
85851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu     * For the given {@link RawContactDelta}, determine if the given
862ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * {@link DataKind} could be inserted under specific
874597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov     * {@link AccountType}.
882ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     */
89851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    public static boolean canInsert(RawContactDelta state, DataKind kind) {
9007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Insert possible when have valid types and under overall maximum
91ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey        final int visibleCount = state.getMimeEntriesCount(kind.mimeType, true);
9207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final boolean validTypes = hasValidTypes(state, kind);
9307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final boolean validOverall = (kind.typeOverallMax == -1)
94ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey                || (visibleCount < kind.typeOverallMax);
9507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        return (validTypes && validOverall);
9607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
9707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
98851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    public static boolean hasValidTypes(RawContactDelta state, DataKind kind) {
99851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu        if (RawContactModifier.hasEditTypes(kind)) {
100d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            return (getValidTypes(state, kind).size() > 0);
101d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        } else {
102d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            return true;
103d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        }
1042ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    }
1052ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
1062ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    /**
107aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey     * Ensure that at least one of the given {@link DataKind} exists in the
108851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu     * given {@link RawContactDelta} state, and try creating one if none exist.
1096fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     * @return The child (either newly created or the first existing one), or null if the
1106fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro     *     account doesn't support this {@link DataKind}.
111aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey     */
1126fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro    public static ValuesDelta ensureKindExists(
113851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu            RawContactDelta state, AccountType accountType, String mimeType) {
11469f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov        final DataKind kind = accountType.getKindForMimetype(mimeType);
115ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey        final boolean hasChild = state.getMimeEntriesCount(mimeType, true) > 0;
116aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
1176fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        if (kind != null) {
1186fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            if (hasChild) {
1196fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                // Return the first entry.
1206fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                return state.getMimeEntries(mimeType).get(0);
1216fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro            } else {
1226fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                // Create child when none exists and valid kind
1236fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                final ValuesDelta child = insertChild(state, kind);
1246fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                if (kind.mimeType.equals(Photo.CONTENT_ITEM_TYPE)) {
1256fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                    child.setFromTemplate(true);
1266fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                }
1276fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro                return child;
128e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi            }
129aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        }
1306fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro        return null;
131aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    }
132aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
133aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    /**
134851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu     * For the given {@link RawContactDelta} and {@link DataKind}, return the
1352ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * list possible {@link EditType} options available based on
1364597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov     * {@link AccountType}.
1372ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     */
138851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    public static ArrayList<EditType> getValidTypes(RawContactDelta state, DataKind kind) {
13907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        return getValidTypes(state, kind, null, true, null);
14007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
14107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
14207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    /**
143851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu     * For the given {@link RawContactDelta} and {@link DataKind}, return the
14407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * list possible {@link EditType} options available based on
1454597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov     * {@link AccountType}.
14607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *
14707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * @param forceInclude Always include this {@link EditType} in the returned
14807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *            list, even when an otherwise-invalid choice. This is useful
14907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *            when showing a dialog that includes the current type.
15007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     */
151851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    public static ArrayList<EditType> getValidTypes(RawContactDelta state, DataKind kind,
1522ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey            EditType forceInclude) {
15307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        return getValidTypes(state, kind, forceInclude, true, null);
15407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
15507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
15607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    /**
157851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu     * For the given {@link RawContactDelta} and {@link DataKind}, return the
15807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * list possible {@link EditType} options available based on
1594597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov     * {@link AccountType}.
16007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *
16107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * @param forceInclude Always include this {@link EditType} in the returned
16207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *            list, even when an otherwise-invalid choice. This is useful
16307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *            when showing a dialog that includes the current type.
16407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * @param includeSecondary If true, include any valid types marked as
16507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *            {@link EditType#secondary}.
16607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * @param typeCount When provided, will be used for the frequency count of
16707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *            each {@link EditType}, otherwise built using
168851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu     *            {@link #getTypeFrequencies(RawContactDelta, DataKind)}.
16907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     */
170851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    private static ArrayList<EditType> getValidTypes(RawContactDelta state, DataKind kind,
17107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            EditType forceInclude, boolean includeSecondary, SparseIntArray typeCount) {
172da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro        final ArrayList<EditType> validTypes = new ArrayList<EditType>();
17307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
17407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Bail early if no types provided
17507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (!hasEditTypes(kind)) return validTypes;
17607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
17707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (typeCount == null) {
17807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            // Build frequency counts if not provided
17907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            typeCount = getTypeFrequencies(state, kind);
18007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        }
18107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
18207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Build list of valid types
18307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final int overallCount = typeCount.get(FREQUENCY_TOTAL);
18407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        for (EditType type : kind.typeList) {
185d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            final boolean validOverall = (kind.typeOverallMax == -1 ? true
186d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                    : overallCount < kind.typeOverallMax);
187d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            final boolean validSpecific = (type.specificMax == -1 ? true : typeCount
188d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                    .get(type.rawValue) < type.specificMax);
18907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            final boolean validSecondary = (includeSecondary ? true : !type.secondary);
19007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            final boolean forcedInclude = type.equals(forceInclude);
191d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            if (forcedInclude || (validOverall && validSpecific && validSecondary)) {
19207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                // Type is valid when no limit, under limit, or forced include
19307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                validTypes.add(type);
19407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            }
19507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        }
19607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
19707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        return validTypes;
19807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
19907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
20007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    private static final int FREQUENCY_TOTAL = Integer.MIN_VALUE;
20107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
20207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    /**
20307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * Count up the frequency that each {@link EditType} appears in the given
204851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu     * {@link RawContactDelta}. The returned {@link SparseIntArray} maps from
20507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * {@link EditType#rawValue} to counts, with the total overall count stored
20607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * as {@link #FREQUENCY_TOTAL}.
20707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     */
208851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    private static SparseIntArray getTypeFrequencies(RawContactDelta state, DataKind kind) {
20907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final SparseIntArray typeCount = new SparseIntArray();
21007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
21107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Find all entries for this kind, bailing early if none found
2128d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey        final List<ValuesDelta> mimeEntries = state.getMimeEntries(kind.mimeType);
21307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (mimeEntries == null) return typeCount;
21407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
21507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        int totalCount = 0;
2168d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey        for (ValuesDelta entry : mimeEntries) {
21707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            // Only count visible entries
21807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            if (!entry.isVisible()) continue;
21907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            totalCount++;
22007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
22107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            final EditType type = getCurrentType(entry, kind);
22207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            if (type != null) {
22307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                final int count = typeCount.get(type.rawValue);
22407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                typeCount.put(type.rawValue, count + 1);
22507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            }
22607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        }
22707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        typeCount.put(FREQUENCY_TOTAL, totalCount);
22807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        return typeCount;
2292ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    }
2302ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
2312ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    /**
2322ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * Check if the given {@link DataKind} has multiple types that should be
2332ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * displayed for users to pick.
2342ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     */
2352ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    public static boolean hasEditTypes(DataKind kind) {
2362ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey        return kind.typeList != null && kind.typeList.size() > 0;
2372ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    }
2382ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
2392ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    /**
2402ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * Find the {@link EditType} that describes the given
2418d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey     * {@link ValuesDelta} row, assuming the given {@link DataKind} dictates
2422ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * the possible types.
2432ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     */
2448d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey    public static EditType getCurrentType(ValuesDelta entry, DataKind kind) {
24507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final Long rawValue = entry.getAsLong(kind.typeColumn);
24607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (rawValue == null) return null;
24707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        return getType(kind, rawValue.intValue());
24807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
24907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
25007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    /**
25111d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar     * Find the {@link EditType} that describes the given {@link ContentValues} row,
25211d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar     * assuming the given {@link DataKind} dictates the possible types.
25311d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar     */
25411d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar    public static EditType getCurrentType(ContentValues entry, DataKind kind) {
25511d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar        if (kind.typeColumn == null) return null;
25649d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey        final Integer rawValue = entry.getAsInteger(kind.typeColumn);
25749d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey        if (rawValue == null) return null;
25811d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar        return getType(kind, rawValue);
25911d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar    }
26011d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar
26111d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar    /**
262802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey     * Find the {@link EditType} that describes the given {@link Cursor} row,
263802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey     * assuming the given {@link DataKind} dictates the possible types.
264802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey     */
265802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey    public static EditType getCurrentType(Cursor cursor, DataKind kind) {
266ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey        if (kind.typeColumn == null) return null;
267802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey        final int index = cursor.getColumnIndex(kind.typeColumn);
26849d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey        if (index == -1) return null;
269802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey        final int rawValue = cursor.getInt(index);
270802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey        return getType(kind, rawValue);
271802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey    }
272802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey
273802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey    /**
27407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * Find the {@link EditType} with the given {@link EditType#rawValue}.
27507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     */
27607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    public static EditType getType(DataKind kind, int rawValue) {
2772ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey        for (EditType type : kind.typeList) {
2782ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey            if (type.rawValue == rawValue) {
2792ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey                return type;
2802ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey            }
2812ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey        }
2822ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey        return null;
2832ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    }
2842ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
2852ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    /**
28649d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey     * Return the precedence for the the given {@link EditType#rawValue}, where
28749d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey     * lower numbers are higher precedence.
28849d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey     */
28949d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey    public static int getTypePrecedence(DataKind kind, int rawValue) {
29049d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey        for (int i = 0; i < kind.typeList.size(); i++) {
29149d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey            final EditType type = kind.typeList.get(i);
29249d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey            if (type.rawValue == rawValue) {
29349d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey                return i;
29449d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey            }
29549d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey        }
29649d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey        return Integer.MAX_VALUE;
29749d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey    }
29849d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey
29949d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey    /**
30007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * Find the best {@link EditType} for a potential insert. The "best" is the
30107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * first primary type that doesn't already exist. When all valid types
30207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * exist, we pick the last valid option.
30307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     */
304851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    public static EditType getBestValidType(RawContactDelta state, DataKind kind,
305d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            boolean includeSecondary, int exactValue) {
30607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Shortcut when no types
30707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (kind.typeColumn == null) return null;
30807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
30907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Find type counts and valid primary types, bail if none
31007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final SparseIntArray typeCount = getTypeFrequencies(state, kind);
31107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final ArrayList<EditType> validTypes = getValidTypes(state, kind, null, includeSecondary,
31207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                typeCount);
31307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (validTypes.size() == 0) return null;
31407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
31507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Keep track of the last valid type
31607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final EditType lastType = validTypes.get(validTypes.size() - 1);
31707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
31807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Remove any types that already exist
31907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        Iterator<EditType> iterator = validTypes.iterator();
32007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        while (iterator.hasNext()) {
32107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            final EditType type = iterator.next();
32207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            final int count = typeCount.get(type.rawValue);
32307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
324e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            if (exactValue == type.rawValue) {
325d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                // Found exact value match
326d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                return type;
327d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            }
328d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
32907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            if (count > 0) {
33007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                // Type already appears, so don't consider
33107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                iterator.remove();
33207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            }
33307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        }
33407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
33507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Use the best remaining, otherwise the last valid
33607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (validTypes.size() > 0) {
33707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            return validTypes.get(0);
33807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        } else {
33907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            return lastType;
34007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        }
34107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
34207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
34307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    /**
3442ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * Insert a new child of kind {@link DataKind} into the given
345851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu     * {@link RawContactDelta}. Tries using the best {@link EditType} found using
346851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu     * {@link #getBestValidType(RawContactDelta, DataKind, boolean, int)}.
3472ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     */
348851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    public static ValuesDelta insertChild(RawContactDelta state, DataKind kind) {
34907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // First try finding a valid primary
350d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        EditType bestType = getBestValidType(state, kind, false, Integer.MIN_VALUE);
35107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (bestType == null) {
35207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            // No valid primary found, so expand search to secondary
353d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            bestType = getBestValidType(state, kind, true, Integer.MIN_VALUE);
35407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        }
355d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        return insertChild(state, kind, bestType);
35607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
35707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
35807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    /**
35907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * Insert a new child of kind {@link DataKind} into the given
360851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu     * {@link RawContactDelta}, marked with the given {@link EditType}.
36107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     */
362851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    public static ValuesDelta insertChild(RawContactDelta state, DataKind kind, EditType type) {
363d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        // Bail early if invalid kind
364d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        if (kind == null) return null;
3652ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey        final ContentValues after = new ContentValues();
3662ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
367e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey        // Our parent CONTACT_ID is provided later
3682ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey        after.put(Data.MIMETYPE, kind.mimeType);
369e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey
370e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey        // Fill-in with any requested default values
371e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey        if (kind.defaultValues != null) {
372e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey            after.putAll(kind.defaultValues);
373e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey        }
374e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey
37507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (kind.typeColumn != null && type != null) {
37607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            // Set type, if provided
37707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            after.put(kind.typeColumn, type.rawValue);
378e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey        }
3792ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
380d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        final ValuesDelta child = ValuesDelta.fromAfter(after);
381a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov        state.addEntry(child);
382d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        return child;
3832ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    }
3842ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
385d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey    /**
386851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu     * Processing to trim any empty {@link ValuesDelta} and {@link RawContactDelta}
387851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu     * from the given {@link RawContactDeltaList}, assuming the given {@link AccountTypeManager}
3887f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey     * dictates the structure for various fields. This method ignores rows not
3894597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov     * described by the {@link AccountType}.
3907f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey     */
391851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    public static void trimEmpty(RawContactDeltaList set, AccountTypeManager accountTypes) {
392851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu        for (RawContactDelta state : set) {
3932b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            ValuesDelta values = state.getValues();
3942b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
3952b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            final String dataSet = values.getAsString(RawContacts.DATA_SET);
3962b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            final AccountType type = accountTypes.getAccountType(accountType, dataSet);
397a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov            trimEmpty(state, type);
3987f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        }
3997f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey    }
4007f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey
401851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    public static boolean hasChanges(RawContactDeltaList set, AccountTypeManager accountTypes) {
402a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov        if (set.isMarkedForSplitting() || set.isMarkedForJoining()) {
403a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov            return true;
404a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov        }
405a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov
406851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu        for (RawContactDelta state : set) {
4072b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            ValuesDelta values = state.getValues();
4082b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
4092b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            final String dataSet = values.getAsString(RawContacts.DATA_SET);
4102b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            final AccountType type = accountTypes.getAccountType(accountType, dataSet);
411a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov            if (hasChanges(state, type)) {
412a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov                return true;
413a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov            }
414a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov        }
415a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov        return false;
416a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov    }
417a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov
4187f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey    /**
4196f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     * Processing to trim any empty {@link ValuesDelta} rows from the given
420851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu     * {@link RawContactDelta}, assuming the given {@link AccountType} dictates
4216f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     * the structure for various fields. This method ignores rows not described
4224597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov     * by the {@link AccountType}.
4236f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     */
424851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    public static void trimEmpty(RawContactDelta state, AccountType accountType) {
4257f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        boolean hasValues = false;
4267f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey
4276f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey        // Walk through entries for each well-known kind
42869f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov        for (DataKind kind : accountType.getSortedDataKinds()) {
4296f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            final String mimeType = kind.mimeType;
4306f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            final ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType);
4316f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            if (entries == null) continue;
4326f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
4336f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            for (ValuesDelta entry : entries) {
4347f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                // Skip any values that haven't been touched
4356f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey                final boolean touched = entry.isInsert() || entry.isUpdate();
4367f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                if (!touched) {
4377f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                    hasValues = true;
4387f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                    continue;
4397f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                }
4407f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey
441a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana                // Test and remove this row if empty and it isn't a photo from google
44269f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov                final boolean isGoogleAccount = TextUtils.equals(GoogleAccountType.ACCOUNT_TYPE,
443a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana                        state.getValues().getAsString(RawContacts.ACCOUNT_TYPE));
444a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana                final boolean isPhoto = TextUtils.equals(Photo.CONTENT_ITEM_TYPE, kind.mimeType);
44569f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov                final boolean isGooglePhoto = isPhoto && isGoogleAccount;
446e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi
447851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu                if (RawContactModifier.isEmpty(entry, kind) && !isGooglePhoto) {
448a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan                    if (DEBUG) {
449a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan                        Log.v(TAG, "Trimming: " + entry.toString());
450a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan                    }
4516f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey                    entry.markDeleted();
452d2fdb9095e0009408b89e24048154ea7a8a14413Megha Joshi                } else if (!entry.isFromTemplate()) {
4537f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                    hasValues = true;
4546f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey                }
4556f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            }
4566f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey        }
4577f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        if (!hasValues) {
4587f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey            // Trim overall entity if no children exist
4597f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey            state.markDeleted();
4607f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        }
4616f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    }
4626f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
463851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    private static boolean hasChanges(RawContactDelta state, AccountType accountType) {
464a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov        for (DataKind kind : accountType.getSortedDataKinds()) {
465a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov            final String mimeType = kind.mimeType;
466a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov            final ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType);
467a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov            if (entries == null) continue;
468a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov
469a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov            for (ValuesDelta entry : entries) {
470de0898f0337d3c6ea818237b0a94acf1f15b6299Daniel Lehmann                // An empty Insert must be ignored, because it won't save anything (an example
471de0898f0337d3c6ea818237b0a94acf1f15b6299Daniel Lehmann                // is an empty name that stays empty)
472de0898f0337d3c6ea818237b0a94acf1f15b6299Daniel Lehmann                final boolean isRealInsert = entry.isInsert() && !isEmpty(entry, kind);
473de0898f0337d3c6ea818237b0a94acf1f15b6299Daniel Lehmann                if (isRealInsert || entry.isUpdate() || entry.isDelete()) {
474a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov                    return true;
475a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov                }
476a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov            }
477a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov        }
478a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov        return false;
479a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov    }
480a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov
4816f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    /**
4826f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     * Test if the given {@link ValuesDelta} would be considered "empty" in
4836f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     * terms of {@link DataKind#fieldList}.
4846f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     */
4856f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    public static boolean isEmpty(ValuesDelta values, DataKind kind) {
486f3cdeca5e0b3a7f9411b6babad5de2925623372aDmitri Plotnikov        if (Photo.CONTENT_ITEM_TYPE.equals(kind.mimeType)) {
487c863b0171f2a54a0ee71b34b33034aeea2464ccaDmitri Plotnikov            return values.isInsert() && values.getAsByteArray(Photo.PHOTO) == null;
488f3cdeca5e0b3a7f9411b6babad5de2925623372aDmitri Plotnikov        }
489f3cdeca5e0b3a7f9411b6babad5de2925623372aDmitri Plotnikov
490dbeb965d19a3c03e78cd54d9fbe7337b4fbb06f2Jeff Sharkey        // No defined fields mean this row is always empty
491dbeb965d19a3c03e78cd54d9fbe7337b4fbb06f2Jeff Sharkey        if (kind.fieldList == null) return true;
492dbeb965d19a3c03e78cd54d9fbe7337b4fbb06f2Jeff Sharkey
4936f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey        for (EditField field : kind.fieldList) {
4946f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            // If any field has values, we're not empty
4956f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            final String value = values.getAsString(field.column);
496e31dac84479a4c7b356edfc062a447cdfb5efc69Jeff Sharkey            if (ContactsUtils.isGraphic(value)) {
497c863b0171f2a54a0ee71b34b33034aeea2464ccaDmitri Plotnikov                return false;
4986f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            }
4996f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey        }
5006f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
501c863b0171f2a54a0ee71b34b33034aeea2464ccaDmitri Plotnikov        return true;
5026f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    }
5036f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
5046f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    /**
505916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     * Compares corresponding fields in values1 and values2. Only the fields
506916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     * declared by the DataKind are taken into consideration.
507916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     */
508916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    protected static boolean areEqual(ValuesDelta values1, ContentValues values2, DataKind kind) {
509916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        if (kind.fieldList == null) return false;
510916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
511916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        for (EditField field : kind.fieldList) {
512916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            final String value1 = values1.getAsString(field.column);
513916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            final String value2 = values2.getAsString(field.column);
514916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            if (!TextUtils.equals(value1, value2)) {
515916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                return false;
516916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            }
517916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        }
518916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
519916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        return true;
520916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    }
521916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
522916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    /**
523851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu     * Parse the given {@link Bundle} into the given {@link RawContactDelta} state,
524d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * assuming the extras defined through {@link Intents}.
525d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     */
526851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    public static void parseExtras(Context context, AccountType accountType, RawContactDelta state,
527aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey            Bundle extras) {
528d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey        if (extras == null || extras.size() == 0) {
529d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey            // Bail early if no useful data
530d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey            return;
531d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey        }
532d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey
533c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        parseStructuredNameExtra(context, accountType, state, extras);
534c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        parseStructuredPostalExtra(accountType, state, extras);
535d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
536d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        {
537d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            // Phone
53869f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov            final DataKind kind = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
539d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            parseExtras(state, kind, extras, Insert.PHONE_TYPE, Insert.PHONE, Phone.NUMBER);
540d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            parseExtras(state, kind, extras, Insert.SECONDARY_PHONE_TYPE, Insert.SECONDARY_PHONE,
541d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                    Phone.NUMBER);
542d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            parseExtras(state, kind, extras, Insert.TERTIARY_PHONE_TYPE, Insert.TERTIARY_PHONE,
543d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                    Phone.NUMBER);
544d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        }
545d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
546d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        {
547d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            // Email
54869f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov            final DataKind kind = accountType.getKindForMimetype(Email.CONTENT_ITEM_TYPE);
549d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey            parseExtras(state, kind, extras, Insert.EMAIL_TYPE, Insert.EMAIL, Email.DATA);
550d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            parseExtras(state, kind, extras, Insert.SECONDARY_EMAIL_TYPE, Insert.SECONDARY_EMAIL,
551d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                    Email.DATA);
552d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            parseExtras(state, kind, extras, Insert.TERTIARY_EMAIL_TYPE, Insert.TERTIARY_EMAIL,
553d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                    Email.DATA);
554d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        }
555d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
556d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        {
557d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            // Im
55869f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov            final DataKind kind = accountType.getKindForMimetype(Im.CONTENT_ITEM_TYPE);
559e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            fixupLegacyImType(extras);
560d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            parseExtras(state, kind, extras, Insert.IM_PROTOCOL, Insert.IM_HANDLE, Im.DATA);
561d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        }
56216c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey
56316c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey        // Organization
56416c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey        final boolean hasOrg = extras.containsKey(Insert.COMPANY)
56516c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey                || extras.containsKey(Insert.JOB_TITLE);
56669f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov        final DataKind kindOrg = accountType.getKindForMimetype(Organization.CONTENT_ITEM_TYPE);
567851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu        if (hasOrg && RawContactModifier.canInsert(state, kindOrg)) {
568851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu            final ValuesDelta child = RawContactModifier.insertChild(state, kindOrg);
56916c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey
57016c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            final String company = extras.getString(Insert.COMPANY);
57116c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            if (ContactsUtils.isGraphic(company)) {
57216c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey                child.put(Organization.COMPANY, company);
57316c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            }
57416c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey
57516c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            final String title = extras.getString(Insert.JOB_TITLE);
57616c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            if (ContactsUtils.isGraphic(title)) {
57716c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey                child.put(Organization.TITLE, title);
57816c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            }
57916c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey        }
58016c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey
58116c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey        // Notes
58216c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey        final boolean hasNotes = extras.containsKey(Insert.NOTES);
58369f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov        final DataKind kindNotes = accountType.getKindForMimetype(Note.CONTENT_ITEM_TYPE);
584851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu        if (hasNotes && RawContactModifier.canInsert(state, kindNotes)) {
585851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu            final ValuesDelta child = RawContactModifier.insertChild(state, kindNotes);
58616c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey
58716c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            final String notes = extras.getString(Insert.NOTES);
58816c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            if (ContactsUtils.isGraphic(notes)) {
58916c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey                child.put(Note.NOTE, notes);
59016c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            }
59116c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey        }
5926a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov
5936a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov        // Arbitrary additional data
594916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        ArrayList<ContentValues> values = extras.getParcelableArrayList(Insert.DATA);
595916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        if (values != null) {
59669f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov            parseValues(state, accountType, values);
597916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        }
598916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    }
599916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
600c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov    private static void parseStructuredNameExtra(
601851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu            Context context, AccountType accountType, RawContactDelta state, Bundle extras) {
602c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        // StructuredName
603851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu        RawContactModifier.ensureKindExists(state, accountType, StructuredName.CONTENT_ITEM_TYPE);
604c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        final ValuesDelta child = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
605c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov
606c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        final String name = extras.getString(Insert.NAME);
607c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        if (ContactsUtils.isGraphic(name)) {
608c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            final DataKind kind = accountType.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE);
609c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            boolean supportsDisplayName = false;
610c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            if (kind.fieldList != null) {
611c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                for (EditField field : kind.fieldList) {
612c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                    if (StructuredName.DISPLAY_NAME.equals(field.column)) {
613c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        supportsDisplayName = true;
614c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        break;
615c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                    }
616c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                }
617c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            }
618c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov
619c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            if (supportsDisplayName) {
620c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                child.put(StructuredName.DISPLAY_NAME, name);
621c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            } else {
622c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                Uri uri = ContactsContract.AUTHORITY_URI.buildUpon()
623c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        .appendPath("complete_name")
624c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        .appendQueryParameter(StructuredName.DISPLAY_NAME, name)
625c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        .build();
626c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                Cursor cursor = context.getContentResolver().query(uri,
627c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        new String[]{
628c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                                StructuredName.PREFIX,
629c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                                StructuredName.GIVEN_NAME,
630c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                                StructuredName.MIDDLE_NAME,
631c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                                StructuredName.FAMILY_NAME,
632c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                                StructuredName.SUFFIX,
633c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        }, null, null, null);
634c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov
635c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                try {
636c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                    if (cursor.moveToFirst()) {
637c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        child.put(StructuredName.PREFIX, cursor.getString(0));
638c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        child.put(StructuredName.GIVEN_NAME, cursor.getString(1));
639c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        child.put(StructuredName.MIDDLE_NAME, cursor.getString(2));
640c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        child.put(StructuredName.FAMILY_NAME, cursor.getString(3));
641c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        child.put(StructuredName.SUFFIX, cursor.getString(4));
642c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                    }
643c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                } finally {
644c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                    cursor.close();
645c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                }
646c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            }
647c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        }
648c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov
649c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        final String phoneticName = extras.getString(Insert.PHONETIC_NAME);
650c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        if (ContactsUtils.isGraphic(phoneticName)) {
651c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            child.put(StructuredName.PHONETIC_GIVEN_NAME, phoneticName);
652c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        }
653c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov    }
654c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov
655c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov    private static void parseStructuredPostalExtra(
656851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu            AccountType accountType, RawContactDelta state, Bundle extras) {
657c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        // StructuredPostal
658c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        final DataKind kind = accountType.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE);
659c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        final ValuesDelta child = parseExtras(state, kind, extras, Insert.POSTAL_TYPE,
660c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                Insert.POSTAL, StructuredPostal.FORMATTED_ADDRESS);
661c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        String address = child == null ? null
662c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                : child.getAsString(StructuredPostal.FORMATTED_ADDRESS);
663c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        if (!TextUtils.isEmpty(address)) {
664c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            boolean supportsFormatted = false;
665c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            if (kind.fieldList != null) {
666c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                for (EditField field : kind.fieldList) {
667c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                    if (StructuredPostal.FORMATTED_ADDRESS.equals(field.column)) {
668c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        supportsFormatted = true;
669c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        break;
670c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                    }
671c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                }
672c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            }
673c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov
674c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            if (!supportsFormatted) {
675c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                child.put(StructuredPostal.STREET, address);
676c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                child.putNull(StructuredPostal.FORMATTED_ADDRESS);
677c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            }
678c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        }
679c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov    }
680c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov
681916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    private static void parseValues(
682851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu            RawContactDelta state, AccountType accountType,
683851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu            ArrayList<ContentValues> dataValueList) {
684916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        for (ContentValues values : dataValueList) {
685916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            String mimeType = values.getAsString(Data.MIMETYPE);
686916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            if (TextUtils.isEmpty(mimeType)) {
687916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                Log.e(TAG, "Mimetype is required. Ignoring: " + values);
688916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                continue;
689916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            }
690916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
691916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            // Won't override the contact name
692916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
693916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                continue;
694916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            }
695916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
69669f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov            DataKind kind = accountType.getKindForMimetype(mimeType);
697916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            if (kind == null) {
6982b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro                Log.e(TAG, "Mimetype not supported for account type "
6992b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro                        + accountType.getAccountTypeAndDataSet() + ". Ignoring: " + values);
700916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                continue;
701916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            }
702916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
703916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            ValuesDelta entry = ValuesDelta.fromAfter(values);
704916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            if (isEmpty(entry, kind)) {
705916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                continue;
706916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            }
707916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
708916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType);
709916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
71050a27b70581b0191995969c63edd1f6a3db3d1b7Makoto Onuki            if ((kind.typeOverallMax != 1) || GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
711916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                // Check for duplicates
712916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                boolean addEntry = true;
713916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                int count = 0;
714916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                if (entries != null && entries.size() > 0) {
715916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    for (ValuesDelta delta : entries) {
716916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                        if (!delta.isDelete()) {
717916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            if (areEqual(delta, values, kind)) {
718916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                                addEntry = false;
719916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                                break;
720916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            }
721916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            count++;
722916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                        }
723916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    }
724916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                }
725916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
726916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                if (kind.typeOverallMax != -1 && count >= kind.typeOverallMax) {
727916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    Log.e(TAG, "Mimetype allows at most " + kind.typeOverallMax
728916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            + " entries. Ignoring: " + values);
729916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    addEntry = false;
730916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                }
731916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
732916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                if (addEntry) {
733916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    addEntry = adjustType(entry, entries, kind);
734916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                }
735916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
736916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                if (addEntry) {
737916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    state.addEntry(entry);
738916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                }
739916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            } else {
740916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                // Non-list entries should not be overridden
741916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                boolean addEntry = true;
742916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                if (entries != null && entries.size() > 0) {
743916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    for (ValuesDelta delta : entries) {
744916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                        if (!delta.isDelete() && !isEmpty(delta, kind)) {
745916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            addEntry = false;
746916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            break;
747916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                        }
748916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    }
749916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    if (addEntry) {
750916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                        for (ValuesDelta delta : entries) {
751916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            delta.markDeleted();
752916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                        }
753916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    }
754916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                }
755916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
756916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                if (addEntry) {
757916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    addEntry = adjustType(entry, entries, kind);
758916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                }
759916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
760916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                if (addEntry) {
761916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    state.addEntry(entry);
762916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType)){
763916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    // Note is most likely to contain large amounts of text
764916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    // that we don't want to drop on the ground.
765916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    for (ValuesDelta delta : entries) {
766916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                        if (!isEmpty(delta, kind)) {
767916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            delta.put(Note.NOTE, delta.getAsString(Note.NOTE) + "\n"
768916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                                    + values.getAsString(Note.NOTE));
769916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            break;
770916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                        }
771916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    }
772916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                } else {
773916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    Log.e(TAG, "Will not override mimetype " + mimeType + ". Ignoring: "
774916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            + values);
775916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                }
776916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            }
777916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        }
778916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    }
779916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
780916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    /**
781916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     * Checks if the data kind allows addition of another entry (e.g. Exchange only
782916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     * supports two "work" phone numbers).  If not, tries to switch to one of the
783916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     * unused types.  If successful, returns true.
784916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     */
785916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    private static boolean adjustType(
786916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            ValuesDelta entry, ArrayList<ValuesDelta> entries, DataKind kind) {
787916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        if (kind.typeColumn == null || kind.typeList == null || kind.typeList.size() == 0) {
788916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            return true;
789916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        }
790916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
791916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        Integer typeInteger = entry.getAsInteger(kind.typeColumn);
792916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        int type = typeInteger != null ? typeInteger : kind.typeList.get(0).rawValue;
793916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
794916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        if (isTypeAllowed(type, entries, kind)) {
795916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            entry.put(kind.typeColumn, type);
796916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            return true;
797916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        }
798916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
799916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        // Specified type is not allowed - choose the first available type that is allowed
800916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        int size = kind.typeList.size();
801916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        for (int i = 0; i < size; i++) {
802916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            EditType editType = kind.typeList.get(i);
803916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            if (isTypeAllowed(editType.rawValue, entries, kind)) {
804916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                entry.put(kind.typeColumn, editType.rawValue);
805916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                return true;
806916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            }
8076a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov        }
808916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
809916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        return false;
810916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    }
811916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
812916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    /**
813916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     * Checks if a new entry of the specified type can be added to the raw
814916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     * contact. For example, Exchange only supports two "work" phone numbers, so
815916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     * addition of a third would not be allowed.
816916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     */
817916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    private static boolean isTypeAllowed(int type, ArrayList<ValuesDelta> entries, DataKind kind) {
818916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        int max = 0;
819916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        int size = kind.typeList.size();
820916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        for (int i = 0; i < size; i++) {
821916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            EditType editType = kind.typeList.get(i);
822916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            if (editType.rawValue == type) {
823916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                max = editType.specificMax;
824916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                break;
825916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            }
826916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        }
827916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
828916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        if (max == 0) {
829916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            // This type is not allowed at all
830916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            return false;
831916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        }
832916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
833916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        if (max == -1) {
834916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            // Unlimited instances of this type are allowed
835916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            return true;
836916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        }
837916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
838916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        return getEntryCountByType(entries, kind.typeColumn, type) < max;
8396a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov    }
8406a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov
841916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    /**
842916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     * Counts occurrences of the specified type in the supplied entry list.
8439629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng     *
8449629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng     * @return The count of occurrences of the type in the entry list. 0 if entries is
8459629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng     * {@literal null}
846916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     */
8479629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng    private static int getEntryCountByType(ArrayList<ValuesDelta> entries, String typeColumn,
8489629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng            int type) {
849916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        int count = 0;
8509629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng        if (entries != null) {
8519629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng            for (ValuesDelta entry : entries) {
8529629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng                Integer typeInteger = entry.getAsInteger(typeColumn);
8539629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng                if (typeInteger != null && typeInteger == type) {
8549629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng                    count++;
8559629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng                }
856916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            }
8576a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov        }
858916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        return count;
859d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey    }
860d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
861d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey    /**
862e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey     * Attempt to parse legacy {@link Insert#IM_PROTOCOL} values, replacing them
863e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey     * with updated values.
864e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey     */
865c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov    @SuppressWarnings("deprecation")
866e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey    private static void fixupLegacyImType(Bundle bundle) {
867e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey        final String encodedString = bundle.getString(Insert.IM_PROTOCOL);
868e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey        if (encodedString == null) return;
869e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey
870e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey        try {
871e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            final Object protocol = android.provider.Contacts.ContactMethods
872e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey                    .decodeImProtocol(encodedString);
873e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            if (protocol instanceof Integer) {
874e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey                bundle.putInt(Insert.IM_PROTOCOL, (Integer)protocol);
875e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            } else {
876e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey                bundle.putString(Insert.IM_PROTOCOL, (String)protocol);
877e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            }
878e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey        } catch (IllegalArgumentException e) {
879e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            // Ignore exception when legacy parser fails
880e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey        }
881e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey    }
882e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey
883e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey    /**
884d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * Parse a specific entry from the given {@link Bundle} and insert into the
885851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu     * given {@link RawContactDelta}. Silently skips the insert when missing value
886d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * or no valid {@link EditType} found.
887d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     *
888d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * @param typeExtra {@link Bundle} key that holds the incoming
889d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     *            {@link EditType#rawValue} value.
890d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * @param valueExtra {@link Bundle} key that holds the incoming value.
891d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * @param valueColumn Column to write value into {@link ValuesDelta}.
892d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     */
893851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    public static ValuesDelta parseExtras(RawContactDelta state, DataKind kind, Bundle extras,
894d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            String typeExtra, String valueExtra, String valueColumn) {
8957f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        final CharSequence value = extras.getCharSequence(valueExtra);
8967f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey
89769f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov        // Bail early if account type doesn't handle this MIME type
898c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        if (kind == null) return null;
899d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
900d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        // Bail when can't insert type, or value missing
901851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu        final boolean canInsert = RawContactModifier.canInsert(state, kind);
902d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey        final boolean validValue = (value != null && TextUtils.isGraphic(value));
903c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        if (!validValue || !canInsert) return null;
904d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
9056164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey        // Find exact type when requested, otherwise best available type
9066164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey        final boolean hasType = extras.containsKey(typeExtra);
9076164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey        final int typeValue = extras.getInt(typeExtra, hasType ? BaseTypes.TYPE_CUSTOM
9086164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey                : Integer.MIN_VALUE);
909851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu        final EditType editType = RawContactModifier.getBestValidType(state, kind, true, typeValue);
910d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
911d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        // Create data row and fill with value
912851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu        final ValuesDelta child = RawContactModifier.insertChild(state, kind, editType);
9137f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        child.put(valueColumn, value.toString());
914d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
915d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        if (editType != null && editType.customColumn != null) {
916d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            // Write down label when custom type picked
917e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            final String customType = extras.getString(typeExtra);
918e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            child.put(editType.customColumn, customType);
919d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        }
920c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov
921c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        return child;
922d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey    }
923d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
924d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    /**
925d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     * Generic mime types with type support (e.g. TYPE_HOME).
926d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     * Here, "type support" means if the data kind has CommonColumns#TYPE or not. Data kinds which
927d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     * have their own migrate methods aren't listed here.
928d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     */
929d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    private static final Set<String> sGenericMimeTypesWithTypeSupport = new HashSet<String>(
930d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            Arrays.asList(Phone.CONTENT_ITEM_TYPE,
931d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    Email.CONTENT_ITEM_TYPE,
932d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    Im.CONTENT_ITEM_TYPE,
933d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    Nickname.CONTENT_ITEM_TYPE,
934d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    Website.CONTENT_ITEM_TYPE,
935d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    Relation.CONTENT_ITEM_TYPE,
936d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    SipAddress.CONTENT_ITEM_TYPE));
937d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    private static final Set<String> sGenericMimeTypesWithoutTypeSupport = new HashSet<String>(
938d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            Arrays.asList(Organization.CONTENT_ITEM_TYPE,
939d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    Note.CONTENT_ITEM_TYPE,
940d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    Photo.CONTENT_ITEM_TYPE,
941d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    GroupMembership.CONTENT_ITEM_TYPE));
942d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    // CommonColumns.TYPE cannot be accessed as it is protected interface, so use
943d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    // Phone.TYPE instead.
944d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    private static final String COLUMN_FOR_TYPE  = Phone.TYPE;
945d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    private static final String COLUMN_FOR_LABEL  = Phone.LABEL;
946d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    private static final int TYPE_CUSTOM = Phone.TYPE_CUSTOM;
947d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
948d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    /**
949851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu     * Migrates old RawContactDelta to newly created one with a new restriction supplied from
950d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     * newAccountType.
951d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     *
952d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     * This is only for account switch during account creation (which must be insert operation).
953d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     */
954d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    public static void migrateStateForNewContact(Context context,
955851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu            RawContactDelta oldState, RawContactDelta newState,
956d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            AccountType oldAccountType, AccountType newAccountType) {
957d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (newAccountType == oldAccountType) {
958d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            // Just copying all data in oldState isn't enough, but we can still rely on a lot of
959d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            // shortcuts.
960d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            for (DataKind kind : newAccountType.getSortedDataKinds()) {
961d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                final String mimeType = kind.mimeType;
962d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                // The fields with short/long form capability must be treated properly.
963d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
964d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    migrateStructuredName(context, oldState, newState, kind);
965d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else {
966d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    List<ValuesDelta> entryList = oldState.getMimeEntries(mimeType);
967d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    if (entryList != null && !entryList.isEmpty()) {
968d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        for (ValuesDelta entry : entryList) {
969d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                            ContentValues values = entry.getAfter();
970d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                            if (values != null) {
971d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                newState.addEntry(ValuesDelta.fromAfter(values));
972d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                            }
973d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        }
974d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    }
975d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
976d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
977d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        } else {
978d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            // Migrate data supported by the new account type.
979d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            // All the other data inside oldState are silently dropped.
980d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            for (DataKind kind : newAccountType.getSortedDataKinds()) {
9812b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann                if (!kind.editable) continue;
982d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                final String mimeType = kind.mimeType;
983d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME.equals(mimeType)
984d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        || DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
9852b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann                    // Ignore pseudo data.
986d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    continue;
987d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
988d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    migrateStructuredName(context, oldState, newState, kind);
989d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) {
990d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    migratePostal(oldState, newState, kind);
991d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType)) {
992d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    migrateEvent(oldState, newState, kind, null /* default Year */);
993d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else if (sGenericMimeTypesWithoutTypeSupport.contains(mimeType)) {
994d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    migrateGenericWithoutTypeColumn(oldState, newState, kind);
9952b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann                } else if (sGenericMimeTypesWithTypeSupport.contains(mimeType)) {
996d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    migrateGenericWithTypeColumn(oldState, newState, kind);
9972b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann                } else {
9982b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann                    throw new IllegalStateException("Unexpected editable mime-type: " + mimeType);
999d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1000d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1001d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1002d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    }
1003d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1004d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    /**
1005d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     * Checks {@link DataKind#isList} and {@link DataKind#typeOverallMax}, and restricts
1006d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     * the number of entries (ValuesDelta) inside newState.
1007d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     */
1008851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    private static ArrayList<ValuesDelta> ensureEntryMaxSize(RawContactDelta newState,
1009851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu            DataKind kind, ArrayList<ValuesDelta> mimeEntries) {
1010d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (mimeEntries == null) {
1011d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            return null;
1012d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1013d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
101450a27b70581b0191995969c63edd1f6a3db3d1b7Makoto Onuki        final int typeOverallMax = kind.typeOverallMax;
1015d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (typeOverallMax >= 0 && (mimeEntries.size() > typeOverallMax)) {
1016d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            ArrayList<ValuesDelta> newMimeEntries = new ArrayList<ValuesDelta>(typeOverallMax);
1017d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            for (int i = 0; i < typeOverallMax; i++) {
1018d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                newMimeEntries.add(mimeEntries.get(i));
1019d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1020d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            mimeEntries = newMimeEntries;
1021d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1022d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        return mimeEntries;
1023d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    }
1024d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1025d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    /** @hide Public only for testing. */
1026d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    public static void migrateStructuredName(
1027851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu            Context context, RawContactDelta oldState, RawContactDelta newState,
1028851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu            DataKind newDataKind) {
1029d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final ContentValues values =
1030d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                oldState.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE).getAfter();
1031d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (values == null) {
1032d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            return;
1033d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1034d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1035d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        boolean supportDisplayName = false;
1036d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        boolean supportPhoneticFullName = false;
1037d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        boolean supportPhoneticFamilyName = false;
1038d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        boolean supportPhoneticMiddleName = false;
1039d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        boolean supportPhoneticGivenName = false;
1040d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        for (EditField editField : newDataKind.fieldList) {
1041d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (StructuredName.DISPLAY_NAME.equals(editField.column)) {
1042d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                supportDisplayName = true;
1043d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1044d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (DataKind.PSEUDO_COLUMN_PHONETIC_NAME.equals(editField.column)) {
1045d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                supportPhoneticFullName = true;
1046d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1047d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (StructuredName.PHONETIC_FAMILY_NAME.equals(editField.column)) {
1048d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                supportPhoneticFamilyName = true;
1049d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1050d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (StructuredName.PHONETIC_MIDDLE_NAME.equals(editField.column)) {
1051d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                supportPhoneticMiddleName = true;
1052d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1053d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (StructuredName.PHONETIC_GIVEN_NAME.equals(editField.column)) {
1054d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                supportPhoneticGivenName = true;
1055d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1056d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1057d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1058d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // DISPLAY_NAME <-> PREFIX, GIVEN_NAME, MIDDLE_NAME, FAMILY_NAME, SUFFIX
1059d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final String displayName = values.getAsString(StructuredName.DISPLAY_NAME);
1060d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (!TextUtils.isEmpty(displayName)) {
1061d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (!supportDisplayName) {
1062d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                // Old data has a display name, while the new account doesn't allow it.
1063da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro                NameConverter.displayNameToStructuredName(context, displayName, values);
1064d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1065d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                // We don't want to migrate unseen data which may confuse users after the creation.
1066d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                values.remove(StructuredName.DISPLAY_NAME);
1067d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1068d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        } else {
1069d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (supportDisplayName) {
1070d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                // Old data does not have display name, while the new account requires it.
1071d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                values.put(StructuredName.DISPLAY_NAME,
1072da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro                        NameConverter.structuredNameToDisplayName(context, values));
1073da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro                for (String field : NameConverter.STRUCTURED_NAME_FIELDS) {
1074da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro                    values.remove(field);
1075da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro                }
1076d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1077d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1078d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1079d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // Phonetic (full) name <-> PHONETIC_FAMILY_NAME, PHONETIC_MIDDLE_NAME, PHONETIC_GIVEN_NAME
1080d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final String phoneticFullName = values.getAsString(DataKind.PSEUDO_COLUMN_PHONETIC_NAME);
1081d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (!TextUtils.isEmpty(phoneticFullName)) {
1082d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (!supportPhoneticFullName) {
1083d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                // Old data has a phonetic (full) name, while the new account doesn't allow it.
1084851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu                final StructuredNameDataItem tmpItem =
1085d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        PhoneticNameEditorView.parsePhoneticName(phoneticFullName, null);
1086d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                values.remove(DataKind.PSEUDO_COLUMN_PHONETIC_NAME);
1087d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (supportPhoneticFamilyName) {
1088d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.put(StructuredName.PHONETIC_FAMILY_NAME,
1089851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu                            tmpItem.getPhoneticFamilyName());
1090d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else {
1091d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredName.PHONETIC_FAMILY_NAME);
1092d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1093d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (supportPhoneticMiddleName) {
1094d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.put(StructuredName.PHONETIC_MIDDLE_NAME,
1095851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu                            tmpItem.getPhoneticMiddleName());
1096d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else {
1097d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredName.PHONETIC_MIDDLE_NAME);
1098d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1099d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (supportPhoneticGivenName) {
1100d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.put(StructuredName.PHONETIC_GIVEN_NAME,
1101851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu                            tmpItem.getPhoneticGivenName());
1102d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else {
1103d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredName.PHONETIC_GIVEN_NAME);
1104d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1105d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1106d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        } else {
1107d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (supportPhoneticFullName) {
1108d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                // Old data does not have a phonetic (full) name, while the new account requires it.
1109d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                values.put(DataKind.PSEUDO_COLUMN_PHONETIC_NAME,
1110d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        PhoneticNameEditorView.buildPhoneticName(
1111d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredName.PHONETIC_FAMILY_NAME),
1112d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredName.PHONETIC_MIDDLE_NAME),
1113d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredName.PHONETIC_GIVEN_NAME)));
1114d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1115d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (!supportPhoneticFamilyName) {
1116d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                values.remove(StructuredName.PHONETIC_FAMILY_NAME);
1117d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1118d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (!supportPhoneticMiddleName) {
1119d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                values.remove(StructuredName.PHONETIC_MIDDLE_NAME);
1120d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1121d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (!supportPhoneticGivenName) {
1122d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                values.remove(StructuredName.PHONETIC_GIVEN_NAME);
1123d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1124d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1125d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1126d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        newState.addEntry(ValuesDelta.fromAfter(values));
1127d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    }
1128d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1129d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    /** @hide Public only for testing. */
1130851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    public static void migratePostal(RawContactDelta oldState, RawContactDelta newState,
1131d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            DataKind newDataKind) {
1132d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind,
1133d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                oldState.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE));
1134d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (mimeEntries == null || mimeEntries.isEmpty()) {
1135d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            return;
1136d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1137d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1138d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        boolean supportFormattedAddress = false;
1139d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        boolean supportStreet = false;
1140d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final String firstColumn = newDataKind.fieldList.get(0).column;
1141d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        for (EditField editField : newDataKind.fieldList) {
1142d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (StructuredPostal.FORMATTED_ADDRESS.equals(editField.column)) {
1143d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                supportFormattedAddress = true;
1144d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1145d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (StructuredPostal.STREET.equals(editField.column)) {
1146d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                supportStreet = true;
1147d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1148d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1149d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1150d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final Set<Integer> supportedTypes = new HashSet<Integer>();
1151d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (newDataKind.typeList != null && !newDataKind.typeList.isEmpty()) {
1152d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            for (EditType editType : newDataKind.typeList) {
1153d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                supportedTypes.add(editType.rawValue);
1154d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1155d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1156d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1157d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        for (ValuesDelta entry : mimeEntries) {
1158d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            final ContentValues values = entry.getAfter();
1159d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (values == null) {
1160d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                continue;
1161d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1162d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            final Integer oldType = values.getAsInteger(StructuredPostal.TYPE);
1163d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (!supportedTypes.contains(oldType)) {
1164d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                int defaultType;
1165d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (newDataKind.defaultValues != null) {
1166d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    defaultType = newDataKind.defaultValues.getAsInteger(StructuredPostal.TYPE);
1167d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else {
1168d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    defaultType = newDataKind.typeList.get(0).rawValue;
1169d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1170d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                values.put(StructuredPostal.TYPE, defaultType);
1171d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (oldType != null && oldType == StructuredPostal.TYPE_CUSTOM) {
1172d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredPostal.LABEL);
1173d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1174d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1175d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1176d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            final String formattedAddress = values.getAsString(StructuredPostal.FORMATTED_ADDRESS);
1177d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (!TextUtils.isEmpty(formattedAddress)) {
1178d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (!supportFormattedAddress) {
1179d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    // Old data has a formatted address, while the new account doesn't allow it.
1180d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredPostal.FORMATTED_ADDRESS);
1181d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1182d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    // Unlike StructuredName we don't have logic to split it, so first
1183d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    // try to use street field and. If the new account doesn't have one,
1184d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    // then select first one anyway.
1185d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    if (supportStreet) {
1186d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        values.put(StructuredPostal.STREET, formattedAddress);
1187d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    } else {
1188d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        values.put(firstColumn, formattedAddress);
1189d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    }
1190d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1191d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            } else {
1192d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (supportFormattedAddress) {
1193d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    // Old data does not have formatted address, while the new account requires it.
1194d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    // Unlike StructuredName we don't have logic to join multiple address values.
1195d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    // Use poor join heuristics for now.
1196d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    String[] structuredData;
1197d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    final boolean useJapaneseOrder =
1198d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                            Locale.JAPANESE.getLanguage().equals(Locale.getDefault().getLanguage());
1199d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    if (useJapaneseOrder) {
1200d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        structuredData = new String[] {
1201d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.COUNTRY),
1202d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.POSTCODE),
1203d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.REGION),
1204d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.CITY),
1205d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.NEIGHBORHOOD),
1206d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.STREET),
1207d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.POBOX) };
1208d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    } else {
1209d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        structuredData = new String[] {
1210d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.POBOX),
1211d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.STREET),
1212d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.NEIGHBORHOOD),
1213d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.CITY),
1214d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.REGION),
1215d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.POSTCODE),
1216d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.COUNTRY) };
1217d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    }
1218d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    final StringBuilder builder = new StringBuilder();
1219d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    for (String elem : structuredData) {
1220d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        if (!TextUtils.isEmpty(elem)) {
1221d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                            builder.append(elem + "\n");
1222d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        }
1223d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    }
1224d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.put(StructuredPostal.FORMATTED_ADDRESS, builder.toString());
1225d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1226d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredPostal.POBOX);
1227d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredPostal.STREET);
1228d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredPostal.NEIGHBORHOOD);
1229d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredPostal.CITY);
1230d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredPostal.REGION);
1231d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredPostal.POSTCODE);
1232d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredPostal.COUNTRY);
1233d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1234d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1235d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1236d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            newState.addEntry(ValuesDelta.fromAfter(values));
1237d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1238d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    }
1239d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1240d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    /** @hide Public only for testing. */
1241851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu    public static void migrateEvent(RawContactDelta oldState, RawContactDelta newState,
1242d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            DataKind newDataKind, Integer defaultYear) {
1243d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind,
1244d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                oldState.getMimeEntries(Event.CONTENT_ITEM_TYPE));
1245d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (mimeEntries == null || mimeEntries.isEmpty()) {
1246d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            return;
1247d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1248d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
12497b1255eab0729fd43a8aae9933bd79afa50a53a9Daniel Lehmann        final SparseArray<EventEditType> allowedTypes = new SparseArray<EventEditType>();
1250d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        for (EditType editType : newDataKind.typeList) {
1251d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            allowedTypes.put(editType.rawValue, (EventEditType) editType);
1252d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1253d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        for (ValuesDelta entry : mimeEntries) {
1254d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            final ContentValues values = entry.getAfter();
1255d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (values == null) {
1256d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                continue;
1257d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1258d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            final String dateString = values.getAsString(Event.START_DATE);
1259d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            final Integer type = values.getAsInteger(Event.TYPE);
12607b1255eab0729fd43a8aae9933bd79afa50a53a9Daniel Lehmann            if (type != null && (allowedTypes.indexOfKey(type) >= 0)
12617b1255eab0729fd43a8aae9933bd79afa50a53a9Daniel Lehmann                    && !TextUtils.isEmpty(dateString)) {
1262d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                EventEditType suitableType = allowedTypes.get(type);
1263d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1264d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                final ParsePosition position = new ParsePosition(0);
1265d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                boolean yearOptional = false;
1266d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                Date date = DateUtils.DATE_AND_TIME_FORMAT.parse(dateString, position);
1267d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (date == null) {
1268d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    yearOptional = true;
1269d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    date = DateUtils.NO_YEAR_DATE_FORMAT.parse(dateString, position);
1270d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1271d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (date != null) {
1272d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    if (yearOptional && !suitableType.isYearOptional()) {
1273d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        // The new EditType doesn't allow optional year. Supply default.
1274d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        final Calendar calendar = Calendar.getInstance(DateUtils.UTC_TIMEZONE,
1275d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                Locale.US);
1276d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        if (defaultYear == null) {
1277d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                            defaultYear = calendar.get(Calendar.YEAR);
1278d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        }
1279d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        calendar.setTime(date);
1280d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        final int month = calendar.get(Calendar.MONTH);
1281d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        final int day = calendar.get(Calendar.DAY_OF_MONTH);
1282d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        // Exchange requires 8:00 for birthdays
1283d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        calendar.set(defaultYear, month, day,
1284d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                EventFieldEditorView.getDefaultHourForBirthday(), 0, 0);
1285d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        values.put(Event.START_DATE,
1286d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                DateUtils.FULL_DATE_FORMAT.format(calendar.getTime()));
1287d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    }
1288d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1289d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                newState.addEntry(ValuesDelta.fromAfter(values));
1290d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            } else {
1291d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                // Just drop it.
1292d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1293d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1294d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    }
1295d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1296d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    /** @hide Public only for testing. */
1297d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    public static void migrateGenericWithoutTypeColumn(
1298851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu            RawContactDelta oldState, RawContactDelta newState, DataKind newDataKind) {
1299d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind,
1300d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                oldState.getMimeEntries(newDataKind.mimeType));
1301d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (mimeEntries == null || mimeEntries.isEmpty()) {
1302d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            return;
1303d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1304d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1305d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        for (ValuesDelta entry : mimeEntries) {
1306d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            ContentValues values = entry.getAfter();
1307d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (values != null) {
1308d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                newState.addEntry(ValuesDelta.fromAfter(values));
1309d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1310d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1311d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    }
1312d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1313d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    /** @hide Public only for testing. */
1314d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    public static void migrateGenericWithTypeColumn(
1315851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu            RawContactDelta oldState, RawContactDelta newState, DataKind newDataKind) {
1316d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final ArrayList<ValuesDelta> mimeEntries = oldState.getMimeEntries(newDataKind.mimeType);
1317d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (mimeEntries == null || mimeEntries.isEmpty()) {
1318d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            return;
1319d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1320d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1321d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // Note that type specified with the old account may be invalid with the new account, while
1322d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // we want to preserve its data as much as possible. e.g. if a user typed a phone number
1323d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // with a type which is valid with an old account but not with a new account, the user
1324d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // probably wants to have the number with default type, rather than seeing complete data
1325d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // loss.
1326d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        //
1327d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // Specifically, this method works as follows:
1328d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // 1. detect defaultType
1329d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // 2. prepare constants & variables for iteration
1330d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // 3. iterate over mimeEntries:
1331d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // 3.1 stop iteration if total number of mimeEntries reached typeOverallMax specified in
1332d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        //     DataKind
1333d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // 3.2 replace unallowed types with defaultType
1334d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // 3.3 check if the number of entries is below specificMax specified in AccountType
1335d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1336d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // Here, defaultType can be supplied in two ways
1337d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // - via kind.defaultValues
1338d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // - via kind.typeList.get(0).rawValue
1339d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        Integer defaultType = null;
1340d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (newDataKind.defaultValues != null) {
1341d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            defaultType = newDataKind.defaultValues.getAsInteger(COLUMN_FOR_TYPE);
1342d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1343d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final Set<Integer> allowedTypes = new HashSet<Integer>();
1344d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // key: type, value: the number of entries allowed for the type (specificMax)
13457b1255eab0729fd43a8aae9933bd79afa50a53a9Daniel Lehmann        final SparseIntArray typeSpecificMaxMap = new SparseIntArray();
1346d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (defaultType != null) {
1347d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            allowedTypes.add(defaultType);
1348d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            typeSpecificMaxMap.put(defaultType, -1);
1349d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1350d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // Note: typeList may be used in different purposes when defaultValues are specified.
1351d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // Especially in IM, typeList contains available protocols (e.g. PROTOCOL_GOOGLE_TALK)
1352d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // instead of "types" which we want to treate here (e.g. TYPE_HOME). So we don't add
1353d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // anything other than defaultType into allowedTypes and typeSpecificMapMax.
1354d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (!Im.CONTENT_ITEM_TYPE.equals(newDataKind.mimeType) &&
1355d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                newDataKind.typeList != null && !newDataKind.typeList.isEmpty()) {
1356d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            for (EditType editType : newDataKind.typeList) {
1357d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                allowedTypes.add(editType.rawValue);
1358d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                typeSpecificMaxMap.put(editType.rawValue, editType.specificMax);
1359d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1360d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (defaultType == null) {
1361d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                defaultType = newDataKind.typeList.get(0).rawValue;
1362d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1363d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1364d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1365d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (defaultType == null) {
1366d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            Log.w(TAG, "Default type isn't available for mimetype " + newDataKind.mimeType);
1367d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1368d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
136950a27b70581b0191995969c63edd1f6a3db3d1b7Makoto Onuki        final int typeOverallMax = newDataKind.typeOverallMax;
1370d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1371d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // key: type, value: the number of current entries.
13727b1255eab0729fd43a8aae9933bd79afa50a53a9Daniel Lehmann        final SparseIntArray currentEntryCount = new SparseIntArray();
1373d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        int totalCount = 0;
1374d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1375d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        for (ValuesDelta entry : mimeEntries) {
1376d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (typeOverallMax != -1 && totalCount >= typeOverallMax) {
1377d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                break;
1378d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1379d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1380d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            final ContentValues values = entry.getAfter();
1381d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (values == null) {
1382d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                continue;
1383d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1384d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1385d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            final Integer oldType = entry.getAsInteger(COLUMN_FOR_TYPE);
1386d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            final Integer typeForNewAccount;
1387d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (!allowedTypes.contains(oldType)) {
1388d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                // The new account doesn't support the type.
1389d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (defaultType != null) {
1390d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    typeForNewAccount = defaultType.intValue();
1391d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.put(COLUMN_FOR_TYPE, defaultType.intValue());
1392d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    if (oldType != null && oldType == TYPE_CUSTOM) {
1393d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        values.remove(COLUMN_FOR_LABEL);
1394d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    }
1395d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else {
1396d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    typeForNewAccount = null;
1397d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(COLUMN_FOR_TYPE);
1398d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1399d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            } else {
1400d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                typeForNewAccount = oldType;
1401d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1402d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (typeForNewAccount != null) {
14037b1255eab0729fd43a8aae9933bd79afa50a53a9Daniel Lehmann                final int specificMax = typeSpecificMaxMap.get(typeForNewAccount, 0);
1404d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (specificMax >= 0) {
14057b1255eab0729fd43a8aae9933bd79afa50a53a9Daniel Lehmann                    final int currentCount = currentEntryCount.get(typeForNewAccount, 0);
1406d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    if (currentCount >= specificMax) {
1407d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        continue;
1408d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    }
1409d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    currentEntryCount.put(typeForNewAccount, currentCount + 1);
1410d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1411d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1412d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            newState.addEntry(ValuesDelta.fromAfter(values));
1413d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            totalCount++;
1414d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1415d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    }
14162ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey}
1417