EntityModifier.java revision 6164461a80cf46ecc4b9d4de21a8c2662d5ac220
12ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey/*
22ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Copyright (C) 2009 The Android Open Source Project
32ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey *
42ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
52ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * you may not use this file except in compliance with the License.
62ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * You may obtain a copy of the License at
72ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey *
82ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
92ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey *
102ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Unless required by applicable law or agreed to in writing, software
112ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
122ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
132ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * See the License for the specific language governing permissions and
142ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * limitations under the License.
152ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */
162ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
172ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeypackage com.android.contacts.model;
182ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
192ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeyimport com.android.contacts.model.ContactsSource.DataKind;
206f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkeyimport com.android.contacts.model.ContactsSource.EditField;
212ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeyimport com.android.contacts.model.ContactsSource.EditType;
22d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport com.android.contacts.model.EntityDelta.ValuesDelta;
23aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkeyimport com.google.android.collect.Lists;
242ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
252ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeyimport android.content.ContentValues;
26802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkeyimport android.content.Context;
27802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkeyimport android.database.Cursor;
28d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.os.Bundle;
292ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeyimport android.provider.ContactsContract.Data;
30d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.Intents;
317f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkeyimport android.provider.ContactsContract.RawContacts;
327f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.BaseTypes;
33d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Email;
34d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Im;
35d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Phone;
36d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredName;
37d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
38d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.Intents.Insert;
39d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.text.TextUtils;
406f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkeyimport android.util.Log;
4107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport android.util.SparseIntArray;
422ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
4307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport java.util.ArrayList;
4407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport java.util.Iterator;
452ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeyimport java.util.List;
462ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
472ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey/**
488d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * Helper methods for modifying an {@link EntityDelta}, such as inserting
492ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * new rows, or enforcing {@link ContactsSource}.
502ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */
512ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeypublic class EntityModifier {
526f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    private static final String TAG = "EntityModifier";
536f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
542ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    /**
558d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey     * For the given {@link EntityDelta}, determine if the given
562ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * {@link DataKind} could be inserted under specific
572ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * {@link ContactsSource}.
582ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     */
598d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey    public static boolean canInsert(EntityDelta state, DataKind kind) {
6007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Insert possible when have valid types and under overall maximum
61ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey        final int visibleCount = state.getMimeEntriesCount(kind.mimeType, true);
6207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final boolean validTypes = hasValidTypes(state, kind);
6307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final boolean validOverall = (kind.typeOverallMax == -1)
64ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey                || (visibleCount < kind.typeOverallMax);
6507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        return (validTypes && validOverall);
6607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
6707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
688d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey    public static boolean hasValidTypes(EntityDelta state, DataKind kind) {
69d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        if (EntityModifier.hasEditTypes(kind)) {
70d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            return (getValidTypes(state, kind).size() > 0);
71d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        } else {
72d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            return true;
73d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        }
742ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    }
752ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
762ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    /**
77aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey     * Ensure that at least one of the given {@link DataKind} exists in the
78aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey     * given {@link EntityDelta} state, and try creating one if none exist.
79aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey     */
80aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    public static void ensureKindExists(EntityDelta state, ContactsSource source, String mimeType) {
81aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        final DataKind kind = source.getKindForMimetype(mimeType);
82ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey        final boolean hasChild = state.getMimeEntriesCount(mimeType, true) > 0;
83aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
84aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        if (!hasChild && kind != null) {
85aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey            // Create child when none exists and valid kind
86aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey            insertChild(state, kind);
87aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        }
88aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    }
89aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey
90aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    /**
918d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey     * For the given {@link EntityDelta} and {@link DataKind}, return the
922ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * list possible {@link EditType} options available based on
932ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * {@link ContactsSource}.
942ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     */
958d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey    public static ArrayList<EditType> getValidTypes(EntityDelta state, DataKind kind) {
9607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        return getValidTypes(state, kind, null, true, null);
9707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
9807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
9907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    /**
1008d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey     * For the given {@link EntityDelta} and {@link DataKind}, return the
10107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * list possible {@link EditType} options available based on
10207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * {@link ContactsSource}.
10307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *
10407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * @param forceInclude Always include this {@link EditType} in the returned
10507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *            list, even when an otherwise-invalid choice. This is useful
10607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *            when showing a dialog that includes the current type.
10707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     */
1088d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey    public static ArrayList<EditType> getValidTypes(EntityDelta state, DataKind kind,
1092ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey            EditType forceInclude) {
11007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        return getValidTypes(state, kind, forceInclude, true, null);
11107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
11207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
11307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    /**
1148d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey     * For the given {@link EntityDelta} and {@link DataKind}, return the
11507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * list possible {@link EditType} options available based on
11607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * {@link ContactsSource}.
11707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *
11807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * @param forceInclude Always include this {@link EditType} in the returned
11907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *            list, even when an otherwise-invalid choice. This is useful
12007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *            when showing a dialog that includes the current type.
12107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * @param includeSecondary If true, include any valid types marked as
12207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *            {@link EditType#secondary}.
12307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * @param typeCount When provided, will be used for the frequency count of
12407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     *            each {@link EditType}, otherwise built using
1258d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey     *            {@link #getTypeFrequencies(EntityDelta, DataKind)}.
12607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     */
1278d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey    private static ArrayList<EditType> getValidTypes(EntityDelta state, DataKind kind,
12807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            EditType forceInclude, boolean includeSecondary, SparseIntArray typeCount) {
129aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey        final ArrayList<EditType> validTypes = Lists.newArrayList();
13007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
13107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Bail early if no types provided
13207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (!hasEditTypes(kind)) return validTypes;
13307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
13407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (typeCount == null) {
13507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            // Build frequency counts if not provided
13607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            typeCount = getTypeFrequencies(state, kind);
13707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        }
13807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
13907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Build list of valid types
14007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final int overallCount = typeCount.get(FREQUENCY_TOTAL);
14107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        for (EditType type : kind.typeList) {
142d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            final boolean validOverall = (kind.typeOverallMax == -1 ? true
143d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                    : overallCount < kind.typeOverallMax);
144d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            final boolean validSpecific = (type.specificMax == -1 ? true : typeCount
145d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                    .get(type.rawValue) < type.specificMax);
14607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            final boolean validSecondary = (includeSecondary ? true : !type.secondary);
14707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            final boolean forcedInclude = type.equals(forceInclude);
148d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            if (forcedInclude || (validOverall && validSpecific && validSecondary)) {
14907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                // Type is valid when no limit, under limit, or forced include
15007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                validTypes.add(type);
15107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            }
15207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        }
15307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
15407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        return validTypes;
15507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
15607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
15707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    private static final int FREQUENCY_TOTAL = Integer.MIN_VALUE;
15807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
15907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    /**
16007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * Count up the frequency that each {@link EditType} appears in the given
1618d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey     * {@link EntityDelta}. The returned {@link SparseIntArray} maps from
16207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * {@link EditType#rawValue} to counts, with the total overall count stored
16307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * as {@link #FREQUENCY_TOTAL}.
16407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     */
1658d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey    private static SparseIntArray getTypeFrequencies(EntityDelta state, DataKind kind) {
16607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final SparseIntArray typeCount = new SparseIntArray();
16707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
16807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Find all entries for this kind, bailing early if none found
1698d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey        final List<ValuesDelta> mimeEntries = state.getMimeEntries(kind.mimeType);
17007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (mimeEntries == null) return typeCount;
17107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
17207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        int totalCount = 0;
1738d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey        for (ValuesDelta entry : mimeEntries) {
17407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            // Only count visible entries
17507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            if (!entry.isVisible()) continue;
17607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            totalCount++;
17707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
17807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            final EditType type = getCurrentType(entry, kind);
17907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            if (type != null) {
18007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                final int count = typeCount.get(type.rawValue);
18107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                typeCount.put(type.rawValue, count + 1);
18207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            }
18307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        }
18407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        typeCount.put(FREQUENCY_TOTAL, totalCount);
18507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        return typeCount;
1862ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    }
1872ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
1882ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    /**
1892ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * Check if the given {@link DataKind} has multiple types that should be
1902ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * displayed for users to pick.
1912ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     */
1922ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    public static boolean hasEditTypes(DataKind kind) {
1932ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey        return kind.typeList != null && kind.typeList.size() > 0;
1942ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    }
1952ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
1962ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    /**
1972ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * Find the {@link EditType} that describes the given
1988d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey     * {@link ValuesDelta} row, assuming the given {@link DataKind} dictates
1992ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * the possible types.
2002ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     */
2018d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey    public static EditType getCurrentType(ValuesDelta entry, DataKind kind) {
20207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final Long rawValue = entry.getAsLong(kind.typeColumn);
20307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (rawValue == null) return null;
20407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        return getType(kind, rawValue.intValue());
20507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
20607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
20707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    /**
20811d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar     * Find the {@link EditType} that describes the given {@link ContentValues} row,
20911d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar     * assuming the given {@link DataKind} dictates the possible types.
21011d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar     */
21111d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar    public static EditType getCurrentType(ContentValues entry, DataKind kind) {
21211d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar        if (kind.typeColumn == null) return null;
21349d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey        final Integer rawValue = entry.getAsInteger(kind.typeColumn);
21449d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey        if (rawValue == null) return null;
21511d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar        return getType(kind, rawValue);
21611d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar    }
21711d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar
21811d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar    /**
219802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey     * Find the {@link EditType} that describes the given {@link Cursor} row,
220802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey     * assuming the given {@link DataKind} dictates the possible types.
221802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey     */
222802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey    public static EditType getCurrentType(Cursor cursor, DataKind kind) {
223ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey        if (kind.typeColumn == null) return null;
224802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey        final int index = cursor.getColumnIndex(kind.typeColumn);
22549d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey        if (index == -1) return null;
226802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey        final int rawValue = cursor.getInt(index);
227802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey        return getType(kind, rawValue);
228802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey    }
229802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey
230802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey    /**
23107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * Find the {@link EditType} with the given {@link EditType#rawValue}.
23207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     */
23307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    public static EditType getType(DataKind kind, int rawValue) {
2342ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey        for (EditType type : kind.typeList) {
2352ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey            if (type.rawValue == rawValue) {
2362ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey                return type;
2372ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey            }
2382ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey        }
2392ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey        return null;
2402ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    }
2412ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
2422ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    /**
24349d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey     * Return the precedence for the the given {@link EditType#rawValue}, where
24449d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey     * lower numbers are higher precedence.
24549d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey     */
24649d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey    public static int getTypePrecedence(DataKind kind, int rawValue) {
24749d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey        for (int i = 0; i < kind.typeList.size(); i++) {
24849d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey            final EditType type = kind.typeList.get(i);
24949d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey            if (type.rawValue == rawValue) {
25049d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey                return i;
25149d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey            }
25249d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey        }
25349d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey        return Integer.MAX_VALUE;
25449d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey    }
25549d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey
25649d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey    /**
25707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * Find the best {@link EditType} for a potential insert. The "best" is the
25807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * first primary type that doesn't already exist. When all valid types
25907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * exist, we pick the last valid option.
26007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     */
2618d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey    public static EditType getBestValidType(EntityDelta state, DataKind kind,
262d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            boolean includeSecondary, int exactValue) {
26307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Shortcut when no types
26407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (kind.typeColumn == null) return null;
26507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
26607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Find type counts and valid primary types, bail if none
26707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final SparseIntArray typeCount = getTypeFrequencies(state, kind);
26807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final ArrayList<EditType> validTypes = getValidTypes(state, kind, null, includeSecondary,
26907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                typeCount);
27007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (validTypes.size() == 0) return null;
27107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
27207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Keep track of the last valid type
27307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        final EditType lastType = validTypes.get(validTypes.size() - 1);
27407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
27507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Remove any types that already exist
27607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        Iterator<EditType> iterator = validTypes.iterator();
27707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        while (iterator.hasNext()) {
27807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            final EditType type = iterator.next();
27907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            final int count = typeCount.get(type.rawValue);
28007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
281e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            if (exactValue == type.rawValue) {
282d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                // Found exact value match
283d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                return type;
284d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            }
285d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
28607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            if (count > 0) {
28707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                // Type already appears, so don't consider
28807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey                iterator.remove();
28907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            }
29007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        }
29107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
29207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // Use the best remaining, otherwise the last valid
29307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (validTypes.size() > 0) {
29407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            return validTypes.get(0);
29507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        } else {
29607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            return lastType;
29707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        }
29807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
29907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
30007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    /**
3012ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     * Insert a new child of kind {@link DataKind} into the given
302d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * {@link EntityDelta}. Tries using the best {@link EditType} found using
303d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * {@link #getBestValidType(EntityDelta, DataKind, boolean, int)}.
3042ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey     */
305d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey    public static ValuesDelta insertChild(EntityDelta state, DataKind kind) {
30607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        // First try finding a valid primary
307d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        EditType bestType = getBestValidType(state, kind, false, Integer.MIN_VALUE);
30807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (bestType == null) {
30907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            // No valid primary found, so expand search to secondary
310d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            bestType = getBestValidType(state, kind, true, Integer.MIN_VALUE);
31107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        }
312d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        return insertChild(state, kind, bestType);
31307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    }
31407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey
31507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey    /**
31607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     * Insert a new child of kind {@link DataKind} into the given
3178d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey     * {@link EntityDelta}, marked with the given {@link EditType}.
31807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey     */
319d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey    public static ValuesDelta insertChild(EntityDelta state, DataKind kind, EditType type) {
320d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        // Bail early if invalid kind
321d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        if (kind == null) return null;
322d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
3232ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey        final ContentValues after = new ContentValues();
3242ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
325e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey        // Our parent CONTACT_ID is provided later
3262ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey        after.put(Data.MIMETYPE, kind.mimeType);
327e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey
328e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey        // Fill-in with any requested default values
329e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey        if (kind.defaultValues != null) {
330e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey            after.putAll(kind.defaultValues);
331e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey        }
332e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey
33307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey        if (kind.typeColumn != null && type != null) {
33407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            // Set type, if provided
33507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey            after.put(kind.typeColumn, type.rawValue);
336e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey        }
3372ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
338d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        final ValuesDelta child = ValuesDelta.fromAfter(after);
339d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        state.addEntry(child);
340d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        return child;
3412ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey    }
3422ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey
343d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey    /**
3447f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey     * Processing to trim any empty {@link ValuesDelta} and {@link EntityDelta}
3457f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey     * from the given {@link EntitySet}, assuming the given {@link Sources}
3467f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey     * dictates the structure for various fields. This method ignores rows not
3477f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey     * described by the {@link ContactsSource}.
3487f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey     */
3497f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey    public static void trimEmpty(EntitySet set, Sources sources) {
3507f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        for (EntityDelta state : set) {
3517f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey            final String accountType = state.getValues().getAsString(RawContacts.ACCOUNT_TYPE);
3527f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey            final ContactsSource source = sources.getInflatedSource(accountType,
3537f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                    ContactsSource.LEVEL_MIMETYPES);
3547f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey            trimEmpty(state, source);
3557f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        }
3567f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey    }
3577f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey
3587f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey    /**
3596f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     * Processing to trim any empty {@link ValuesDelta} rows from the given
3606f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     * {@link EntityDelta}, assuming the given {@link ContactsSource} dictates
3616f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     * the structure for various fields. This method ignores rows not described
3626f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     * by the {@link ContactsSource}.
3636f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     */
3647f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey    public static void trimEmpty(EntityDelta state, ContactsSource source) {
3657f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        boolean hasValues = false;
3667f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey
3676f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey        // Walk through entries for each well-known kind
3686f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey        for (DataKind kind : source.getSortedDataKinds()) {
3696f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            final String mimeType = kind.mimeType;
3706f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            final ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType);
3716f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            if (entries == null) continue;
3726f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
3736f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            for (ValuesDelta entry : entries) {
3747f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                // Skip any values that haven't been touched
3756f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey                final boolean touched = entry.isInsert() || entry.isUpdate();
3767f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                if (!touched) {
3777f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                    hasValues = true;
3787f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                    continue;
3797f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                }
3807f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey
3817f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                // Test and remove this row if empty
3827f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                if (EntityModifier.isEmpty(entry, kind)) {
3836f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey                    // TODO: remove this verbose logging
3846f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey                    Log.w(TAG, "Trimming: " + entry.toString());
3856f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey                    entry.markDeleted();
3867f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                } else {
3877f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey                    hasValues = true;
3886f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey                }
3896f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            }
3906f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey        }
3917f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey
3927f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        if (!hasValues) {
3937f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey            // Trim overall entity if no children exist
3947f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey            state.markDeleted();
3957f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        }
3966f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    }
3976f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
3986f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    /**
3996f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     * Test if the given {@link ValuesDelta} would be considered "empty" in
4006f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     * terms of {@link DataKind#fieldList}.
4016f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey     */
4026f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    public static boolean isEmpty(ValuesDelta values, DataKind kind) {
4036f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey        boolean hasValues = false;
4046f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey        for (EditField field : kind.fieldList) {
4056f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            // If any field has values, we're not empty
4066f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            final String value = values.getAsString(field.column);
4076f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            if (!TextUtils.isEmpty(value)) {
4086f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey                hasValues = true;
4096f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey            }
4106f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey        }
4116f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
4126f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey        return !hasValues;
4136f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    }
4146f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey
4156f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey    /**
416d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * Parse the given {@link Bundle} into the given {@link EntityDelta} state,
417d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * assuming the extras defined through {@link Intents}.
418d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     */
419aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey    public static void parseExtras(Context context, ContactsSource source, EntityDelta state,
420aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey            Bundle extras) {
421d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey        if (extras == null || extras.size() == 0) {
422d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey            // Bail early if no useful data
423d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey            return;
424d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey        }
425d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey
426d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        {
427d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            // StructuredName
428ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey            EntityModifier.ensureKindExists(state, source, StructuredName.CONTENT_ITEM_TYPE);
429ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey            final ValuesDelta child = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE);
430ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey
431d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            final String name = extras.getString(Insert.NAME);
432ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey            if (!TextUtils.isEmpty(name) && TextUtils.isGraphic(name)) {
433ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey                child.put(StructuredName.GIVEN_NAME, name);
434ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey            }
435ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey
436d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            final String phoneticName = extras.getString(Insert.PHONETIC_NAME);
437ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey            if (!TextUtils.isEmpty(phoneticName) && TextUtils.isGraphic(phoneticName)) {
438d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                child.put(StructuredName.PHONETIC_GIVEN_NAME, phoneticName);
439d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            }
440d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        }
441d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
442d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        {
443d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            // StructuredPostal
444d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            final DataKind kind = source.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE);
445d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            parseExtras(state, kind, extras, Insert.POSTAL_TYPE, Insert.POSTAL,
446a62e6db031a02bb04a57049839545e378d3b72c2Neel Parekh                    StructuredPostal.STREET);
447d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        }
448d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
449d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        {
450d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            // Phone
451d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            final DataKind kind = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
452d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            parseExtras(state, kind, extras, Insert.PHONE_TYPE, Insert.PHONE, Phone.NUMBER);
453d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            parseExtras(state, kind, extras, Insert.SECONDARY_PHONE_TYPE, Insert.SECONDARY_PHONE,
454d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                    Phone.NUMBER);
455d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            parseExtras(state, kind, extras, Insert.TERTIARY_PHONE_TYPE, Insert.TERTIARY_PHONE,
456d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                    Phone.NUMBER);
457d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        }
458d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
459d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        {
460d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            // Email
461d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            final DataKind kind = source.getKindForMimetype(Email.CONTENT_ITEM_TYPE);
462d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey            parseExtras(state, kind, extras, Insert.EMAIL_TYPE, Insert.EMAIL, Email.DATA);
463d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            parseExtras(state, kind, extras, Insert.SECONDARY_EMAIL_TYPE, Insert.SECONDARY_EMAIL,
464d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                    Email.DATA);
465d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            parseExtras(state, kind, extras, Insert.TERTIARY_EMAIL_TYPE, Insert.TERTIARY_EMAIL,
466d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey                    Email.DATA);
467d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        }
468d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
469d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        {
470d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            // Im
471d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            final DataKind kind = source.getKindForMimetype(Im.CONTENT_ITEM_TYPE);
472e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            fixupLegacyImType(extras);
473d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            parseExtras(state, kind, extras, Insert.IM_PROTOCOL, Insert.IM_HANDLE, Im.DATA);
474d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        }
475d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey    }
476d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
477d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey    /**
478e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey     * Attempt to parse legacy {@link Insert#IM_PROTOCOL} values, replacing them
479e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey     * with updated values.
480e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey     */
481e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey    private static void fixupLegacyImType(Bundle bundle) {
482e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey        final String encodedString = bundle.getString(Insert.IM_PROTOCOL);
483e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey        if (encodedString == null) return;
484e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey
485e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey        try {
486e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            final Object protocol = android.provider.Contacts.ContactMethods
487e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey                    .decodeImProtocol(encodedString);
488e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            if (protocol instanceof Integer) {
489e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey                bundle.putInt(Insert.IM_PROTOCOL, (Integer)protocol);
490e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            } else {
491e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey                bundle.putString(Insert.IM_PROTOCOL, (String)protocol);
492e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            }
493e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey        } catch (IllegalArgumentException e) {
494e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            // Ignore exception when legacy parser fails
495e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey        }
496e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey    }
497e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey
498e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey    /**
499d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * Parse a specific entry from the given {@link Bundle} and insert into the
500d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * given {@link EntityDelta}. Silently skips the insert when missing value
501d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * or no valid {@link EditType} found.
502d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     *
503d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * @param typeExtra {@link Bundle} key that holds the incoming
504d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     *            {@link EditType#rawValue} value.
505d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * @param valueExtra {@link Bundle} key that holds the incoming value.
506d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     * @param valueColumn Column to write value into {@link ValuesDelta}.
507d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey     */
508d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey    public static void parseExtras(EntityDelta state, DataKind kind, Bundle extras,
509d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            String typeExtra, String valueExtra, String valueColumn) {
5107f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        final CharSequence value = extras.getCharSequence(valueExtra);
5117f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey
5127f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        // Bail early if source doesn't handle this type
5137f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        if (kind == null) return;
514d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
515d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        // Bail when can't insert type, or value missing
516d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        final boolean canInsert = EntityModifier.canInsert(state, kind);
517d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey        final boolean validValue = (value != null && TextUtils.isGraphic(value));
518d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        if (!validValue || !canInsert) return;
519d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
5206164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey        // Find exact type when requested, otherwise best available type
5216164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey        final boolean hasType = extras.containsKey(typeExtra);
5226164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey        final int typeValue = extras.getInt(typeExtra, hasType ? BaseTypes.TYPE_CUSTOM
5236164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey                : Integer.MIN_VALUE);
524d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        final EditType editType = EntityModifier.getBestValidType(state, kind, true, typeValue);
525d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
526d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        // Create data row and fill with value
527d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        final ValuesDelta child = EntityModifier.insertChild(state, kind, editType);
5287f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey        child.put(valueColumn, value.toString());
529d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey
530d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        if (editType != null && editType.customColumn != null) {
531d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey            // Write down label when custom type picked
532e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            final String customType = extras.getString(typeExtra);
533e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey            child.put(editType.customColumn, customType);
534d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey        }
535d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey    }
5362ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey}
537