15ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee/*
25ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Copyright (C) 2009 The Android Open Source Project
35ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee *
45ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Licensed under the Apache License, Version 2.0 (the "License");
55ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * you may not use this file except in compliance with the License.
65ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * You may obtain a copy of the License at
75ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee *
85ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee *      http://www.apache.org/licenses/LICENSE-2.0
95ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee *
105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Unless required by applicable law or agreed to in writing, software
115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * distributed under the License is distributed on an "AS IS" BASIS,
125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * See the License for the specific language governing permissions and
145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * limitations under the License.
155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */
165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leepackage com.android.contacts.common.model;
185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.content.ContentValues;
205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.content.Context;
215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.database.Cursor;
225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.net.Uri;
235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.os.Bundle;
245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract;
255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.CommonDataKinds.BaseTypes;
265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.CommonDataKinds.Email;
275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.CommonDataKinds.Event;
285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.CommonDataKinds.GroupMembership;
295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.CommonDataKinds.Im;
305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.CommonDataKinds.Nickname;
315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.CommonDataKinds.Note;
325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.CommonDataKinds.Organization;
335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.CommonDataKinds.Phone;
345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.CommonDataKinds.Photo;
355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.CommonDataKinds.Relation;
365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.CommonDataKinds.SipAddress;
375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.CommonDataKinds.Website;
405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.Data;
415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.Intents;
425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.Intents.Insert;
435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.RawContacts;
445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.text.TextUtils;
455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.util.Log;
465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.util.SparseArray;
475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.util.SparseIntArray;
485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.ContactsUtils;
505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.model.AccountTypeManager;
515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.model.ValuesDelta;
525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.util.CommonDateUtils;
535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.util.DateUtils;
545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.util.NameConverter;
555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.model.account.AccountType;
565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.model.account.AccountType.EditField;
575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.model.account.AccountType.EditType;
585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.model.account.AccountType.EventEditType;
595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.model.account.GoogleAccountType;
605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.model.dataitem.DataKind;
615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.model.dataitem.PhoneDataItem;
625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.model.dataitem.StructuredNameDataItem;
635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport java.text.ParsePosition;
655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport java.util.ArrayList;
665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport java.util.Arrays;
675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport java.util.Calendar;
685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport java.util.Date;
695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport java.util.HashSet;
705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport java.util.Iterator;
715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport java.util.List;
725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport java.util.Locale;
735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport java.util.Set;
745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee/**
765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Helper methods for modifying an {@link RawContactDelta}, such as inserting
775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * new rows, or enforcing {@link AccountType}.
785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */
795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leepublic class RawContactModifier {
805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static final String TAG = RawContactModifier.class.getSimpleName();
815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /** Set to true in order to view logs on entity operations */
835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static final boolean DEBUG = false;
845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * For the given {@link RawContactDelta}, determine if the given
875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * {@link DataKind} could be inserted under specific
885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * {@link AccountType}.
895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static boolean canInsert(RawContactDelta state, DataKind kind) {
915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Insert possible when have valid types and under overall maximum
925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final int visibleCount = state.getMimeEntriesCount(kind.mimeType, true);
935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final boolean validTypes = hasValidTypes(state, kind);
945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final boolean validOverall = (kind.typeOverallMax == -1)
955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                || (visibleCount < kind.typeOverallMax);
965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return (validTypes && validOverall);
975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static boolean hasValidTypes(RawContactDelta state, DataKind kind) {
1005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (RawContactModifier.hasEditTypes(kind)) {
1015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return (getValidTypes(state, kind).size() > 0);
1025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        } else {
1035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return true;
1045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
1055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
1065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
1085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Ensure that at least one of the given {@link DataKind} exists in the
1095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * given {@link RawContactDelta} state, and try creating one if none exist.
1105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * @return The child (either newly created or the first existing one), or null if the
1115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     *     account doesn't support this {@link DataKind}.
1125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
1135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static ValuesDelta ensureKindExists(
1145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            RawContactDelta state, AccountType accountType, String mimeType) {
1155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final DataKind kind = accountType.getKindForMimetype(mimeType);
1165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final boolean hasChild = state.getMimeEntriesCount(mimeType, true) > 0;
1175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (kind != null) {
1195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (hasChild) {
1205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // Return the first entry.
1215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                return state.getMimeEntries(mimeType).get(0);
1225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            } else {
1235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // Create child when none exists and valid kind
1245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                final ValuesDelta child = insertChild(state, kind);
1255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (kind.mimeType.equals(Photo.CONTENT_ITEM_TYPE)) {
1265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    child.setFromTemplate(true);
1275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
1285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                return child;
1295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
1305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
1315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return null;
1325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
1335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
1355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * For the given {@link RawContactDelta} and {@link DataKind}, return the
1365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * list possible {@link EditType} options available based on
1375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * {@link AccountType}.
1385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
1395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static ArrayList<EditType> getValidTypes(RawContactDelta state, DataKind kind) {
1405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return getValidTypes(state, kind, null, true, null);
1415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
1425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
1445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * For the given {@link RawContactDelta} and {@link DataKind}, return the
1455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * list possible {@link EditType} options available based on
1465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * {@link AccountType}.
1475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     *
1485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * @param forceInclude Always include this {@link EditType} in the returned
1495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     *            list, even when an otherwise-invalid choice. This is useful
1505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     *            when showing a dialog that includes the current type.
1515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
1525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static ArrayList<EditType> getValidTypes(RawContactDelta state, DataKind kind,
1535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            EditType forceInclude) {
1545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return getValidTypes(state, kind, forceInclude, true, null);
1555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
1565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
1585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * For the given {@link RawContactDelta} and {@link DataKind}, return the
1595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * list possible {@link EditType} options available based on
1605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * {@link AccountType}.
1615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     *
1625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * @param forceInclude Always include this {@link EditType} in the returned
1635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     *            list, even when an otherwise-invalid choice. This is useful
1645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     *            when showing a dialog that includes the current type.
1655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * @param includeSecondary If true, include any valid types marked as
1665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     *            {@link EditType#secondary}.
1675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * @param typeCount When provided, will be used for the frequency count of
1685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     *            each {@link EditType}, otherwise built using
1695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     *            {@link #getTypeFrequencies(RawContactDelta, DataKind)}.
1705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
1715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static ArrayList<EditType> getValidTypes(RawContactDelta state, DataKind kind,
1725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            EditType forceInclude, boolean includeSecondary, SparseIntArray typeCount) {
1735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final ArrayList<EditType> validTypes = new ArrayList<EditType>();
1745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Bail early if no types provided
1765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (!hasEditTypes(kind)) return validTypes;
1775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (typeCount == null) {
1795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Build frequency counts if not provided
1805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            typeCount = getTypeFrequencies(state, kind);
1815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
1825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Build list of valid types
1845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final int overallCount = typeCount.get(FREQUENCY_TOTAL);
1855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (EditType type : kind.typeList) {
1865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final boolean validOverall = (kind.typeOverallMax == -1 ? true
1875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    : overallCount < kind.typeOverallMax);
1885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final boolean validSpecific = (type.specificMax == -1 ? true : typeCount
1895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    .get(type.rawValue) < type.specificMax);
1905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final boolean validSecondary = (includeSecondary ? true : !type.secondary);
1915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final boolean forcedInclude = type.equals(forceInclude);
1925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (forcedInclude || (validOverall && validSpecific && validSecondary)) {
1935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // Type is valid when no limit, under limit, or forced include
1945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                validTypes.add(type);
1955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
1965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
1975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
1985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return validTypes;
1995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static final int FREQUENCY_TOTAL = Integer.MIN_VALUE;
2025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
2045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Count up the frequency that each {@link EditType} appears in the given
2055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * {@link RawContactDelta}. The returned {@link SparseIntArray} maps from
2065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * {@link EditType#rawValue} to counts, with the total overall count stored
2075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * as {@link #FREQUENCY_TOTAL}.
2085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
2095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static SparseIntArray getTypeFrequencies(RawContactDelta state, DataKind kind) {
2105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final SparseIntArray typeCount = new SparseIntArray();
2115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Find all entries for this kind, bailing early if none found
2135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final List<ValuesDelta> mimeEntries = state.getMimeEntries(kind.mimeType);
2145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (mimeEntries == null) return typeCount;
2155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        int totalCount = 0;
2175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (ValuesDelta entry : mimeEntries) {
2185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Only count visible entries
2195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (!entry.isVisible()) continue;
2205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            totalCount++;
2215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final EditType type = getCurrentType(entry, kind);
2235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (type != null) {
2245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                final int count = typeCount.get(type.rawValue);
2255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                typeCount.put(type.rawValue, count + 1);
2265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
2275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
2285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        typeCount.put(FREQUENCY_TOTAL, totalCount);
2295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return typeCount;
2305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
2335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Check if the given {@link DataKind} has multiple types that should be
2345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * displayed for users to pick.
2355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
2365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static boolean hasEditTypes(DataKind kind) {
2375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return kind.typeList != null && kind.typeList.size() > 0;
2385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
2415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Find the {@link EditType} that describes the given
2425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * {@link ValuesDelta} row, assuming the given {@link DataKind} dictates
2435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * the possible types.
2445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
2455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static EditType getCurrentType(ValuesDelta entry, DataKind kind) {
2465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final Long rawValue = entry.getAsLong(kind.typeColumn);
2475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (rawValue == null) return null;
2485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return getType(kind, rawValue.intValue());
2495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
2525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Find the {@link EditType} that describes the given {@link ContentValues} row,
2535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * assuming the given {@link DataKind} dictates the possible types.
2545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
2555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static EditType getCurrentType(ContentValues entry, DataKind kind) {
2565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (kind.typeColumn == null) return null;
2575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final Integer rawValue = entry.getAsInteger(kind.typeColumn);
2585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (rawValue == null) return null;
2595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return getType(kind, rawValue);
2605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
2635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Find the {@link EditType} that describes the given {@link Cursor} row,
2645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * assuming the given {@link DataKind} dictates the possible types.
2655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
2665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static EditType getCurrentType(Cursor cursor, DataKind kind) {
2675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (kind.typeColumn == null) return null;
2685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final int index = cursor.getColumnIndex(kind.typeColumn);
2695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (index == -1) return null;
2705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final int rawValue = cursor.getInt(index);
2715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return getType(kind, rawValue);
2725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
2755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Find the {@link EditType} with the given {@link EditType#rawValue}.
2765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
2775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static EditType getType(DataKind kind, int rawValue) {
2785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (EditType type : kind.typeList) {
2795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (type.rawValue == rawValue) {
2805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                return type;
2815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
2825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
2835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return null;
2845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
2865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
2875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Return the precedence for the the given {@link EditType#rawValue}, where
2885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * lower numbers are higher precedence.
2895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
2905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static int getTypePrecedence(DataKind kind, int rawValue) {
2915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (int i = 0; i < kind.typeList.size(); i++) {
2925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final EditType type = kind.typeList.get(i);
2935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (type.rawValue == rawValue) {
2945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                return i;
2955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
2965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
2975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return Integer.MAX_VALUE;
2985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
2995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
3015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Find the best {@link EditType} for a potential insert. The "best" is the
3025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * first primary type that doesn't already exist. When all valid types
3035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * exist, we pick the last valid option.
3045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
3055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static EditType getBestValidType(RawContactDelta state, DataKind kind,
3065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            boolean includeSecondary, int exactValue) {
3075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Shortcut when no types
30829a094106357ea806dc029437af77ec91b94cafbJay Shrauner        if (kind == null || kind.typeColumn == null) return null;
3095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Find type counts and valid primary types, bail if none
3115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final SparseIntArray typeCount = getTypeFrequencies(state, kind);
3125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final ArrayList<EditType> validTypes = getValidTypes(state, kind, null, includeSecondary,
3135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                typeCount);
3145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (validTypes.size() == 0) return null;
3155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Keep track of the last valid type
3175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final EditType lastType = validTypes.get(validTypes.size() - 1);
3185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Remove any types that already exist
3205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        Iterator<EditType> iterator = validTypes.iterator();
3215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        while (iterator.hasNext()) {
3225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final EditType type = iterator.next();
3235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final int count = typeCount.get(type.rawValue);
3245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (exactValue == type.rawValue) {
3265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // Found exact value match
3275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                return type;
3285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
3295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (count > 0) {
3315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // Type already appears, so don't consider
3325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                iterator.remove();
3335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
3345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
3355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Use the best remaining, otherwise the last valid
3375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (validTypes.size() > 0) {
3385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return validTypes.get(0);
3395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        } else {
3405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return lastType;
3415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
3425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
3435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
3455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Insert a new child of kind {@link DataKind} into the given
3465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * {@link RawContactDelta}. Tries using the best {@link EditType} found using
3475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * {@link #getBestValidType(RawContactDelta, DataKind, boolean, int)}.
3485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
3495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static ValuesDelta insertChild(RawContactDelta state, DataKind kind) {
35029a094106357ea806dc029437af77ec91b94cafbJay Shrauner        // Bail early if invalid kind
35129a094106357ea806dc029437af77ec91b94cafbJay Shrauner        if (kind == null) return null;
3525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // First try finding a valid primary
3535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        EditType bestType = getBestValidType(state, kind, false, Integer.MIN_VALUE);
3545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (bestType == null) {
3555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // No valid primary found, so expand search to secondary
3565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            bestType = getBestValidType(state, kind, true, Integer.MIN_VALUE);
3575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
3585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return insertChild(state, kind, bestType);
3595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
3605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
3625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Insert a new child of kind {@link DataKind} into the given
3635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * {@link RawContactDelta}, marked with the given {@link EditType}.
3645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
3655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static ValuesDelta insertChild(RawContactDelta state, DataKind kind, EditType type) {
3665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Bail early if invalid kind
3675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (kind == null) return null;
3685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final ContentValues after = new ContentValues();
3695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Our parent CONTACT_ID is provided later
3715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        after.put(Data.MIMETYPE, kind.mimeType);
3725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Fill-in with any requested default values
3745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (kind.defaultValues != null) {
3755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            after.putAll(kind.defaultValues);
3765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
3775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (kind.typeColumn != null && type != null) {
3795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Set type, if provided
3805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            after.put(kind.typeColumn, type.rawValue);
3815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
3825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final ValuesDelta child = ValuesDelta.fromAfter(after);
3845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        state.addEntry(child);
3855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return child;
3865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
3875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
3885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
3895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Processing to trim any empty {@link ValuesDelta} and {@link RawContactDelta}
3905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * from the given {@link RawContactDeltaList}, assuming the given {@link AccountTypeManager}
3915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * dictates the structure for various fields. This method ignores rows not
3925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * described by the {@link AccountType}.
3935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
3945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static void trimEmpty(RawContactDeltaList set, AccountTypeManager accountTypes) {
3955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (RawContactDelta state : set) {
3965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            ValuesDelta values = state.getValues();
3975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
3985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final String dataSet = values.getAsString(RawContacts.DATA_SET);
3995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final AccountType type = accountTypes.getAccountType(accountType, dataSet);
4005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            trimEmpty(state, type);
4015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
4025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
4035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static boolean hasChanges(RawContactDeltaList set, AccountTypeManager accountTypes) {
4055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (set.isMarkedForSplitting() || set.isMarkedForJoining()) {
4065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return true;
4075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
4085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (RawContactDelta state : set) {
4105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            ValuesDelta values = state.getValues();
4115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE);
4125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final String dataSet = values.getAsString(RawContacts.DATA_SET);
4135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final AccountType type = accountTypes.getAccountType(accountType, dataSet);
4145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (hasChanges(state, type)) {
4155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                return true;
4165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
4175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
4185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return false;
4195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
4205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
4225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Processing to trim any empty {@link ValuesDelta} rows from the given
4235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * {@link RawContactDelta}, assuming the given {@link AccountType} dictates
4245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * the structure for various fields. This method ignores rows not described
4255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * by the {@link AccountType}.
4265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
4275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static void trimEmpty(RawContactDelta state, AccountType accountType) {
4285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        boolean hasValues = false;
4295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Walk through entries for each well-known kind
4315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (DataKind kind : accountType.getSortedDataKinds()) {
4325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final String mimeType = kind.mimeType;
4335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType);
4345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (entries == null) continue;
4355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            for (ValuesDelta entry : entries) {
4375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // Skip any values that haven't been touched
4385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                final boolean touched = entry.isInsert() || entry.isUpdate();
4395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (!touched) {
4405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    hasValues = true;
4415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    continue;
4425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
4435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // Test and remove this row if empty and it isn't a photo from google
4455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                final boolean isGoogleAccount = TextUtils.equals(GoogleAccountType.ACCOUNT_TYPE,
4465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        state.getValues().getAsString(RawContacts.ACCOUNT_TYPE));
4475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                final boolean isPhoto = TextUtils.equals(Photo.CONTENT_ITEM_TYPE, kind.mimeType);
4485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                final boolean isGooglePhoto = isPhoto && isGoogleAccount;
4495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (RawContactModifier.isEmpty(entry, kind) && !isGooglePhoto) {
4515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    if (DEBUG) {
4525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        Log.v(TAG, "Trimming: " + entry.toString());
4535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    }
4545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    entry.markDeleted();
4555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                } else if (!entry.isFromTemplate()) {
4565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    hasValues = true;
4575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
4585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
4595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
4605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (!hasValues) {
4615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Trim overall entity if no children exist
4625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            state.markDeleted();
4635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
4645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
4655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static boolean hasChanges(RawContactDelta state, AccountType accountType) {
4675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (DataKind kind : accountType.getSortedDataKinds()) {
4685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final String mimeType = kind.mimeType;
4695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType);
4705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (entries == null) continue;
4715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            for (ValuesDelta entry : entries) {
4735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // An empty Insert must be ignored, because it won't save anything (an example
4745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // is an empty name that stays empty)
4755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                final boolean isRealInsert = entry.isInsert() && !isEmpty(entry, kind);
4765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (isRealInsert || entry.isUpdate() || entry.isDelete()) {
4775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    return true;
4785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
4795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
4805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
4815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return false;
4825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
4835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
4855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Test if the given {@link ValuesDelta} would be considered "empty" in
4865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * terms of {@link DataKind#fieldList}.
4875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
4885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static boolean isEmpty(ValuesDelta values, DataKind kind) {
4895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (Photo.CONTENT_ITEM_TYPE.equals(kind.mimeType)) {
4905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return values.isInsert() && values.getAsByteArray(Photo.PHOTO) == null;
4915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
4925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // No defined fields mean this row is always empty
4945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (kind.fieldList == null) return true;
4955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
4965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (EditField field : kind.fieldList) {
4975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // If any field has values, we're not empty
4985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final String value = values.getAsString(field.column);
4995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (ContactsUtils.isGraphic(value)) {
5005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                return false;
5015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
5025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
5035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return true;
5055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
5065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
5085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Compares corresponding fields in values1 and values2. Only the fields
5095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * declared by the DataKind are taken into consideration.
5105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
5115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    protected static boolean areEqual(ValuesDelta values1, ContentValues values2, DataKind kind) {
5125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (kind.fieldList == null) return false;
5135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (EditField field : kind.fieldList) {
5155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final String value1 = values1.getAsString(field.column);
5165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final String value2 = values2.getAsString(field.column);
5175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (!TextUtils.equals(value1, value2)) {
5185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                return false;
5195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
5205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
5215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return true;
5235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
5245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
5265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Parse the given {@link Bundle} into the given {@link RawContactDelta} state,
5275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * assuming the extras defined through {@link Intents}.
5285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
5295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static void parseExtras(Context context, AccountType accountType, RawContactDelta state,
5305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            Bundle extras) {
5315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (extras == null || extras.size() == 0) {
5325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Bail early if no useful data
5335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return;
5345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
5355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        parseStructuredNameExtra(context, accountType, state, extras);
5375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        parseStructuredPostalExtra(accountType, state, extras);
5385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        {
5405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Phone
5415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final DataKind kind = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
5425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            parseExtras(state, kind, extras, Insert.PHONE_TYPE, Insert.PHONE, Phone.NUMBER);
5435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            parseExtras(state, kind, extras, Insert.SECONDARY_PHONE_TYPE, Insert.SECONDARY_PHONE,
5445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    Phone.NUMBER);
5455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            parseExtras(state, kind, extras, Insert.TERTIARY_PHONE_TYPE, Insert.TERTIARY_PHONE,
5465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    Phone.NUMBER);
5475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
5485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        {
5505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Email
5515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final DataKind kind = accountType.getKindForMimetype(Email.CONTENT_ITEM_TYPE);
5525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            parseExtras(state, kind, extras, Insert.EMAIL_TYPE, Insert.EMAIL, Email.DATA);
5535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            parseExtras(state, kind, extras, Insert.SECONDARY_EMAIL_TYPE, Insert.SECONDARY_EMAIL,
5545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    Email.DATA);
5555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            parseExtras(state, kind, extras, Insert.TERTIARY_EMAIL_TYPE, Insert.TERTIARY_EMAIL,
5565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    Email.DATA);
5575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
5585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        {
5605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Im
5615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final DataKind kind = accountType.getKindForMimetype(Im.CONTENT_ITEM_TYPE);
5625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            fixupLegacyImType(extras);
5635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            parseExtras(state, kind, extras, Insert.IM_PROTOCOL, Insert.IM_HANDLE, Im.DATA);
5645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
5655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Organization
5675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final boolean hasOrg = extras.containsKey(Insert.COMPANY)
5685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                || extras.containsKey(Insert.JOB_TITLE);
5695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final DataKind kindOrg = accountType.getKindForMimetype(Organization.CONTENT_ITEM_TYPE);
5705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (hasOrg && RawContactModifier.canInsert(state, kindOrg)) {
5715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final ValuesDelta child = RawContactModifier.insertChild(state, kindOrg);
5725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final String company = extras.getString(Insert.COMPANY);
5745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (ContactsUtils.isGraphic(company)) {
5755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                child.put(Organization.COMPANY, company);
5765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
5775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final String title = extras.getString(Insert.JOB_TITLE);
5795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (ContactsUtils.isGraphic(title)) {
5805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                child.put(Organization.TITLE, title);
5815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
5825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
5835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Notes
5855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final boolean hasNotes = extras.containsKey(Insert.NOTES);
5865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final DataKind kindNotes = accountType.getKindForMimetype(Note.CONTENT_ITEM_TYPE);
5875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (hasNotes && RawContactModifier.canInsert(state, kindNotes)) {
5885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final ValuesDelta child = RawContactModifier.insertChild(state, kindNotes);
5895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final String notes = extras.getString(Insert.NOTES);
5915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (ContactsUtils.isGraphic(notes)) {
5925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                child.put(Note.NOTE, notes);
5935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
5945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
5955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
5965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Arbitrary additional data
5975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        ArrayList<ContentValues> values = extras.getParcelableArrayList(Insert.DATA);
5985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (values != null) {
5995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            parseValues(state, accountType, values);
6005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
6015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
6025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
6035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static void parseStructuredNameExtra(
6045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            Context context, AccountType accountType, RawContactDelta state, Bundle extras) {
6055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // StructuredName
6065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        RawContactModifier.ensureKindExists(state, accountType, StructuredName.CONTENT_ITEM_TYPE);
6075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final ValuesDelta child = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
6085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
6095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final String name = extras.getString(Insert.NAME);
6105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (ContactsUtils.isGraphic(name)) {
6115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final DataKind kind = accountType.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE);
6125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            boolean supportsDisplayName = false;
6135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (kind.fieldList != null) {
6145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                for (EditField field : kind.fieldList) {
6155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    if (StructuredName.DISPLAY_NAME.equals(field.column)) {
6165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        supportsDisplayName = true;
6175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        break;
6185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    }
6195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
6205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
6215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
6225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (supportsDisplayName) {
6235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                child.put(StructuredName.DISPLAY_NAME, name);
6245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            } else {
6255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                Uri uri = ContactsContract.AUTHORITY_URI.buildUpon()
6265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        .appendPath("complete_name")
6275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        .appendQueryParameter(StructuredName.DISPLAY_NAME, name)
6285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        .build();
6295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                Cursor cursor = context.getContentResolver().query(uri,
6305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        new String[]{
6315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                StructuredName.PREFIX,
6325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                StructuredName.GIVEN_NAME,
6335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                StructuredName.MIDDLE_NAME,
6345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                StructuredName.FAMILY_NAME,
6355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                StructuredName.SUFFIX,
6365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        }, null, null, null);
6375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
63823fe38b225346526e3f425570f3648dd7d893a5dJay Shrauner                if (cursor != null) {
63923fe38b225346526e3f425570f3648dd7d893a5dJay Shrauner                    try {
64023fe38b225346526e3f425570f3648dd7d893a5dJay Shrauner                        if (cursor.moveToFirst()) {
64123fe38b225346526e3f425570f3648dd7d893a5dJay Shrauner                            child.put(StructuredName.PREFIX, cursor.getString(0));
64223fe38b225346526e3f425570f3648dd7d893a5dJay Shrauner                            child.put(StructuredName.GIVEN_NAME, cursor.getString(1));
64323fe38b225346526e3f425570f3648dd7d893a5dJay Shrauner                            child.put(StructuredName.MIDDLE_NAME, cursor.getString(2));
64423fe38b225346526e3f425570f3648dd7d893a5dJay Shrauner                            child.put(StructuredName.FAMILY_NAME, cursor.getString(3));
64523fe38b225346526e3f425570f3648dd7d893a5dJay Shrauner                            child.put(StructuredName.SUFFIX, cursor.getString(4));
64623fe38b225346526e3f425570f3648dd7d893a5dJay Shrauner                        }
64723fe38b225346526e3f425570f3648dd7d893a5dJay Shrauner                    } finally {
64823fe38b225346526e3f425570f3648dd7d893a5dJay Shrauner                        cursor.close();
6495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    }
6505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
6515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
6525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
6535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
6545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final String phoneticName = extras.getString(Insert.PHONETIC_NAME);
6555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (ContactsUtils.isGraphic(phoneticName)) {
6565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            child.put(StructuredName.PHONETIC_GIVEN_NAME, phoneticName);
6575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
6585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
6595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
6605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static void parseStructuredPostalExtra(
6615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            AccountType accountType, RawContactDelta state, Bundle extras) {
6625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // StructuredPostal
6635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final DataKind kind = accountType.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE);
6645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final ValuesDelta child = parseExtras(state, kind, extras, Insert.POSTAL_TYPE,
6655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                Insert.POSTAL, StructuredPostal.FORMATTED_ADDRESS);
6665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        String address = child == null ? null
6675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                : child.getAsString(StructuredPostal.FORMATTED_ADDRESS);
6685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (!TextUtils.isEmpty(address)) {
6695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            boolean supportsFormatted = false;
6705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (kind.fieldList != null) {
6715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                for (EditField field : kind.fieldList) {
6725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    if (StructuredPostal.FORMATTED_ADDRESS.equals(field.column)) {
6735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        supportsFormatted = true;
6745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        break;
6755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    }
6765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
6775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
6785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
6795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (!supportsFormatted) {
6805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                child.put(StructuredPostal.STREET, address);
6815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                child.putNull(StructuredPostal.FORMATTED_ADDRESS);
6825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
6835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
6845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
6855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
6865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static void parseValues(
6875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            RawContactDelta state, AccountType accountType,
6885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            ArrayList<ContentValues> dataValueList) {
6895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (ContentValues values : dataValueList) {
6905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            String mimeType = values.getAsString(Data.MIMETYPE);
6915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (TextUtils.isEmpty(mimeType)) {
6925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                Log.e(TAG, "Mimetype is required. Ignoring: " + values);
6935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                continue;
6945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
6955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
6965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Won't override the contact name
6975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
6985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                continue;
6995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            } else if (Phone.CONTENT_ITEM_TYPE.equals(mimeType)) {
7005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                values.remove(PhoneDataItem.KEY_FORMATTED_PHONE_NUMBER);
7015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                final Integer type = values.getAsInteger(Phone.TYPE);
7025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // If the provided phone number provides a custom phone type but not a label,
7035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // replace it with mobile (by default) to avoid the "Enter custom label" from
7045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // popping up immediately upon entering the ContactEditorFragment
7055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (type != null && type == Phone.TYPE_CUSTOM &&
7065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        TextUtils.isEmpty(values.getAsString(Phone.LABEL))) {
7075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.put(Phone.TYPE, Phone.TYPE_MOBILE);
7085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
7095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
7105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
7115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            DataKind kind = accountType.getKindForMimetype(mimeType);
7125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (kind == null) {
7135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                Log.e(TAG, "Mimetype not supported for account type "
7145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        + accountType.getAccountTypeAndDataSet() + ". Ignoring: " + values);
7155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                continue;
7165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
7175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
7185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            ValuesDelta entry = ValuesDelta.fromAfter(values);
7195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (isEmpty(entry, kind)) {
7205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                continue;
7215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
7225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
7235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType);
7245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
7255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if ((kind.typeOverallMax != 1) || GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) {
7265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // Check for duplicates
7275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                boolean addEntry = true;
7285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                int count = 0;
7295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (entries != null && entries.size() > 0) {
7305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    for (ValuesDelta delta : entries) {
7315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        if (!delta.isDelete()) {
7325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            if (areEqual(delta, values, kind)) {
7335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                addEntry = false;
7345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                break;
7355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            }
7365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            count++;
7375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        }
7385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    }
7395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
7405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
7415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (kind.typeOverallMax != -1 && count >= kind.typeOverallMax) {
7425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    Log.e(TAG, "Mimetype allows at most " + kind.typeOverallMax
7435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            + " entries. Ignoring: " + values);
7445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    addEntry = false;
7455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
7465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
7475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (addEntry) {
7485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    addEntry = adjustType(entry, entries, kind);
7495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
7505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
7515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (addEntry) {
7525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    state.addEntry(entry);
7535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
7545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            } else {
7555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // Non-list entries should not be overridden
7565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                boolean addEntry = true;
7575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (entries != null && entries.size() > 0) {
7585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    for (ValuesDelta delta : entries) {
7595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        if (!delta.isDelete() && !isEmpty(delta, kind)) {
7605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            addEntry = false;
7615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            break;
7625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        }
7635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    }
7645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    if (addEntry) {
7655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        for (ValuesDelta delta : entries) {
7665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            delta.markDeleted();
7675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        }
7685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    }
7695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
7705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
7715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (addEntry) {
7725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    addEntry = adjustType(entry, entries, kind);
7735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
7745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
7755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (addEntry) {
7765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    state.addEntry(entry);
7775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType)){
7785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    // Note is most likely to contain large amounts of text
7795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    // that we don't want to drop on the ground.
7805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    for (ValuesDelta delta : entries) {
7815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        if (!isEmpty(delta, kind)) {
7825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            delta.put(Note.NOTE, delta.getAsString(Note.NOTE) + "\n"
7835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                    + values.getAsString(Note.NOTE));
7845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            break;
7855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        }
7865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    }
7875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                } else {
7885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    Log.e(TAG, "Will not override mimetype " + mimeType + ". Ignoring: "
7895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            + values);
7905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
7915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
7925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
7935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
7945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
7955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
7965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Checks if the data kind allows addition of another entry (e.g. Exchange only
7975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * supports two "work" phone numbers).  If not, tries to switch to one of the
7985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * unused types.  If successful, returns true.
7995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
8005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static boolean adjustType(
8015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            ValuesDelta entry, ArrayList<ValuesDelta> entries, DataKind kind) {
8025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (kind.typeColumn == null || kind.typeList == null || kind.typeList.size() == 0) {
8035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return true;
8045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
8055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
8065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        Integer typeInteger = entry.getAsInteger(kind.typeColumn);
8075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        int type = typeInteger != null ? typeInteger : kind.typeList.get(0).rawValue;
8085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
8095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (isTypeAllowed(type, entries, kind)) {
8105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            entry.put(kind.typeColumn, type);
8115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return true;
8125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
8135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
8145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Specified type is not allowed - choose the first available type that is allowed
8155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        int size = kind.typeList.size();
8165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (int i = 0; i < size; i++) {
8175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            EditType editType = kind.typeList.get(i);
8185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (isTypeAllowed(editType.rawValue, entries, kind)) {
8195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                entry.put(kind.typeColumn, editType.rawValue);
8205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                return true;
8215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
8225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
8235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
8245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return false;
8255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
8265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
8275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
8285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Checks if a new entry of the specified type can be added to the raw
8295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * contact. For example, Exchange only supports two "work" phone numbers, so
8305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * addition of a third would not be allowed.
8315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
8325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static boolean isTypeAllowed(int type, ArrayList<ValuesDelta> entries, DataKind kind) {
8335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        int max = 0;
8345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        int size = kind.typeList.size();
8355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (int i = 0; i < size; i++) {
8365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            EditType editType = kind.typeList.get(i);
8375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (editType.rawValue == type) {
8385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                max = editType.specificMax;
8395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                break;
8405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
8415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
8425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
8435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (max == 0) {
8445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // This type is not allowed at all
8455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return false;
8465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
8475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
8485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (max == -1) {
8495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Unlimited instances of this type are allowed
8505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return true;
8515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
8525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
8535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return getEntryCountByType(entries, kind.typeColumn, type) < max;
8545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
8555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
8565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
8575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Counts occurrences of the specified type in the supplied entry list.
8585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     *
8595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * @return The count of occurrences of the type in the entry list. 0 if entries is
8605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * {@literal null}
8615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
8625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static int getEntryCountByType(ArrayList<ValuesDelta> entries, String typeColumn,
8635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            int type) {
8645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        int count = 0;
8655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (entries != null) {
8665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            for (ValuesDelta entry : entries) {
8675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                Integer typeInteger = entry.getAsInteger(typeColumn);
8685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (typeInteger != null && typeInteger == type) {
8695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    count++;
8705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
8715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
8725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
8735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return count;
8745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
8755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
8765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
8775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Attempt to parse legacy {@link Insert#IM_PROTOCOL} values, replacing them
8785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * with updated values.
8795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
8805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    @SuppressWarnings("deprecation")
8815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static void fixupLegacyImType(Bundle bundle) {
8825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final String encodedString = bundle.getString(Insert.IM_PROTOCOL);
8835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (encodedString == null) return;
8845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
8855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        try {
8865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final Object protocol = android.provider.Contacts.ContactMethods
8875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    .decodeImProtocol(encodedString);
8885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (protocol instanceof Integer) {
8895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                bundle.putInt(Insert.IM_PROTOCOL, (Integer)protocol);
8905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            } else {
8915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                bundle.putString(Insert.IM_PROTOCOL, (String)protocol);
8925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
8935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        } catch (IllegalArgumentException e) {
8945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Ignore exception when legacy parser fails
8955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
8965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
8975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
8985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
8995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Parse a specific entry from the given {@link Bundle} and insert into the
9005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * given {@link RawContactDelta}. Silently skips the insert when missing value
9015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * or no valid {@link EditType} found.
9025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     *
9035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * @param typeExtra {@link Bundle} key that holds the incoming
9045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     *            {@link EditType#rawValue} value.
9055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * @param valueExtra {@link Bundle} key that holds the incoming value.
9065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * @param valueColumn Column to write value into {@link ValuesDelta}.
9075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
9085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static ValuesDelta parseExtras(RawContactDelta state, DataKind kind, Bundle extras,
9095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            String typeExtra, String valueExtra, String valueColumn) {
9105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final CharSequence value = extras.getCharSequence(valueExtra);
9115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
9125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Bail early if account type doesn't handle this MIME type
9135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (kind == null) return null;
9145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
9155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Bail when can't insert type, or value missing
9165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final boolean canInsert = RawContactModifier.canInsert(state, kind);
9175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final boolean validValue = (value != null && TextUtils.isGraphic(value));
9185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (!validValue || !canInsert) return null;
9195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
9205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Find exact type when requested, otherwise best available type
9215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final boolean hasType = extras.containsKey(typeExtra);
9225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final int typeValue = extras.getInt(typeExtra, hasType ? BaseTypes.TYPE_CUSTOM
9235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                : Integer.MIN_VALUE);
9245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final EditType editType = RawContactModifier.getBestValidType(state, kind, true, typeValue);
9255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
9265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Create data row and fill with value
9275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final ValuesDelta child = RawContactModifier.insertChild(state, kind, editType);
9285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        child.put(valueColumn, value.toString());
9295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
9305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (editType != null && editType.customColumn != null) {
9315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Write down label when custom type picked
9325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final String customType = extras.getString(typeExtra);
9335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            child.put(editType.customColumn, customType);
9345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
9355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
9365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return child;
9375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
9385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
9395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
9405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Generic mime types with type support (e.g. TYPE_HOME).
9415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Here, "type support" means if the data kind has CommonColumns#TYPE or not. Data kinds which
9425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * have their own migrate methods aren't listed here.
9435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
9445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static final Set<String> sGenericMimeTypesWithTypeSupport = new HashSet<String>(
9455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            Arrays.asList(Phone.CONTENT_ITEM_TYPE,
9465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    Email.CONTENT_ITEM_TYPE,
9475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    Im.CONTENT_ITEM_TYPE,
9485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    Nickname.CONTENT_ITEM_TYPE,
9495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    Website.CONTENT_ITEM_TYPE,
9505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    Relation.CONTENT_ITEM_TYPE,
9515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    SipAddress.CONTENT_ITEM_TYPE));
9525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static final Set<String> sGenericMimeTypesWithoutTypeSupport = new HashSet<String>(
9535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            Arrays.asList(Organization.CONTENT_ITEM_TYPE,
9545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    Note.CONTENT_ITEM_TYPE,
9555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    Photo.CONTENT_ITEM_TYPE,
9565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    GroupMembership.CONTENT_ITEM_TYPE));
9575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    // CommonColumns.TYPE cannot be accessed as it is protected interface, so use
9585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    // Phone.TYPE instead.
9595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static final String COLUMN_FOR_TYPE  = Phone.TYPE;
9605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static final String COLUMN_FOR_LABEL  = Phone.LABEL;
9615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static final int TYPE_CUSTOM = Phone.TYPE_CUSTOM;
9625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
9635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
9645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Migrates old RawContactDelta to newly created one with a new restriction supplied from
9655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * newAccountType.
9665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     *
9675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * This is only for account switch during account creation (which must be insert operation).
9685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
9695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static void migrateStateForNewContact(Context context,
9705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            RawContactDelta oldState, RawContactDelta newState,
9715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            AccountType oldAccountType, AccountType newAccountType) {
9725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (newAccountType == oldAccountType) {
9735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Just copying all data in oldState isn't enough, but we can still rely on a lot of
9745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // shortcuts.
9755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            for (DataKind kind : newAccountType.getSortedDataKinds()) {
9765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                final String mimeType = kind.mimeType;
9775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // The fields with short/long form capability must be treated properly.
9785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
9795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    migrateStructuredName(context, oldState, newState, kind);
9805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                } else {
9815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    List<ValuesDelta> entryList = oldState.getMimeEntries(mimeType);
9825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    if (entryList != null && !entryList.isEmpty()) {
9835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        for (ValuesDelta entry : entryList) {
9845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            ContentValues values = entry.getAfter();
9855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            if (values != null) {
9865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                newState.addEntry(ValuesDelta.fromAfter(values));
9875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            }
9885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        }
9895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    }
9905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
9915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
9925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        } else {
9935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // Migrate data supported by the new account type.
9945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            // All the other data inside oldState are silently dropped.
9955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            for (DataKind kind : newAccountType.getSortedDataKinds()) {
9965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (!kind.editable) continue;
9975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                final String mimeType = kind.mimeType;
9985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME.equals(mimeType)
9995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        || DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) {
10005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    // Ignore pseudo data.
10015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    continue;
10025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                } else if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) {
10035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    migrateStructuredName(context, oldState, newState, kind);
10045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) {
10055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    migratePostal(oldState, newState, kind);
10065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType)) {
10075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    migrateEvent(oldState, newState, kind, null /* default Year */);
10085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                } else if (sGenericMimeTypesWithoutTypeSupport.contains(mimeType)) {
10095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    migrateGenericWithoutTypeColumn(oldState, newState, kind);
10105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                } else if (sGenericMimeTypesWithTypeSupport.contains(mimeType)) {
10115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    migrateGenericWithTypeColumn(oldState, newState, kind);
10125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                } else {
10135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    throw new IllegalStateException("Unexpected editable mime-type: " + mimeType);
10145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
10155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
10165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
10175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
10185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
10195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /**
10205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * Checks {@link DataKind#isList} and {@link DataKind#typeOverallMax}, and restricts
10215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     * the number of entries (ValuesDelta) inside newState.
10225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee     */
10235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    private static ArrayList<ValuesDelta> ensureEntryMaxSize(RawContactDelta newState,
10245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            DataKind kind, ArrayList<ValuesDelta> mimeEntries) {
10255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (mimeEntries == null) {
10265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return null;
10275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
10285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
10295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final int typeOverallMax = kind.typeOverallMax;
10305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (typeOverallMax >= 0 && (mimeEntries.size() > typeOverallMax)) {
10315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            ArrayList<ValuesDelta> newMimeEntries = new ArrayList<ValuesDelta>(typeOverallMax);
10325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            for (int i = 0; i < typeOverallMax; i++) {
10335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                newMimeEntries.add(mimeEntries.get(i));
10345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
10355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            mimeEntries = newMimeEntries;
10365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
10375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        return mimeEntries;
10385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
10395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
10405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /** @hide Public only for testing. */
10415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static void migrateStructuredName(
10425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            Context context, RawContactDelta oldState, RawContactDelta newState,
10435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            DataKind newDataKind) {
10445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final ContentValues values =
10455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                oldState.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE).getAfter();
10465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (values == null) {
10475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return;
10485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
10495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
10505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        boolean supportDisplayName = false;
10515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        boolean supportPhoneticFullName = false;
10525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        boolean supportPhoneticFamilyName = false;
10535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        boolean supportPhoneticMiddleName = false;
10545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        boolean supportPhoneticGivenName = false;
10555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (EditField editField : newDataKind.fieldList) {
10565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (StructuredName.DISPLAY_NAME.equals(editField.column)) {
10575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                supportDisplayName = true;
10585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
10595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (DataKind.PSEUDO_COLUMN_PHONETIC_NAME.equals(editField.column)) {
10605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                supportPhoneticFullName = true;
10615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
10625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (StructuredName.PHONETIC_FAMILY_NAME.equals(editField.column)) {
10635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                supportPhoneticFamilyName = true;
10645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
10655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (StructuredName.PHONETIC_MIDDLE_NAME.equals(editField.column)) {
10665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                supportPhoneticMiddleName = true;
10675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
10685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (StructuredName.PHONETIC_GIVEN_NAME.equals(editField.column)) {
10695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                supportPhoneticGivenName = true;
10705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
10715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
10725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
10735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // DISPLAY_NAME <-> PREFIX, GIVEN_NAME, MIDDLE_NAME, FAMILY_NAME, SUFFIX
10745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final String displayName = values.getAsString(StructuredName.DISPLAY_NAME);
10755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (!TextUtils.isEmpty(displayName)) {
10765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (!supportDisplayName) {
10775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // Old data has a display name, while the new account doesn't allow it.
10785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                NameConverter.displayNameToStructuredName(context, displayName, values);
10795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
10805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // We don't want to migrate unseen data which may confuse users after the creation.
10815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                values.remove(StructuredName.DISPLAY_NAME);
10825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
10835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        } else {
10845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (supportDisplayName) {
10855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // Old data does not have display name, while the new account requires it.
10865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                values.put(StructuredName.DISPLAY_NAME,
10875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        NameConverter.structuredNameToDisplayName(context, values));
10885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                for (String field : NameConverter.STRUCTURED_NAME_FIELDS) {
10895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.remove(field);
10905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
10915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
10925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
10935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
10945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Phonetic (full) name <-> PHONETIC_FAMILY_NAME, PHONETIC_MIDDLE_NAME, PHONETIC_GIVEN_NAME
10955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final String phoneticFullName = values.getAsString(DataKind.PSEUDO_COLUMN_PHONETIC_NAME);
10965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (!TextUtils.isEmpty(phoneticFullName)) {
10975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (!supportPhoneticFullName) {
10985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // Old data has a phonetic (full) name, while the new account doesn't allow it.
10995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                final StructuredNameDataItem tmpItem =
11005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        NameConverter.parsePhoneticName(phoneticFullName, null);
11015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                values.remove(DataKind.PSEUDO_COLUMN_PHONETIC_NAME);
11025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (supportPhoneticFamilyName) {
11035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.put(StructuredName.PHONETIC_FAMILY_NAME,
11045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            tmpItem.getPhoneticFamilyName());
11055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                } else {
11065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.remove(StructuredName.PHONETIC_FAMILY_NAME);
11075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
11085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (supportPhoneticMiddleName) {
11095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.put(StructuredName.PHONETIC_MIDDLE_NAME,
11105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            tmpItem.getPhoneticMiddleName());
11115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                } else {
11125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.remove(StructuredName.PHONETIC_MIDDLE_NAME);
11135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
11145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (supportPhoneticGivenName) {
11155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.put(StructuredName.PHONETIC_GIVEN_NAME,
11165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            tmpItem.getPhoneticGivenName());
11175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                } else {
11185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.remove(StructuredName.PHONETIC_GIVEN_NAME);
11195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
11205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
11215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        } else {
11225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (supportPhoneticFullName) {
11235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // Old data does not have a phonetic (full) name, while the new account requires it.
11245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                values.put(DataKind.PSEUDO_COLUMN_PHONETIC_NAME,
11255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        NameConverter.buildPhoneticName(
11265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                values.getAsString(StructuredName.PHONETIC_FAMILY_NAME),
11275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                values.getAsString(StructuredName.PHONETIC_MIDDLE_NAME),
11285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                values.getAsString(StructuredName.PHONETIC_GIVEN_NAME)));
11295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
11305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (!supportPhoneticFamilyName) {
11315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                values.remove(StructuredName.PHONETIC_FAMILY_NAME);
11325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
11335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (!supportPhoneticMiddleName) {
11345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                values.remove(StructuredName.PHONETIC_MIDDLE_NAME);
11355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
11365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (!supportPhoneticGivenName) {
11375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                values.remove(StructuredName.PHONETIC_GIVEN_NAME);
11385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
11395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
11405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
11415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        newState.addEntry(ValuesDelta.fromAfter(values));
11425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
11435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
11445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /** @hide Public only for testing. */
11455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static void migratePostal(RawContactDelta oldState, RawContactDelta newState,
11465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            DataKind newDataKind) {
11475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind,
11485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                oldState.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE));
11495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (mimeEntries == null || mimeEntries.isEmpty()) {
11505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return;
11515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
11525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
11535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        boolean supportFormattedAddress = false;
11545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        boolean supportStreet = false;
11555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final String firstColumn = newDataKind.fieldList.get(0).column;
11565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (EditField editField : newDataKind.fieldList) {
11575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (StructuredPostal.FORMATTED_ADDRESS.equals(editField.column)) {
11585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                supportFormattedAddress = true;
11595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
11605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (StructuredPostal.STREET.equals(editField.column)) {
11615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                supportStreet = true;
11625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
11635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
11645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
11655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final Set<Integer> supportedTypes = new HashSet<Integer>();
11665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (newDataKind.typeList != null && !newDataKind.typeList.isEmpty()) {
11675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            for (EditType editType : newDataKind.typeList) {
11685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                supportedTypes.add(editType.rawValue);
11695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
11705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
11715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
11725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (ValuesDelta entry : mimeEntries) {
11735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final ContentValues values = entry.getAfter();
11745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (values == null) {
11755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                continue;
11765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
11775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final Integer oldType = values.getAsInteger(StructuredPostal.TYPE);
11785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (!supportedTypes.contains(oldType)) {
11795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                int defaultType;
11805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (newDataKind.defaultValues != null) {
11815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    defaultType = newDataKind.defaultValues.getAsInteger(StructuredPostal.TYPE);
11825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                } else {
11835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    defaultType = newDataKind.typeList.get(0).rawValue;
11845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
11855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                values.put(StructuredPostal.TYPE, defaultType);
11865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (oldType != null && oldType == StructuredPostal.TYPE_CUSTOM) {
11875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.remove(StructuredPostal.LABEL);
11885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
11895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
11905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
11915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final String formattedAddress = values.getAsString(StructuredPostal.FORMATTED_ADDRESS);
11925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (!TextUtils.isEmpty(formattedAddress)) {
11935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (!supportFormattedAddress) {
11945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    // Old data has a formatted address, while the new account doesn't allow it.
11955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.remove(StructuredPostal.FORMATTED_ADDRESS);
11965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
11975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    // Unlike StructuredName we don't have logic to split it, so first
11985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    // try to use street field and. If the new account doesn't have one,
11995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    // then select first one anyway.
12005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    if (supportStreet) {
12015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        values.put(StructuredPostal.STREET, formattedAddress);
12025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    } else {
12035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        values.put(firstColumn, formattedAddress);
12045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    }
12055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
12065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            } else {
12075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (supportFormattedAddress) {
12085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    // Old data does not have formatted address, while the new account requires it.
12095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    // Unlike StructuredName we don't have logic to join multiple address values.
12105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    // Use poor join heuristics for now.
12115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    String[] structuredData;
12125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    final boolean useJapaneseOrder =
12135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            Locale.JAPANESE.getLanguage().equals(Locale.getDefault().getLanguage());
12145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    if (useJapaneseOrder) {
12155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        structuredData = new String[] {
12165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                values.getAsString(StructuredPostal.COUNTRY),
12175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                values.getAsString(StructuredPostal.POSTCODE),
12185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                values.getAsString(StructuredPostal.REGION),
12195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                values.getAsString(StructuredPostal.CITY),
12205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                values.getAsString(StructuredPostal.NEIGHBORHOOD),
12215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                values.getAsString(StructuredPostal.STREET),
12225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                values.getAsString(StructuredPostal.POBOX) };
12235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    } else {
12245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        structuredData = new String[] {
12255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                values.getAsString(StructuredPostal.POBOX),
12265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                values.getAsString(StructuredPostal.STREET),
12275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                values.getAsString(StructuredPostal.NEIGHBORHOOD),
12285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                values.getAsString(StructuredPostal.CITY),
12295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                values.getAsString(StructuredPostal.REGION),
12305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                values.getAsString(StructuredPostal.POSTCODE),
12315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                values.getAsString(StructuredPostal.COUNTRY) };
12325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    }
12335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    final StringBuilder builder = new StringBuilder();
12345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    for (String elem : structuredData) {
12355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        if (!TextUtils.isEmpty(elem)) {
12365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            builder.append(elem + "\n");
12375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        }
12385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    }
12395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.put(StructuredPostal.FORMATTED_ADDRESS, builder.toString());
12405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
12415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.remove(StructuredPostal.POBOX);
12425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.remove(StructuredPostal.STREET);
12435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.remove(StructuredPostal.NEIGHBORHOOD);
12445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.remove(StructuredPostal.CITY);
12455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.remove(StructuredPostal.REGION);
12465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.remove(StructuredPostal.POSTCODE);
12475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.remove(StructuredPostal.COUNTRY);
12485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
12495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
12505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
12515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            newState.addEntry(ValuesDelta.fromAfter(values));
12525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
12535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
12545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
12555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /** @hide Public only for testing. */
12565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static void migrateEvent(RawContactDelta oldState, RawContactDelta newState,
12575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            DataKind newDataKind, Integer defaultYear) {
12585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind,
12595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                oldState.getMimeEntries(Event.CONTENT_ITEM_TYPE));
12605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (mimeEntries == null || mimeEntries.isEmpty()) {
12615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return;
12625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
12635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
12645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final SparseArray<EventEditType> allowedTypes = new SparseArray<EventEditType>();
12655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (EditType editType : newDataKind.typeList) {
12665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            allowedTypes.put(editType.rawValue, (EventEditType) editType);
12675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
12685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (ValuesDelta entry : mimeEntries) {
12695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final ContentValues values = entry.getAfter();
12705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (values == null) {
12715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                continue;
12725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
12735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final String dateString = values.getAsString(Event.START_DATE);
12745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final Integer type = values.getAsInteger(Event.TYPE);
12755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (type != null && (allowedTypes.indexOfKey(type) >= 0)
12765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    && !TextUtils.isEmpty(dateString)) {
12775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                EventEditType suitableType = allowedTypes.get(type);
12785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
12795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                final ParsePosition position = new ParsePosition(0);
12805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                boolean yearOptional = false;
12815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                Date date = CommonDateUtils.DATE_AND_TIME_FORMAT.parse(dateString, position);
12825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (date == null) {
12835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    yearOptional = true;
12845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    date = CommonDateUtils.NO_YEAR_DATE_FORMAT.parse(dateString, position);
12855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
12865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (date != null) {
12875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    if (yearOptional && !suitableType.isYearOptional()) {
12885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        // The new EditType doesn't allow optional year. Supply default.
12895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        final Calendar calendar = Calendar.getInstance(DateUtils.UTC_TIMEZONE,
12905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                Locale.US);
12915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        if (defaultYear == null) {
12925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                            defaultYear = calendar.get(Calendar.YEAR);
12935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        }
12945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        calendar.setTime(date);
12955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        final int month = calendar.get(Calendar.MONTH);
12965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        final int day = calendar.get(Calendar.DAY_OF_MONTH);
12975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        // Exchange requires 8:00 for birthdays
12985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        calendar.set(defaultYear, month, day,
12995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                CommonDateUtils.DEFAULT_HOUR, 0, 0);
13005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        values.put(Event.START_DATE,
13015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                                CommonDateUtils.FULL_DATE_FORMAT.format(calendar.getTime()));
13025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    }
13035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
13045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                newState.addEntry(ValuesDelta.fromAfter(values));
13055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            } else {
13065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // Just drop it.
13075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
13085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
13095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
13105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
13115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /** @hide Public only for testing. */
13125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static void migrateGenericWithoutTypeColumn(
13135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            RawContactDelta oldState, RawContactDelta newState, DataKind newDataKind) {
13145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind,
13155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                oldState.getMimeEntries(newDataKind.mimeType));
13165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (mimeEntries == null || mimeEntries.isEmpty()) {
13175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return;
13185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
13195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
13205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (ValuesDelta entry : mimeEntries) {
13215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            ContentValues values = entry.getAfter();
13225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (values != null) {
13235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                newState.addEntry(ValuesDelta.fromAfter(values));
13245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
13255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
13265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
13275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
13285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    /** @hide Public only for testing. */
13295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    public static void migrateGenericWithTypeColumn(
13305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            RawContactDelta oldState, RawContactDelta newState, DataKind newDataKind) {
13315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final ArrayList<ValuesDelta> mimeEntries = oldState.getMimeEntries(newDataKind.mimeType);
13325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (mimeEntries == null || mimeEntries.isEmpty()) {
13335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            return;
13345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
13355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
13365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Note that type specified with the old account may be invalid with the new account, while
13375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // we want to preserve its data as much as possible. e.g. if a user typed a phone number
13385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // with a type which is valid with an old account but not with a new account, the user
13395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // probably wants to have the number with default type, rather than seeing complete data
13405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // loss.
13415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        //
13425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Specifically, this method works as follows:
13435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // 1. detect defaultType
13445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // 2. prepare constants & variables for iteration
13455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // 3. iterate over mimeEntries:
13465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // 3.1 stop iteration if total number of mimeEntries reached typeOverallMax specified in
13475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        //     DataKind
13485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // 3.2 replace unallowed types with defaultType
13495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // 3.3 check if the number of entries is below specificMax specified in AccountType
13505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
13515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Here, defaultType can be supplied in two ways
13525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // - via kind.defaultValues
13535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // - via kind.typeList.get(0).rawValue
13545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        Integer defaultType = null;
13555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (newDataKind.defaultValues != null) {
13565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            defaultType = newDataKind.defaultValues.getAsInteger(COLUMN_FOR_TYPE);
13575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
13585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final Set<Integer> allowedTypes = new HashSet<Integer>();
13595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // key: type, value: the number of entries allowed for the type (specificMax)
13605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final SparseIntArray typeSpecificMaxMap = new SparseIntArray();
13615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (defaultType != null) {
13625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            allowedTypes.add(defaultType);
13635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            typeSpecificMaxMap.put(defaultType, -1);
13645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
13655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Note: typeList may be used in different purposes when defaultValues are specified.
13665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // Especially in IM, typeList contains available protocols (e.g. PROTOCOL_GOOGLE_TALK)
13675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // instead of "types" which we want to treate here (e.g. TYPE_HOME). So we don't add
13685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // anything other than defaultType into allowedTypes and typeSpecificMapMax.
13695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (!Im.CONTENT_ITEM_TYPE.equals(newDataKind.mimeType) &&
13705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                newDataKind.typeList != null && !newDataKind.typeList.isEmpty()) {
13715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            for (EditType editType : newDataKind.typeList) {
13725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                allowedTypes.add(editType.rawValue);
13735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                typeSpecificMaxMap.put(editType.rawValue, editType.specificMax);
13745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
13755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (defaultType == null) {
13765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                defaultType = newDataKind.typeList.get(0).rawValue;
13775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
13785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
13795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
13805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        if (defaultType == null) {
13815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            Log.w(TAG, "Default type isn't available for mimetype " + newDataKind.mimeType);
13825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
13835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
13845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final int typeOverallMax = newDataKind.typeOverallMax;
13855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
13865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        // key: type, value: the number of current entries.
13875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        final SparseIntArray currentEntryCount = new SparseIntArray();
13885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        int totalCount = 0;
13895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
13905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        for (ValuesDelta entry : mimeEntries) {
13915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (typeOverallMax != -1 && totalCount >= typeOverallMax) {
13925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                break;
13935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
13945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
13955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final ContentValues values = entry.getAfter();
13965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (values == null) {
13975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                continue;
13985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
13995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee
14005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final Integer oldType = entry.getAsInteger(COLUMN_FOR_TYPE);
14015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            final Integer typeForNewAccount;
14025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (!allowedTypes.contains(oldType)) {
14035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                // The new account doesn't support the type.
14045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (defaultType != null) {
14055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    typeForNewAccount = defaultType.intValue();
14065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.put(COLUMN_FOR_TYPE, defaultType.intValue());
14075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    if (oldType != null && oldType == TYPE_CUSTOM) {
14085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        values.remove(COLUMN_FOR_LABEL);
14095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    }
14105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                } else {
14115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    typeForNewAccount = null;
14125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    values.remove(COLUMN_FOR_TYPE);
14135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
14145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            } else {
14155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                typeForNewAccount = oldType;
14165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
14175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            if (typeForNewAccount != null) {
14185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                final int specificMax = typeSpecificMaxMap.get(typeForNewAccount, 0);
14195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                if (specificMax >= 0) {
14205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    final int currentCount = currentEntryCount.get(typeForNewAccount, 0);
14215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    if (currentCount >= specificMax) {
14225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                        continue;
14235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    }
14245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                    currentEntryCount.put(typeForNewAccount, currentCount + 1);
14255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee                }
14265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            }
14275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            newState.addEntry(ValuesDelta.fromAfter(values));
14285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee            totalCount++;
14295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee        }
14305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee    }
14315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee}
1432