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
19e31dac84479a4c7b356edfc062a447cdfb5efc69Jeff Sharkeyimport com.android.contacts.ContactsUtils;
20d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport com.android.contacts.editor.EventFieldEditorView;
21da5bf1cf60beef3de5e651a569fa544293683926Dave Santoroimport com.android.contacts.util.NameConverter;
22d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport com.android.contacts.editor.PhoneticNameEditorView;
234597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikovimport com.android.contacts.model.AccountType.EditField;
244597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikovimport com.android.contacts.model.AccountType.EditType;
25d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport com.android.contacts.model.AccountType.EventEditType;
26d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport com.android.contacts.model.EntityDelta.ValuesDelta;
27d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport com.android.contacts.util.DateUtils;
282ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
292ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeyimport android.content.ContentValues;
30802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkeyimport android.content.Context;
31802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkeyimport android.database.Cursor;
32c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.net.Uri;
33d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.os.Bundle;
34c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract;
357f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.BaseTypes;
36d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Email;
37d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Event;
38c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
39d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Im;
40d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Nickname;
4116c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Note;
4216c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Organization;
43d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Phone;
44a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintanaimport android.provider.ContactsContract.CommonDataKinds.Photo;
45d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Relation;
46d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.SipAddress;
47d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
48d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
49d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Website;
50c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract.Data;
51c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract.Intents;
52d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.Intents.Insert;
53c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract.RawContacts;
54d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.text.TextUtils;
556f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkeyimport android.util.Log;
5607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport android.util.SparseIntArray;
572ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
58d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.text.ParsePosition;
5907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport java.util.ArrayList;
60d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Arrays;
61d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Calendar;
62d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Date;
63d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.HashMap;
64d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.HashSet;
6507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport java.util.Iterator;
662ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeyimport java.util.List;
67d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Locale;
68d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Map;
69d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Set;
702ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
712ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey/**
728d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * Helper methods for modifying an {@link EntityDelta}, such as inserting
734597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov * new rows, or enforcing {@link AccountType}.
742ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */
752ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeypublic class EntityModifier {
766f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    private static final String TAG = "EntityModifier";
776f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
78a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan    /** Set to true in order to view logs on entity operations */
79a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan    private static final boolean DEBUG = false;
80a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan
812ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    /**
828d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey     * For the given {@link EntityDelta}, determine if the given
832ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * {@link DataKind} could be inserted under specific
844597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov     * {@link AccountType}.
852ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     */
868d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey    public static boolean canInsert(EntityDelta state, DataKind kind) {
8707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Insert possible when have valid types and under overall maximum
88ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey        final int visibleCount = state.getMimeEntriesCount(kind.mimeType, true);
8907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final boolean validTypes = hasValidTypes(state, kind);
9007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final boolean validOverall = (kind.typeOverallMax == -1)
91ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey                || (visibleCount < kind.typeOverallMax);
9207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        return (validTypes && validOverall);
9307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
9407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
958d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey    public static boolean hasValidTypes(EntityDelta state, DataKind kind) {
96d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        if (EntityModifier.hasEditTypes(kind)) {
97d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            return (getValidTypes(state, kind).size() > 0);
98d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        } else {
99d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            return true;
100d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        }
1012ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    }
1022ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
1032ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    /**
104aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey     * Ensure that at least one of the given {@link DataKind} exists in the
105aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey     * given {@link EntityDelta} state, and try creating one if none exist.
106aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey     */
10769f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov    public static void ensureKindExists(
10869f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov            EntityDelta state, AccountType accountType, String mimeType) {
10969f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov        final DataKind kind = accountType.getKindForMimetype(mimeType);
110ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey        final boolean hasChild = state.getMimeEntriesCount(mimeType, true) > 0;
111aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
112aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        if (!hasChild && kind != null) {
113aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey            // Create child when none exists and valid kind
114e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi            final ValuesDelta child = insertChild(state, kind);
115e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi            if (kind.mimeType.equals(Photo.CONTENT_ITEM_TYPE)) {
116e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi                child.setFromTemplate(true);
117e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi            }
118aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        }
119aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    }
120aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
121aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    /**
1228d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey     * For the given {@link EntityDelta} and {@link DataKind}, return the
1232ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * list possible {@link EditType} options available based on
1244597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov     * {@link AccountType}.
1252ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     */
1268d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey    public static ArrayList<EditType> getValidTypes(EntityDelta state, DataKind kind) {
12707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        return getValidTypes(state, kind, null, true, null);
12807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
12907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
13007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    /**
1318d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey     * For the given {@link EntityDelta} and {@link DataKind}, return the
13207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * list possible {@link EditType} options available based on
1334597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov     * {@link AccountType}.
13407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *
13507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * @param forceInclude Always include this {@link EditType} in the returned
13607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *            list, even when an otherwise-invalid choice. This is useful
13707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *            when showing a dialog that includes the current type.
13807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     */
1398d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey    public static ArrayList<EditType> getValidTypes(EntityDelta state, DataKind kind,
1402ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey            EditType forceInclude) {
14107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        return getValidTypes(state, kind, forceInclude, true, null);
14207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
14307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
14407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    /**
1458d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey     * For the given {@link EntityDelta} and {@link DataKind}, return the
14607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * list possible {@link EditType} options available based on
1474597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov     * {@link AccountType}.
14807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *
14907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * @param forceInclude Always include this {@link EditType} in the returned
15007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *            list, even when an otherwise-invalid choice. This is useful
15107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *            when showing a dialog that includes the current type.
15207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * @param includeSecondary If true, include any valid types marked as
15307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *            {@link EditType#secondary}.
15407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * @param typeCount When provided, will be used for the frequency count of
15507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *            each {@link EditType}, otherwise built using
1568d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey     *            {@link #getTypeFrequencies(EntityDelta, DataKind)}.
15707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     */
1588d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey    private static ArrayList<EditType> getValidTypes(EntityDelta state, DataKind kind,
15907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            EditType forceInclude, boolean includeSecondary, SparseIntArray typeCount) {
160da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro        final ArrayList<EditType> validTypes = new ArrayList<EditType>();
16107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
16207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Bail early if no types provided
16307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (!hasEditTypes(kind)) return validTypes;
16407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
16507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (typeCount == null) {
16607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            // Build frequency counts if not provided
16707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            typeCount = getTypeFrequencies(state, kind);
16807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        }
16907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
17007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Build list of valid types
17107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final int overallCount = typeCount.get(FREQUENCY_TOTAL);
17207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        for (EditType type : kind.typeList) {
173d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            final boolean validOverall = (kind.typeOverallMax == -1 ? true
174d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                    : overallCount < kind.typeOverallMax);
175d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            final boolean validSpecific = (type.specificMax == -1 ? true : typeCount
176d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                    .get(type.rawValue) < type.specificMax);
17707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            final boolean validSecondary = (includeSecondary ? true : !type.secondary);
17807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            final boolean forcedInclude = type.equals(forceInclude);
179d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            if (forcedInclude || (validOverall && validSpecific && validSecondary)) {
18007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                // Type is valid when no limit, under limit, or forced include
18107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                validTypes.add(type);
18207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            }
18307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        }
18407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
18507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        return validTypes;
18607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
18707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
18807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    private static final int FREQUENCY_TOTAL = Integer.MIN_VALUE;
18907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
19007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    /**
19107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * Count up the frequency that each {@link EditType} appears in the given
1928d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey     * {@link EntityDelta}. The returned {@link SparseIntArray} maps from
19307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * {@link EditType#rawValue} to counts, with the total overall count stored
19407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * as {@link #FREQUENCY_TOTAL}.
19507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     */
1968d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey    private static SparseIntArray getTypeFrequencies(EntityDelta state, DataKind kind) {
19707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final SparseIntArray typeCount = new SparseIntArray();
19807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
19907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Find all entries for this kind, bailing early if none found
2008d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey        final List<ValuesDelta> mimeEntries = state.getMimeEntries(kind.mimeType);
20107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (mimeEntries == null) return typeCount;
20207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
20307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        int totalCount = 0;
2048d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey        for (ValuesDelta entry : mimeEntries) {
20507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            // Only count visible entries
20607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            if (!entry.isVisible()) continue;
20707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            totalCount++;
20807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
20907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            final EditType type = getCurrentType(entry, kind);
21007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            if (type != null) {
21107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                final int count = typeCount.get(type.rawValue);
21207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                typeCount.put(type.rawValue, count + 1);
21307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            }
21407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        }
21507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        typeCount.put(FREQUENCY_TOTAL, totalCount);
21607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        return typeCount;
2172ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    }
2182ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
2192ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    /**
2202ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * Check if the given {@link DataKind} has multiple types that should be
2212ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * displayed for users to pick.
2222ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     */
2232ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    public static boolean hasEditTypes(DataKind kind) {
2242ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey        return kind.typeList != null && kind.typeList.size() > 0;
2252ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    }
2262ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
2272ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    /**
2282ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * Find the {@link EditType} that describes the given
2298d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey     * {@link ValuesDelta} row, assuming the given {@link DataKind} dictates
2302ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * the possible types.
2312ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     */
2328d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey    public static EditType getCurrentType(ValuesDelta entry, DataKind kind) {
23307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final Long rawValue = entry.getAsLong(kind.typeColumn);
23407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (rawValue == null) return null;
23507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        return getType(kind, rawValue.intValue());
23607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
23707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
23807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    /**
23911d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar     * Find the {@link EditType} that describes the given {@link ContentValues} row,
24011d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar     * assuming the given {@link DataKind} dictates the possible types.
24111d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar     */
24211d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar    public static EditType getCurrentType(ContentValues entry, DataKind kind) {
24311d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar        if (kind.typeColumn == null) return null;
24449d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey        final Integer rawValue = entry.getAsInteger(kind.typeColumn);
24549d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey        if (rawValue == null) return null;
24611d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar        return getType(kind, rawValue);
24711d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar    }
24811d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar
24911d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar    /**
250802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey     * Find the {@link EditType} that describes the given {@link Cursor} row,
251802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey     * assuming the given {@link DataKind} dictates the possible types.
252802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey     */
253802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey    public static EditType getCurrentType(Cursor cursor, DataKind kind) {
254ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey        if (kind.typeColumn == null) return null;
255802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey        final int index = cursor.getColumnIndex(kind.typeColumn);
25649d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey        if (index == -1) return null;
257802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey        final int rawValue = cursor.getInt(index);
258802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey        return getType(kind, rawValue);
259802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey    }
260802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey
261802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey    /**
26207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * Find the {@link EditType} with the given {@link EditType#rawValue}.
26307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     */
26407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    public static EditType getType(DataKind kind, int rawValue) {
2652ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey        for (EditType type : kind.typeList) {
2662ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey            if (type.rawValue == rawValue) {
2672ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey                return type;
2682ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey            }
2692ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey        }
2702ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey        return null;
2712ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    }
2722ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
2732ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    /**
27449d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey     * Return the precedence for the the given {@link EditType#rawValue}, where
27549d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey     * lower numbers are higher precedence.
27649d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey     */
27749d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey    public static int getTypePrecedence(DataKind kind, int rawValue) {
27849d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey        for (int i = 0; i < kind.typeList.size(); i++) {
27949d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey            final EditType type = kind.typeList.get(i);
28049d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey            if (type.rawValue == rawValue) {
28149d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey                return i;
28249d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey            }
28349d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey        }
28449d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey        return Integer.MAX_VALUE;
28549d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey    }
28649d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey
28749d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey    /**
28807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * Find the best {@link EditType} for a potential insert. The "best" is the
28907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * first primary type that doesn't already exist. When all valid types
29007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * exist, we pick the last valid option.
29107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     */
2928d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey    public static EditType getBestValidType(EntityDelta state, DataKind kind,
293d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            boolean includeSecondary, int exactValue) {
29407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Shortcut when no types
29507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (kind.typeColumn == null) return null;
29607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
29707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Find type counts and valid primary types, bail if none
29807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final SparseIntArray typeCount = getTypeFrequencies(state, kind);
29907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final ArrayList<EditType> validTypes = getValidTypes(state, kind, null, includeSecondary,
30007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                typeCount);
30107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (validTypes.size() == 0) return null;
30207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
30307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Keep track of the last valid type
30407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final EditType lastType = validTypes.get(validTypes.size() - 1);
30507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
30607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Remove any types that already exist
30707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        Iterator<EditType> iterator = validTypes.iterator();
30807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        while (iterator.hasNext()) {
30907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            final EditType type = iterator.next();
31007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            final int count = typeCount.get(type.rawValue);
31107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
312e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            if (exactValue == type.rawValue) {
313d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                // Found exact value match
314d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                return type;
315d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            }
316d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
31707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            if (count > 0) {
31807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                // Type already appears, so don't consider
31907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                iterator.remove();
32007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            }
32107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        }
32207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
32307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Use the best remaining, otherwise the last valid
32407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (validTypes.size() > 0) {
32507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            return validTypes.get(0);
32607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        } else {
32707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            return lastType;
32807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        }
32907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
33007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
33107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    /**
3322ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * Insert a new child of kind {@link DataKind} into the given
333d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * {@link EntityDelta}. Tries using the best {@link EditType} found using
334d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * {@link #getBestValidType(EntityDelta, DataKind, boolean, int)}.
3352ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     */
336d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey    public static ValuesDelta insertChild(EntityDelta state, DataKind kind) {
33707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // First try finding a valid primary
338d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        EditType bestType = getBestValidType(state, kind, false, Integer.MIN_VALUE);
33907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (bestType == null) {
34007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            // No valid primary found, so expand search to secondary
341d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            bestType = getBestValidType(state, kind, true, Integer.MIN_VALUE);
34207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        }
343d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        return insertChild(state, kind, bestType);
34407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
34507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
34607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    /**
34707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * Insert a new child of kind {@link DataKind} into the given
3488d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey     * {@link EntityDelta}, marked with the given {@link EditType}.
34907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     */
350d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey    public static ValuesDelta insertChild(EntityDelta state, DataKind kind, EditType type) {
351d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        // Bail early if invalid kind
352d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        if (kind == null) return null;
3532ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey        final ContentValues after = new ContentValues();
3542ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
355e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey        // Our parent CONTACT_ID is provided later
3562ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey        after.put(Data.MIMETYPE, kind.mimeType);
357e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey
358e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey        // Fill-in with any requested default values
359e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey        if (kind.defaultValues != null) {
360e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey            after.putAll(kind.defaultValues);
361e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey        }
362e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey
36307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (kind.typeColumn != null && type != null) {
36407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            // Set type, if provided
36507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            after.put(kind.typeColumn, type.rawValue);
366e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey        }
3672ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
368d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        final ValuesDelta child = ValuesDelta.fromAfter(after);
369a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov        state.addEntry(child);
370d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        return child;
3712ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    }
3722ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
373d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey    /**
3747f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey     * Processing to trim any empty {@link ValuesDelta} and {@link EntityDelta}
375a07fa5f37031e4c5cd2933de02d2db41ec153e2bDmitri Plotnikov     * from the given {@link EntityDeltaList}, assuming the given {@link AccountTypeManager}
3767f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey     * dictates the structure for various fields. This method ignores rows not
3774597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov     * described by the {@link AccountType}.
3787f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey     */
379a07fa5f37031e4c5cd2933de02d2db41ec153e2bDmitri Plotnikov    public static void trimEmpty(EntityDeltaList set, AccountTypeManager accountTypes) {
3807f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        for (EntityDelta state : set) {
3812b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            ValuesDelta values = state.getValues();
3822b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
3832b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            final String dataSet = values.getAsString(RawContacts.DATA_SET);
3842b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            final AccountType type = accountTypes.getAccountType(accountType, dataSet);
385a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov            trimEmpty(state, type);
3867f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        }
3877f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey    }
3887f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey
389a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov    public static boolean hasChanges(EntityDeltaList set, AccountTypeManager accountTypes) {
390a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov        if (set.isMarkedForSplitting() || set.isMarkedForJoining()) {
391a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov            return true;
392a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov        }
393a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov
394a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov        for (EntityDelta state : set) {
3952b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            ValuesDelta values = state.getValues();
3962b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
3972b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            final String dataSet = values.getAsString(RawContacts.DATA_SET);
3982b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro            final AccountType type = accountTypes.getAccountType(accountType, dataSet);
399a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov            if (hasChanges(state, type)) {
400a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov                return true;
401a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov            }
402a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov        }
403a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov        return false;
404a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov    }
405a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov
4067f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey    /**
4076f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     * Processing to trim any empty {@link ValuesDelta} rows from the given
4084597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov     * {@link EntityDelta}, assuming the given {@link AccountType} dictates
4096f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     * the structure for various fields. This method ignores rows not described
4104597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov     * by the {@link AccountType}.
4116f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     */
41269f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov    public static void trimEmpty(EntityDelta state, AccountType accountType) {
4137f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        boolean hasValues = false;
4147f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey
4156f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey        // Walk through entries for each well-known kind
41669f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov        for (DataKind kind : accountType.getSortedDataKinds()) {
4176f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            final String mimeType = kind.mimeType;
4186f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            final ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType);
4196f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            if (entries == null) continue;
4206f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
4216f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            for (ValuesDelta entry : entries) {
4227f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                // Skip any values that haven't been touched
4236f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey                final boolean touched = entry.isInsert() || entry.isUpdate();
4247f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                if (!touched) {
4257f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                    hasValues = true;
4267f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                    continue;
4277f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                }
4287f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey
429a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana                // Test and remove this row if empty and it isn't a photo from google
43069f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov                final boolean isGoogleAccount = TextUtils.equals(GoogleAccountType.ACCOUNT_TYPE,
431a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana                        state.getValues().getAsString(RawContacts.ACCOUNT_TYPE));
432a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana                final boolean isPhoto = TextUtils.equals(Photo.CONTENT_ITEM_TYPE, kind.mimeType);
43369f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov                final boolean isGooglePhoto = isPhoto && isGoogleAccount;
434e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi
435a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana                if (EntityModifier.isEmpty(entry, kind) && !isGooglePhoto) {
436a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan                    if (DEBUG) {
437a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan                        Log.v(TAG, "Trimming: " + entry.toString());
438a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan                    }
4396f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey                    entry.markDeleted();
440d2fdb9095e0009408b89e24048154ea7a8a14413Megha Joshi                } else if (!entry.isFromTemplate()) {
4417f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                    hasValues = true;
4426f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey                }
4436f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            }
4446f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey        }
4457f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        if (!hasValues) {
4467f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey            // Trim overall entity if no children exist
4477f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey            state.markDeleted();
4487f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        }
4496f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    }
4506f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
451a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov    private static boolean hasChanges(EntityDelta state, AccountType accountType) {
452a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov        for (DataKind kind : accountType.getSortedDataKinds()) {
453a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov            final String mimeType = kind.mimeType;
454a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov            final ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType);
455a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov            if (entries == null) continue;
456a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov
457a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov            for (ValuesDelta entry : entries) {
458de0898f0337d3c6ea818237b0a94acf1f15b6299Daniel Lehmann                // An empty Insert must be ignored, because it won't save anything (an example
459de0898f0337d3c6ea818237b0a94acf1f15b6299Daniel Lehmann                // is an empty name that stays empty)
460de0898f0337d3c6ea818237b0a94acf1f15b6299Daniel Lehmann                final boolean isRealInsert = entry.isInsert() && !isEmpty(entry, kind);
461de0898f0337d3c6ea818237b0a94acf1f15b6299Daniel Lehmann                if (isRealInsert || entry.isUpdate() || entry.isDelete()) {
462a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov                    return true;
463a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov                }
464a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov            }
465a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov        }
466a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov        return false;
467a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov    }
468a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov
4696f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    /**
4706f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     * Test if the given {@link ValuesDelta} would be considered "empty" in
4716f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     * terms of {@link DataKind#fieldList}.
4726f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     */
4736f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    public static boolean isEmpty(ValuesDelta values, DataKind kind) {
474f3cdeca5e0b3a7f9411b6babad5de2925623372aDmitri Plotnikov        if (Photo.CONTENT_ITEM_TYPE.equals(kind.mimeType)) {
475c863b0171f2a54a0ee71b34b33034aeea2464ccaDmitri Plotnikov            return values.isInsert() && values.getAsByteArray(Photo.PHOTO) == null;
476f3cdeca5e0b3a7f9411b6babad5de2925623372aDmitri Plotnikov        }
477f3cdeca5e0b3a7f9411b6babad5de2925623372aDmitri Plotnikov
478dbeb965d19a3c03e78cd54d9fbe7337b4fbb06f2Jeff Sharkey        // No defined fields mean this row is always empty
479dbeb965d19a3c03e78cd54d9fbe7337b4fbb06f2Jeff Sharkey        if (kind.fieldList == null) return true;
480dbeb965d19a3c03e78cd54d9fbe7337b4fbb06f2Jeff Sharkey
4816f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey        for (EditField field : kind.fieldList) {
4826f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            // If any field has values, we're not empty
4836f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            final String value = values.getAsString(field.column);
484e31dac84479a4c7b356edfc062a447cdfb5efc69Jeff Sharkey            if (ContactsUtils.isGraphic(value)) {
485c863b0171f2a54a0ee71b34b33034aeea2464ccaDmitri Plotnikov                return false;
4866f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            }
4876f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey        }
4886f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
489c863b0171f2a54a0ee71b34b33034aeea2464ccaDmitri Plotnikov        return true;
4906f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    }
4916f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
4926f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    /**
493916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     * Compares corresponding fields in values1 and values2. Only the fields
494916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     * declared by the DataKind are taken into consideration.
495916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     */
496916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    protected static boolean areEqual(ValuesDelta values1, ContentValues values2, DataKind kind) {
497916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        if (kind.fieldList == null) return false;
498916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
499916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        for (EditField field : kind.fieldList) {
500916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            final String value1 = values1.getAsString(field.column);
501916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            final String value2 = values2.getAsString(field.column);
502916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            if (!TextUtils.equals(value1, value2)) {
503916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                return false;
504916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            }
505916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        }
506916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
507916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        return true;
508916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    }
509916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
510916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    /**
511d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * Parse the given {@link Bundle} into the given {@link EntityDelta} state,
512d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * assuming the extras defined through {@link Intents}.
513d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     */
51469f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov    public static void parseExtras(Context context, AccountType accountType, EntityDelta state,
515aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey            Bundle extras) {
516d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey        if (extras == null || extras.size() == 0) {
517d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey            // Bail early if no useful data
518d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey            return;
519d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey        }
520d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey
521c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        parseStructuredNameExtra(context, accountType, state, extras);
522c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        parseStructuredPostalExtra(accountType, state, extras);
523d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
524d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        {
525d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            // Phone
52669f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov            final DataKind kind = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
527d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            parseExtras(state, kind, extras, Insert.PHONE_TYPE, Insert.PHONE, Phone.NUMBER);
528d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            parseExtras(state, kind, extras, Insert.SECONDARY_PHONE_TYPE, Insert.SECONDARY_PHONE,
529d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                    Phone.NUMBER);
530d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            parseExtras(state, kind, extras, Insert.TERTIARY_PHONE_TYPE, Insert.TERTIARY_PHONE,
531d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                    Phone.NUMBER);
532d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        }
533d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
534d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        {
535d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            // Email
53669f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov            final DataKind kind = accountType.getKindForMimetype(Email.CONTENT_ITEM_TYPE);
537d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey            parseExtras(state, kind, extras, Insert.EMAIL_TYPE, Insert.EMAIL, Email.DATA);
538d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            parseExtras(state, kind, extras, Insert.SECONDARY_EMAIL_TYPE, Insert.SECONDARY_EMAIL,
539d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                    Email.DATA);
540d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            parseExtras(state, kind, extras, Insert.TERTIARY_EMAIL_TYPE, Insert.TERTIARY_EMAIL,
541d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                    Email.DATA);
542d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        }
543d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
544d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        {
545d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            // Im
54669f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov            final DataKind kind = accountType.getKindForMimetype(Im.CONTENT_ITEM_TYPE);
547e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            fixupLegacyImType(extras);
548d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            parseExtras(state, kind, extras, Insert.IM_PROTOCOL, Insert.IM_HANDLE, Im.DATA);
549d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        }
55016c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey
55116c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey        // Organization
55216c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey        final boolean hasOrg = extras.containsKey(Insert.COMPANY)
55316c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey                || extras.containsKey(Insert.JOB_TITLE);
55469f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov        final DataKind kindOrg = accountType.getKindForMimetype(Organization.CONTENT_ITEM_TYPE);
55516c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey        if (hasOrg && EntityModifier.canInsert(state, kindOrg)) {
55616c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            final ValuesDelta child = EntityModifier.insertChild(state, kindOrg);
55716c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey
55816c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            final String company = extras.getString(Insert.COMPANY);
55916c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            if (ContactsUtils.isGraphic(company)) {
56016c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey                child.put(Organization.COMPANY, company);
56116c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            }
56216c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey
56316c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            final String title = extras.getString(Insert.JOB_TITLE);
56416c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            if (ContactsUtils.isGraphic(title)) {
56516c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey                child.put(Organization.TITLE, title);
56616c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            }
56716c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey        }
56816c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey
56916c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey        // Notes
57016c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey        final boolean hasNotes = extras.containsKey(Insert.NOTES);
57169f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov        final DataKind kindNotes = accountType.getKindForMimetype(Note.CONTENT_ITEM_TYPE);
57216c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey        if (hasNotes && EntityModifier.canInsert(state, kindNotes)) {
57316c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            final ValuesDelta child = EntityModifier.insertChild(state, kindNotes);
57416c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey
57516c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            final String notes = extras.getString(Insert.NOTES);
57616c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            if (ContactsUtils.isGraphic(notes)) {
57716c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey                child.put(Note.NOTE, notes);
57816c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey            }
57916c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey        }
5806a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov
5816a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov        // Arbitrary additional data
582916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        ArrayList<ContentValues> values = extras.getParcelableArrayList(Insert.DATA);
583916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        if (values != null) {
58469f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov            parseValues(state, accountType, values);
585916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        }
586916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    }
587916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
588c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov    private static void parseStructuredNameExtra(
589c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            Context context, AccountType accountType, EntityDelta state, Bundle extras) {
590c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        // StructuredName
591c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        EntityModifier.ensureKindExists(state, accountType, StructuredName.CONTENT_ITEM_TYPE);
592c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        final ValuesDelta child = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
593c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov
594c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        final String name = extras.getString(Insert.NAME);
595c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        if (ContactsUtils.isGraphic(name)) {
596c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            final DataKind kind = accountType.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE);
597c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            boolean supportsDisplayName = false;
598c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            if (kind.fieldList != null) {
599c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                for (EditField field : kind.fieldList) {
600c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                    if (StructuredName.DISPLAY_NAME.equals(field.column)) {
601c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        supportsDisplayName = true;
602c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        break;
603c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                    }
604c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                }
605c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            }
606c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov
607c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            if (supportsDisplayName) {
608c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                child.put(StructuredName.DISPLAY_NAME, name);
609c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            } else {
610c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                Uri uri = ContactsContract.AUTHORITY_URI.buildUpon()
611c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        .appendPath("complete_name")
612c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        .appendQueryParameter(StructuredName.DISPLAY_NAME, name)
613c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        .build();
614c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                Cursor cursor = context.getContentResolver().query(uri,
615c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        new String[]{
616c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                                StructuredName.PREFIX,
617c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                                StructuredName.GIVEN_NAME,
618c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                                StructuredName.MIDDLE_NAME,
619c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                                StructuredName.FAMILY_NAME,
620c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                                StructuredName.SUFFIX,
621c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        }, null, null, null);
622c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov
623c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                try {
624c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                    if (cursor.moveToFirst()) {
625c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        child.put(StructuredName.PREFIX, cursor.getString(0));
626c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        child.put(StructuredName.GIVEN_NAME, cursor.getString(1));
627c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        child.put(StructuredName.MIDDLE_NAME, cursor.getString(2));
628c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        child.put(StructuredName.FAMILY_NAME, cursor.getString(3));
629c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        child.put(StructuredName.SUFFIX, cursor.getString(4));
630c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                    }
631c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                } finally {
632c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                    cursor.close();
633c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                }
634c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            }
635c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        }
636c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov
637c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        final String phoneticName = extras.getString(Insert.PHONETIC_NAME);
638c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        if (ContactsUtils.isGraphic(phoneticName)) {
639c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            child.put(StructuredName.PHONETIC_GIVEN_NAME, phoneticName);
640c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        }
641c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov    }
642c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov
643c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov    private static void parseStructuredPostalExtra(
644c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            AccountType accountType, EntityDelta state, Bundle extras) {
645c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        // StructuredPostal
646c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        final DataKind kind = accountType.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE);
647c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        final ValuesDelta child = parseExtras(state, kind, extras, Insert.POSTAL_TYPE,
648c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                Insert.POSTAL, StructuredPostal.FORMATTED_ADDRESS);
649c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        String address = child == null ? null
650c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                : child.getAsString(StructuredPostal.FORMATTED_ADDRESS);
651c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        if (!TextUtils.isEmpty(address)) {
652c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            boolean supportsFormatted = false;
653c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            if (kind.fieldList != null) {
654c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                for (EditField field : kind.fieldList) {
655c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                    if (StructuredPostal.FORMATTED_ADDRESS.equals(field.column)) {
656c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        supportsFormatted = true;
657c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                        break;
658c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                    }
659c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                }
660c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            }
661c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov
662c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            if (!supportsFormatted) {
663c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                child.put(StructuredPostal.STREET, address);
664c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov                child.putNull(StructuredPostal.FORMATTED_ADDRESS);
665c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov            }
666c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        }
667c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov    }
668c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov
669916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    private static void parseValues(
67069f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov            EntityDelta state, AccountType accountType, ArrayList<ContentValues> dataValueList) {
671916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        for (ContentValues values : dataValueList) {
672916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            String mimeType = values.getAsString(Data.MIMETYPE);
673916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            if (TextUtils.isEmpty(mimeType)) {
674916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                Log.e(TAG, "Mimetype is required. Ignoring: " + values);
675916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                continue;
676916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            }
677916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
678916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            // Won't override the contact name
679916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
680916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                continue;
681916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            }
682916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
68369f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov            DataKind kind = accountType.getKindForMimetype(mimeType);
684916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            if (kind == null) {
6852b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro                Log.e(TAG, "Mimetype not supported for account type "
6862b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro                        + accountType.getAccountTypeAndDataSet() + ". Ignoring: " + values);
687916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                continue;
688916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            }
689916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
690916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            ValuesDelta entry = ValuesDelta.fromAfter(values);
691916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            if (isEmpty(entry, kind)) {
692916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                continue;
693916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            }
694916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
695916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType);
696916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
69750a27b70581b0191995969c63edd1f6a3db3d1b7Makoto Onuki            if ((kind.typeOverallMax != 1) || GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
698916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                // Check for duplicates
699916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                boolean addEntry = true;
700916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                int count = 0;
701916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                if (entries != null && entries.size() > 0) {
702916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    for (ValuesDelta delta : entries) {
703916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                        if (!delta.isDelete()) {
704916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            if (areEqual(delta, values, kind)) {
705916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                                addEntry = false;
706916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                                break;
707916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            }
708916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            count++;
709916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                        }
710916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    }
711916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                }
712916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
713916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                if (kind.typeOverallMax != -1 && count >= kind.typeOverallMax) {
714916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    Log.e(TAG, "Mimetype allows at most " + kind.typeOverallMax
715916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            + " entries. Ignoring: " + values);
716916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    addEntry = false;
717916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                }
718916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
719916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                if (addEntry) {
720916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    addEntry = adjustType(entry, entries, kind);
721916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                }
722916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
723916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                if (addEntry) {
724916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    state.addEntry(entry);
725916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                }
726916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            } else {
727916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                // Non-list entries should not be overridden
728916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                boolean addEntry = true;
729916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                if (entries != null && entries.size() > 0) {
730916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    for (ValuesDelta delta : entries) {
731916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                        if (!delta.isDelete() && !isEmpty(delta, kind)) {
732916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            addEntry = false;
733916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            break;
734916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                        }
735916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    }
736916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    if (addEntry) {
737916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                        for (ValuesDelta delta : entries) {
738916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            delta.markDeleted();
739916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                        }
740916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    }
741916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                }
742916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
743916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                if (addEntry) {
744916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    addEntry = adjustType(entry, entries, kind);
745916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                }
746916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
747916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                if (addEntry) {
748916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    state.addEntry(entry);
749916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType)){
750916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    // Note is most likely to contain large amounts of text
751916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    // that we don't want to drop on the ground.
752916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    for (ValuesDelta delta : entries) {
753916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                        if (!isEmpty(delta, kind)) {
754916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            delta.put(Note.NOTE, delta.getAsString(Note.NOTE) + "\n"
755916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                                    + values.getAsString(Note.NOTE));
756916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            break;
757916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                        }
758916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    }
759916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                } else {
760916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                    Log.e(TAG, "Will not override mimetype " + mimeType + ". Ignoring: "
761916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                            + values);
762916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                }
763916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            }
764916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        }
765916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    }
766916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
767916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    /**
768916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     * Checks if the data kind allows addition of another entry (e.g. Exchange only
769916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     * supports two "work" phone numbers).  If not, tries to switch to one of the
770916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     * unused types.  If successful, returns true.
771916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     */
772916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    private static boolean adjustType(
773916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            ValuesDelta entry, ArrayList<ValuesDelta> entries, DataKind kind) {
774916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        if (kind.typeColumn == null || kind.typeList == null || kind.typeList.size() == 0) {
775916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            return true;
776916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        }
777916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
778916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        Integer typeInteger = entry.getAsInteger(kind.typeColumn);
779916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        int type = typeInteger != null ? typeInteger : kind.typeList.get(0).rawValue;
780916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
781916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        if (isTypeAllowed(type, entries, kind)) {
782916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            entry.put(kind.typeColumn, type);
783916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            return true;
784916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        }
785916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
786916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        // Specified type is not allowed - choose the first available type that is allowed
787916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        int size = kind.typeList.size();
788916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        for (int i = 0; i < size; i++) {
789916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            EditType editType = kind.typeList.get(i);
790916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            if (isTypeAllowed(editType.rawValue, entries, kind)) {
791916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                entry.put(kind.typeColumn, editType.rawValue);
792916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                return true;
793916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            }
7946a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov        }
795916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
796916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        return false;
797916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    }
798916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
799916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    /**
800916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     * Checks if a new entry of the specified type can be added to the raw
801916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     * contact. For example, Exchange only supports two "work" phone numbers, so
802916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     * addition of a third would not be allowed.
803916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     */
804916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    private static boolean isTypeAllowed(int type, ArrayList<ValuesDelta> entries, DataKind kind) {
805916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        int max = 0;
806916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        int size = kind.typeList.size();
807916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        for (int i = 0; i < size; i++) {
808916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            EditType editType = kind.typeList.get(i);
809916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            if (editType.rawValue == type) {
810916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                max = editType.specificMax;
811916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                break;
812916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            }
813916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        }
814916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
815916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        if (max == 0) {
816916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            // This type is not allowed at all
817916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            return false;
818916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        }
819916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
820916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        if (max == -1) {
821916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            // Unlimited instances of this type are allowed
822916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            return true;
823916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        }
824916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov
825916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        return getEntryCountByType(entries, kind.typeColumn, type) < max;
8266a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov    }
8276a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov
828916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    /**
829916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     * Counts occurrences of the specified type in the supplied entry list.
830916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov     */
831916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov    private static int getEntryCountByType(
832916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            ArrayList<ValuesDelta> entries, String typeColumn, int type) {
833916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        int count = 0;
834916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        int size = entries.size();
835916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        for (int i = 0; i < size; i++) {
836916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            Integer typeInteger = entries.get(i).getAsInteger(typeColumn);
837916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            if (typeInteger != null && typeInteger == type) {
838916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov                count++;
839916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov            }
8406a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov        }
841916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov        return count;
842d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey    }
843d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
844d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey    /**
845e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey     * Attempt to parse legacy {@link Insert#IM_PROTOCOL} values, replacing them
846e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey     * with updated values.
847e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey     */
848c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov    @SuppressWarnings("deprecation")
849e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey    private static void fixupLegacyImType(Bundle bundle) {
850e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey        final String encodedString = bundle.getString(Insert.IM_PROTOCOL);
851e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey        if (encodedString == null) return;
852e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey
853e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey        try {
854e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            final Object protocol = android.provider.Contacts.ContactMethods
855e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey                    .decodeImProtocol(encodedString);
856e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            if (protocol instanceof Integer) {
857e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey                bundle.putInt(Insert.IM_PROTOCOL, (Integer)protocol);
858e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            } else {
859e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey                bundle.putString(Insert.IM_PROTOCOL, (String)protocol);
860e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            }
861e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey        } catch (IllegalArgumentException e) {
862e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            // Ignore exception when legacy parser fails
863e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey        }
864e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey    }
865e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey
866e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey    /**
867d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * Parse a specific entry from the given {@link Bundle} and insert into the
868d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * given {@link EntityDelta}. Silently skips the insert when missing value
869d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * or no valid {@link EditType} found.
870d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     *
871d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * @param typeExtra {@link Bundle} key that holds the incoming
872d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     *            {@link EditType#rawValue} value.
873d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * @param valueExtra {@link Bundle} key that holds the incoming value.
874d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * @param valueColumn Column to write value into {@link ValuesDelta}.
875d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     */
876c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov    public static ValuesDelta parseExtras(EntityDelta state, DataKind kind, Bundle extras,
877d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            String typeExtra, String valueExtra, String valueColumn) {
8787f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        final CharSequence value = extras.getCharSequence(valueExtra);
8797f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey
88069f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov        // Bail early if account type doesn't handle this MIME type
881c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        if (kind == null) return null;
882d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
883d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        // Bail when can't insert type, or value missing
884d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        final boolean canInsert = EntityModifier.canInsert(state, kind);
885d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey        final boolean validValue = (value != null && TextUtils.isGraphic(value));
886c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        if (!validValue || !canInsert) return null;
887d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
8886164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey        // Find exact type when requested, otherwise best available type
8896164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey        final boolean hasType = extras.containsKey(typeExtra);
8906164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey        final int typeValue = extras.getInt(typeExtra, hasType ? BaseTypes.TYPE_CUSTOM
8916164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey                : Integer.MIN_VALUE);
892d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        final EditType editType = EntityModifier.getBestValidType(state, kind, true, typeValue);
893d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
894d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        // Create data row and fill with value
895d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        final ValuesDelta child = EntityModifier.insertChild(state, kind, editType);
8967f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        child.put(valueColumn, value.toString());
897d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
898d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        if (editType != null && editType.customColumn != null) {
899d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            // Write down label when custom type picked
900e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            final String customType = extras.getString(typeExtra);
901e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            child.put(editType.customColumn, customType);
902d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        }
903c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov
904c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov        return child;
905d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey    }
906d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
907d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    /**
908d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     * Generic mime types with type support (e.g. TYPE_HOME).
909d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     * Here, "type support" means if the data kind has CommonColumns#TYPE or not. Data kinds which
910d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     * have their own migrate methods aren't listed here.
911d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     */
912d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    private static final Set<String> sGenericMimeTypesWithTypeSupport = new HashSet<String>(
913d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            Arrays.asList(Phone.CONTENT_ITEM_TYPE,
914d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    Email.CONTENT_ITEM_TYPE,
915d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    Im.CONTENT_ITEM_TYPE,
916d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    Nickname.CONTENT_ITEM_TYPE,
917d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    Website.CONTENT_ITEM_TYPE,
918d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    Relation.CONTENT_ITEM_TYPE,
919d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    SipAddress.CONTENT_ITEM_TYPE));
920d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    private static final Set<String> sGenericMimeTypesWithoutTypeSupport = new HashSet<String>(
921d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            Arrays.asList(Organization.CONTENT_ITEM_TYPE,
922d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    Note.CONTENT_ITEM_TYPE,
923d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    Photo.CONTENT_ITEM_TYPE,
924d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    GroupMembership.CONTENT_ITEM_TYPE));
925d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    // CommonColumns.TYPE cannot be accessed as it is protected interface, so use
926d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    // Phone.TYPE instead.
927d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    private static final String COLUMN_FOR_TYPE  = Phone.TYPE;
928d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    private static final String COLUMN_FOR_LABEL  = Phone.LABEL;
929d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    private static final int TYPE_CUSTOM = Phone.TYPE_CUSTOM;
930d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
931d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    /**
932d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     * Migrates old EntityDelta to newly created one with a new restriction supplied from
933d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     * newAccountType.
934d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     *
935d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     * This is only for account switch during account creation (which must be insert operation).
936d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     */
937d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    public static void migrateStateForNewContact(Context context,
938d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            EntityDelta oldState, EntityDelta newState,
939d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            AccountType oldAccountType, AccountType newAccountType) {
940d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (newAccountType == oldAccountType) {
941d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            // Just copying all data in oldState isn't enough, but we can still rely on a lot of
942d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            // shortcuts.
943d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            for (DataKind kind : newAccountType.getSortedDataKinds()) {
944d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                final String mimeType = kind.mimeType;
945d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                // The fields with short/long form capability must be treated properly.
946d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
947d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    migrateStructuredName(context, oldState, newState, kind);
948d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else {
949d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    List<ValuesDelta> entryList = oldState.getMimeEntries(mimeType);
950d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    if (entryList != null && !entryList.isEmpty()) {
951d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        for (ValuesDelta entry : entryList) {
952d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                            ContentValues values = entry.getAfter();
953d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                            if (values != null) {
954d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                newState.addEntry(ValuesDelta.fromAfter(values));
955d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                            }
956d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        }
957d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    }
958d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
959d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
960d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        } else {
961d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            // Migrate data supported by the new account type.
962d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            // All the other data inside oldState are silently dropped.
963d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            for (DataKind kind : newAccountType.getSortedDataKinds()) {
9642b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann                if (!kind.editable) continue;
965d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                final String mimeType = kind.mimeType;
966d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME.equals(mimeType)
967d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        || DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
9682b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann                    // Ignore pseudo data.
969d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    continue;
970d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
971d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    migrateStructuredName(context, oldState, newState, kind);
972d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) {
973d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    migratePostal(oldState, newState, kind);
974d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType)) {
975d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    migrateEvent(oldState, newState, kind, null /* default Year */);
976d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else if (sGenericMimeTypesWithoutTypeSupport.contains(mimeType)) {
977d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    migrateGenericWithoutTypeColumn(oldState, newState, kind);
9782b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann                } else if (sGenericMimeTypesWithTypeSupport.contains(mimeType)) {
979d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    migrateGenericWithTypeColumn(oldState, newState, kind);
9802b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann                } else {
9812b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann                    throw new IllegalStateException("Unexpected editable mime-type: " + mimeType);
982d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
983d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
984d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
985d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    }
986d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
987d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    /**
988d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     * Checks {@link DataKind#isList} and {@link DataKind#typeOverallMax}, and restricts
989d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     * the number of entries (ValuesDelta) inside newState.
990d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa     */
991d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    private static ArrayList<ValuesDelta> ensureEntryMaxSize(EntityDelta newState, DataKind kind,
992d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            ArrayList<ValuesDelta> mimeEntries) {
993d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (mimeEntries == null) {
994d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            return null;
995d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
996d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
99750a27b70581b0191995969c63edd1f6a3db3d1b7Makoto Onuki        final int typeOverallMax = kind.typeOverallMax;
998d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (typeOverallMax >= 0 && (mimeEntries.size() > typeOverallMax)) {
999d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            ArrayList<ValuesDelta> newMimeEntries = new ArrayList<ValuesDelta>(typeOverallMax);
1000d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            for (int i = 0; i < typeOverallMax; i++) {
1001d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                newMimeEntries.add(mimeEntries.get(i));
1002d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1003d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            mimeEntries = newMimeEntries;
1004d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1005d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        return mimeEntries;
1006d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    }
1007d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1008d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    /** @hide Public only for testing. */
1009d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    public static void migrateStructuredName(
1010d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            Context context, EntityDelta oldState, EntityDelta newState, DataKind newDataKind) {
1011d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final ContentValues values =
1012d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                oldState.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE).getAfter();
1013d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (values == null) {
1014d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            return;
1015d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1016d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1017d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        boolean supportDisplayName = false;
1018d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        boolean supportPhoneticFullName = false;
1019d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        boolean supportPhoneticFamilyName = false;
1020d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        boolean supportPhoneticMiddleName = false;
1021d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        boolean supportPhoneticGivenName = false;
1022d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        for (EditField editField : newDataKind.fieldList) {
1023d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (StructuredName.DISPLAY_NAME.equals(editField.column)) {
1024d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                supportDisplayName = true;
1025d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1026d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (DataKind.PSEUDO_COLUMN_PHONETIC_NAME.equals(editField.column)) {
1027d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                supportPhoneticFullName = true;
1028d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1029d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (StructuredName.PHONETIC_FAMILY_NAME.equals(editField.column)) {
1030d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                supportPhoneticFamilyName = true;
1031d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1032d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (StructuredName.PHONETIC_MIDDLE_NAME.equals(editField.column)) {
1033d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                supportPhoneticMiddleName = true;
1034d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1035d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (StructuredName.PHONETIC_GIVEN_NAME.equals(editField.column)) {
1036d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                supportPhoneticGivenName = true;
1037d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1038d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1039d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1040d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // DISPLAY_NAME <-> PREFIX, GIVEN_NAME, MIDDLE_NAME, FAMILY_NAME, SUFFIX
1041d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final String displayName = values.getAsString(StructuredName.DISPLAY_NAME);
1042d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (!TextUtils.isEmpty(displayName)) {
1043d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (!supportDisplayName) {
1044d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                // Old data has a display name, while the new account doesn't allow it.
1045da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro                NameConverter.displayNameToStructuredName(context, displayName, values);
1046d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1047d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                // We don't want to migrate unseen data which may confuse users after the creation.
1048d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                values.remove(StructuredName.DISPLAY_NAME);
1049d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1050d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        } else {
1051d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (supportDisplayName) {
1052d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                // Old data does not have display name, while the new account requires it.
1053d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                values.put(StructuredName.DISPLAY_NAME,
1054da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro                        NameConverter.structuredNameToDisplayName(context, values));
1055da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro                for (String field : NameConverter.STRUCTURED_NAME_FIELDS) {
1056da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro                    values.remove(field);
1057da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro                }
1058d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1059d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1060d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1061d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // Phonetic (full) name <-> PHONETIC_FAMILY_NAME, PHONETIC_MIDDLE_NAME, PHONETIC_GIVEN_NAME
1062d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final String phoneticFullName = values.getAsString(DataKind.PSEUDO_COLUMN_PHONETIC_NAME);
1063d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (!TextUtils.isEmpty(phoneticFullName)) {
1064d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (!supportPhoneticFullName) {
1065d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                // Old data has a phonetic (full) name, while the new account doesn't allow it.
1066d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                final ContentValues tmpValues =
1067d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        PhoneticNameEditorView.parsePhoneticName(phoneticFullName, null);
1068d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                values.remove(DataKind.PSEUDO_COLUMN_PHONETIC_NAME);
1069d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (supportPhoneticFamilyName) {
1070d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.put(StructuredName.PHONETIC_FAMILY_NAME,
1071d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                            tmpValues.getAsString(StructuredName.PHONETIC_FAMILY_NAME));
1072d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else {
1073d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredName.PHONETIC_FAMILY_NAME);
1074d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1075d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (supportPhoneticMiddleName) {
1076d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.put(StructuredName.PHONETIC_MIDDLE_NAME,
1077d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                            tmpValues.getAsString(StructuredName.PHONETIC_MIDDLE_NAME));
1078d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else {
1079d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredName.PHONETIC_MIDDLE_NAME);
1080d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1081d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (supportPhoneticGivenName) {
1082d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.put(StructuredName.PHONETIC_GIVEN_NAME,
1083d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                            tmpValues.getAsString(StructuredName.PHONETIC_GIVEN_NAME));
1084d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else {
1085d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredName.PHONETIC_GIVEN_NAME);
1086d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1087d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1088d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        } else {
1089d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (supportPhoneticFullName) {
1090d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                // Old data does not have a phonetic (full) name, while the new account requires it.
1091d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                values.put(DataKind.PSEUDO_COLUMN_PHONETIC_NAME,
1092d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        PhoneticNameEditorView.buildPhoneticName(
1093d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredName.PHONETIC_FAMILY_NAME),
1094d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredName.PHONETIC_MIDDLE_NAME),
1095d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredName.PHONETIC_GIVEN_NAME)));
1096d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1097d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (!supportPhoneticFamilyName) {
1098d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                values.remove(StructuredName.PHONETIC_FAMILY_NAME);
1099d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1100d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (!supportPhoneticMiddleName) {
1101d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                values.remove(StructuredName.PHONETIC_MIDDLE_NAME);
1102d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1103d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (!supportPhoneticGivenName) {
1104d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                values.remove(StructuredName.PHONETIC_GIVEN_NAME);
1105d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1106d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1107d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1108d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        newState.addEntry(ValuesDelta.fromAfter(values));
1109d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    }
1110d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1111d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    /** @hide Public only for testing. */
1112d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    public static void migratePostal(EntityDelta oldState, EntityDelta newState,
1113d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            DataKind newDataKind) {
1114d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind,
1115d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                oldState.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE));
1116d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (mimeEntries == null || mimeEntries.isEmpty()) {
1117d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            return;
1118d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1119d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1120d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        boolean supportFormattedAddress = false;
1121d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        boolean supportStreet = false;
1122d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final String firstColumn = newDataKind.fieldList.get(0).column;
1123d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        for (EditField editField : newDataKind.fieldList) {
1124d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (StructuredPostal.FORMATTED_ADDRESS.equals(editField.column)) {
1125d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                supportFormattedAddress = true;
1126d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1127d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (StructuredPostal.STREET.equals(editField.column)) {
1128d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                supportStreet = true;
1129d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1130d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1131d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1132d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final Set<Integer> supportedTypes = new HashSet<Integer>();
1133d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (newDataKind.typeList != null && !newDataKind.typeList.isEmpty()) {
1134d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            for (EditType editType : newDataKind.typeList) {
1135d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                supportedTypes.add(editType.rawValue);
1136d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1137d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1138d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1139d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        for (ValuesDelta entry : mimeEntries) {
1140d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            final ContentValues values = entry.getAfter();
1141d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (values == null) {
1142d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                continue;
1143d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1144d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            final Integer oldType = values.getAsInteger(StructuredPostal.TYPE);
1145d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (!supportedTypes.contains(oldType)) {
1146d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                int defaultType;
1147d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (newDataKind.defaultValues != null) {
1148d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    defaultType = newDataKind.defaultValues.getAsInteger(StructuredPostal.TYPE);
1149d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else {
1150d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    defaultType = newDataKind.typeList.get(0).rawValue;
1151d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1152d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                values.put(StructuredPostal.TYPE, defaultType);
1153d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (oldType != null && oldType == StructuredPostal.TYPE_CUSTOM) {
1154d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredPostal.LABEL);
1155d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1156d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1157d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1158d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            final String formattedAddress = values.getAsString(StructuredPostal.FORMATTED_ADDRESS);
1159d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (!TextUtils.isEmpty(formattedAddress)) {
1160d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (!supportFormattedAddress) {
1161d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    // Old data has a formatted address, while the new account doesn't allow it.
1162d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredPostal.FORMATTED_ADDRESS);
1163d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1164d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    // Unlike StructuredName we don't have logic to split it, so first
1165d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    // try to use street field and. If the new account doesn't have one,
1166d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    // then select first one anyway.
1167d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    if (supportStreet) {
1168d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        values.put(StructuredPostal.STREET, formattedAddress);
1169d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    } else {
1170d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        values.put(firstColumn, formattedAddress);
1171d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    }
1172d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1173d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            } else {
1174d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (supportFormattedAddress) {
1175d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    // Old data does not have formatted address, while the new account requires it.
1176d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    // Unlike StructuredName we don't have logic to join multiple address values.
1177d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    // Use poor join heuristics for now.
1178d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    String[] structuredData;
1179d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    final boolean useJapaneseOrder =
1180d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                            Locale.JAPANESE.getLanguage().equals(Locale.getDefault().getLanguage());
1181d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    if (useJapaneseOrder) {
1182d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        structuredData = new String[] {
1183d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.COUNTRY),
1184d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.POSTCODE),
1185d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.REGION),
1186d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.CITY),
1187d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.NEIGHBORHOOD),
1188d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.STREET),
1189d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.POBOX) };
1190d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    } else {
1191d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        structuredData = new String[] {
1192d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.POBOX),
1193d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.STREET),
1194d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.NEIGHBORHOOD),
1195d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.CITY),
1196d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.REGION),
1197d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.POSTCODE),
1198d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                values.getAsString(StructuredPostal.COUNTRY) };
1199d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    }
1200d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    final StringBuilder builder = new StringBuilder();
1201d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    for (String elem : structuredData) {
1202d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        if (!TextUtils.isEmpty(elem)) {
1203d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                            builder.append(elem + "\n");
1204d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        }
1205d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    }
1206d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.put(StructuredPostal.FORMATTED_ADDRESS, builder.toString());
1207d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1208d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredPostal.POBOX);
1209d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredPostal.STREET);
1210d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredPostal.NEIGHBORHOOD);
1211d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredPostal.CITY);
1212d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredPostal.REGION);
1213d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredPostal.POSTCODE);
1214d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(StructuredPostal.COUNTRY);
1215d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1216d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1217d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1218d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            newState.addEntry(ValuesDelta.fromAfter(values));
1219d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1220d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    }
1221d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1222d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    /** @hide Public only for testing. */
1223d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    public static void migrateEvent(EntityDelta oldState, EntityDelta newState,
1224d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            DataKind newDataKind, Integer defaultYear) {
1225d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind,
1226d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                oldState.getMimeEntries(Event.CONTENT_ITEM_TYPE));
1227d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (mimeEntries == null || mimeEntries.isEmpty()) {
1228d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            return;
1229d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1230d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1231d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final Map<Integer, EventEditType> allowedTypes = new HashMap<Integer, EventEditType>();
1232d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        for (EditType editType : newDataKind.typeList) {
1233d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            allowedTypes.put(editType.rawValue, (EventEditType) editType);
1234d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1235d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        for (ValuesDelta entry : mimeEntries) {
1236d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            final ContentValues values = entry.getAfter();
1237d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (values == null) {
1238d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                continue;
1239d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1240d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            final String dateString = values.getAsString(Event.START_DATE);
1241d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            final Integer type = values.getAsInteger(Event.TYPE);
1242d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (type != null && allowedTypes.containsKey(type) && !TextUtils.isEmpty(dateString)) {
1243d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                EventEditType suitableType = allowedTypes.get(type);
1244d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1245d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                final ParsePosition position = new ParsePosition(0);
1246d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                boolean yearOptional = false;
1247d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                Date date = DateUtils.DATE_AND_TIME_FORMAT.parse(dateString, position);
1248d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (date == null) {
1249d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    yearOptional = true;
1250d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    date = DateUtils.NO_YEAR_DATE_FORMAT.parse(dateString, position);
1251d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1252d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (date != null) {
1253d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    if (yearOptional && !suitableType.isYearOptional()) {
1254d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        // The new EditType doesn't allow optional year. Supply default.
1255d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        final Calendar calendar = Calendar.getInstance(DateUtils.UTC_TIMEZONE,
1256d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                Locale.US);
1257d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        if (defaultYear == null) {
1258d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                            defaultYear = calendar.get(Calendar.YEAR);
1259d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        }
1260d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        calendar.setTime(date);
1261d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        final int month = calendar.get(Calendar.MONTH);
1262d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        final int day = calendar.get(Calendar.DAY_OF_MONTH);
1263d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        // Exchange requires 8:00 for birthdays
1264d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        calendar.set(defaultYear, month, day,
1265d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                EventFieldEditorView.getDefaultHourForBirthday(), 0, 0);
1266d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        values.put(Event.START_DATE,
1267d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                                DateUtils.FULL_DATE_FORMAT.format(calendar.getTime()));
1268d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    }
1269d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1270d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                newState.addEntry(ValuesDelta.fromAfter(values));
1271d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            } else {
1272d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                // Just drop it.
1273d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1274d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1275d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    }
1276d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1277d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    /** @hide Public only for testing. */
1278d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    public static void migrateGenericWithoutTypeColumn(
1279d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            EntityDelta oldState, EntityDelta newState, DataKind newDataKind) {
1280d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind,
1281d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                oldState.getMimeEntries(newDataKind.mimeType));
1282d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (mimeEntries == null || mimeEntries.isEmpty()) {
1283d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            return;
1284d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1285d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1286d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        for (ValuesDelta entry : mimeEntries) {
1287d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            ContentValues values = entry.getAfter();
1288d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (values != null) {
1289d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                newState.addEntry(ValuesDelta.fromAfter(values));
1290d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1291d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1292d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    }
1293d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1294d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    /** @hide Public only for testing. */
1295d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    public static void migrateGenericWithTypeColumn(
1296d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            EntityDelta oldState, EntityDelta newState, DataKind newDataKind) {
1297d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final ArrayList<ValuesDelta> mimeEntries = oldState.getMimeEntries(newDataKind.mimeType);
1298d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (mimeEntries == null || mimeEntries.isEmpty()) {
1299d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            return;
1300d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1301d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1302d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // Note that type specified with the old account may be invalid with the new account, while
1303d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // we want to preserve its data as much as possible. e.g. if a user typed a phone number
1304d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // with a type which is valid with an old account but not with a new account, the user
1305d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // probably wants to have the number with default type, rather than seeing complete data
1306d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // loss.
1307d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        //
1308d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // Specifically, this method works as follows:
1309d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // 1. detect defaultType
1310d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // 2. prepare constants & variables for iteration
1311d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // 3. iterate over mimeEntries:
1312d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // 3.1 stop iteration if total number of mimeEntries reached typeOverallMax specified in
1313d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        //     DataKind
1314d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // 3.2 replace unallowed types with defaultType
1315d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // 3.3 check if the number of entries is below specificMax specified in AccountType
1316d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1317d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // Here, defaultType can be supplied in two ways
1318d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // - via kind.defaultValues
1319d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // - via kind.typeList.get(0).rawValue
1320d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        Integer defaultType = null;
1321d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (newDataKind.defaultValues != null) {
1322d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            defaultType = newDataKind.defaultValues.getAsInteger(COLUMN_FOR_TYPE);
1323d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1324d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final Set<Integer> allowedTypes = new HashSet<Integer>();
1325d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // key: type, value: the number of entries allowed for the type (specificMax)
1326d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final Map<Integer, Integer> typeSpecificMaxMap = new HashMap<Integer, Integer>();
1327d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (defaultType != null) {
1328d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            allowedTypes.add(defaultType);
1329d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            typeSpecificMaxMap.put(defaultType, -1);
1330d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1331d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // Note: typeList may be used in different purposes when defaultValues are specified.
1332d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // Especially in IM, typeList contains available protocols (e.g. PROTOCOL_GOOGLE_TALK)
1333d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // instead of "types" which we want to treate here (e.g. TYPE_HOME). So we don't add
1334d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // anything other than defaultType into allowedTypes and typeSpecificMapMax.
1335d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (!Im.CONTENT_ITEM_TYPE.equals(newDataKind.mimeType) &&
1336d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                newDataKind.typeList != null && !newDataKind.typeList.isEmpty()) {
1337d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            for (EditType editType : newDataKind.typeList) {
1338d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                allowedTypes.add(editType.rawValue);
1339d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                typeSpecificMaxMap.put(editType.rawValue, editType.specificMax);
1340d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1341d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (defaultType == null) {
1342d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                defaultType = newDataKind.typeList.get(0).rawValue;
1343d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1344d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1345d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1346d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        if (defaultType == null) {
1347d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            Log.w(TAG, "Default type isn't available for mimetype " + newDataKind.mimeType);
1348d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1349d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
135050a27b70581b0191995969c63edd1f6a3db3d1b7Makoto Onuki        final int typeOverallMax = newDataKind.typeOverallMax;
1351d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1352d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        // key: type, value: the number of current entries.
1353d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        final Map<Integer, Integer> currentEntryCount = new HashMap<Integer, Integer>();
1354d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        int totalCount = 0;
1355d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1356d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        for (ValuesDelta entry : mimeEntries) {
1357d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (typeOverallMax != -1 && totalCount >= typeOverallMax) {
1358d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                break;
1359d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1360d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1361d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            final ContentValues values = entry.getAfter();
1362d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (values == null) {
1363d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                continue;
1364d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1365d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa
1366d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            final Integer oldType = entry.getAsInteger(COLUMN_FOR_TYPE);
1367d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            final Integer typeForNewAccount;
1368d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (!allowedTypes.contains(oldType)) {
1369d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                // The new account doesn't support the type.
1370d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (defaultType != null) {
1371d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    typeForNewAccount = defaultType.intValue();
1372d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.put(COLUMN_FOR_TYPE, defaultType.intValue());
1373d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    if (oldType != null && oldType == TYPE_CUSTOM) {
1374d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        values.remove(COLUMN_FOR_LABEL);
1375d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    }
1376d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                } else {
1377d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    typeForNewAccount = null;
1378d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    values.remove(COLUMN_FOR_TYPE);
1379d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1380d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            } else {
1381d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                typeForNewAccount = oldType;
1382d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1383d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            if (typeForNewAccount != null) {
1384d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                final int specificMax = (typeSpecificMaxMap.containsKey(typeForNewAccount) ?
1385d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        typeSpecificMaxMap.get(typeForNewAccount) : 0);
1386d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                if (specificMax >= 0) {
1387d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    final int currentCount = (currentEntryCount.get(typeForNewAccount) != null ?
1388d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                            currentEntryCount.get(typeForNewAccount) : 0);
1389d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    if (currentCount >= specificMax) {
1390d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                        continue;
1391d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    }
1392d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                    currentEntryCount.put(typeForNewAccount, currentCount + 1);
1393d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa                }
1394d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            }
1395d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            newState.addEntry(ValuesDelta.fromAfter(values));
1396d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa            totalCount++;
1397d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa        }
1398d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa    }
13992ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey}
1400