EntityModifier.java revision 6a91292251913b3c0c8add733326cc70eae4cf51
12ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey/* 22ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Copyright (C) 2009 The Android Open Source Project 32ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * 42ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License"); 52ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * you may not use this file except in compliance with the License. 62ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * You may obtain a copy of the License at 72ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * 82ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * http://www.apache.org/licenses/LICENSE-2.0 92ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * 102ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Unless required by applicable law or agreed to in writing, software 112ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS, 122ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * See the License for the specific language governing permissions and 142ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * limitations under the License. 152ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 162ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 172ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeypackage com.android.contacts.model; 182ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 19e31dac84479a4c7b356edfc062a447cdfb5efc69Jeff Sharkeyimport com.android.contacts.ContactsUtils; 2035769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmannimport com.android.contacts.model.BaseAccountType.DataKind; 2135769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmannimport com.android.contacts.model.BaseAccountType.EditField; 2235769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmannimport com.android.contacts.model.BaseAccountType.EditType; 23d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport com.android.contacts.model.EntityDelta.ValuesDelta; 24aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkeyimport com.google.android.collect.Lists; 252ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 262ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeyimport android.content.ContentValues; 27802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkeyimport android.content.Context; 28802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkeyimport android.database.Cursor; 29d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.os.Bundle; 302ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeyimport android.provider.ContactsContract.Data; 31d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.Intents; 327f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkeyimport android.provider.ContactsContract.RawContacts; 337f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.BaseTypes; 34d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Email; 35d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Im; 3616c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Note; 3716c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Organization; 38d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Phone; 39a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintanaimport android.provider.ContactsContract.CommonDataKinds.Photo; 40d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredName; 41d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal; 42d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.Intents.Insert; 43d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.text.TextUtils; 446f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkeyimport android.util.Log; 4507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport android.util.SparseIntArray; 462ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 4707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport java.util.ArrayList; 4807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport java.util.Iterator; 492ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeyimport java.util.List; 502ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 512ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey/** 528d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * Helper methods for modifying an {@link EntityDelta}, such as inserting 5335769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann * new rows, or enforcing {@link BaseAccountType}. 542ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 552ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeypublic class EntityModifier { 566f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey private static final String TAG = "EntityModifier"; 576f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 582ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 598d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * For the given {@link EntityDelta}, determine if the given 602ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * {@link DataKind} could be inserted under specific 6135769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann * {@link BaseAccountType}. 622ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 638d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey public static boolean canInsert(EntityDelta state, DataKind kind) { 6407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Insert possible when have valid types and under overall maximum 65ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey final int visibleCount = state.getMimeEntriesCount(kind.mimeType, true); 6607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final boolean validTypes = hasValidTypes(state, kind); 6707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final boolean validOverall = (kind.typeOverallMax == -1) 68ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey || (visibleCount < kind.typeOverallMax); 6907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return (validTypes && validOverall); 7007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 7107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 728d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey public static boolean hasValidTypes(EntityDelta state, DataKind kind) { 73d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey if (EntityModifier.hasEditTypes(kind)) { 74d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return (getValidTypes(state, kind).size() > 0); 75d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } else { 76d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return true; 77d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 782ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 792ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 802ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 81aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * Ensure that at least one of the given {@link DataKind} exists in the 82aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * given {@link EntityDelta} state, and try creating one if none exist. 83aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey */ 8435769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann public static void ensureKindExists(EntityDelta state, BaseAccountType source, String mimeType) { 85aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey final DataKind kind = source.getKindForMimetype(mimeType); 86ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey final boolean hasChild = state.getMimeEntriesCount(mimeType, true) > 0; 87aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey 88aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey if (!hasChild && kind != null) { 89aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey // Create child when none exists and valid kind 90e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi final ValuesDelta child = insertChild(state, kind); 91e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi if (kind.mimeType.equals(Photo.CONTENT_ITEM_TYPE)) { 92e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi child.setFromTemplate(true); 93e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi } 94aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey } 95aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey } 96aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey 97aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey /** 988d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * For the given {@link EntityDelta} and {@link DataKind}, return the 992ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * list possible {@link EditType} options available based on 10035769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann * {@link BaseAccountType}. 1012ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 1028d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey public static ArrayList<EditType> getValidTypes(EntityDelta state, DataKind kind) { 10307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return getValidTypes(state, kind, null, true, null); 10407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 10507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 10607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 1078d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * For the given {@link EntityDelta} and {@link DataKind}, return the 10807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * list possible {@link EditType} options available based on 10935769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann * {@link BaseAccountType}. 11007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * 11107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * @param forceInclude Always include this {@link EditType} in the returned 11207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * list, even when an otherwise-invalid choice. This is useful 11307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * when showing a dialog that includes the current type. 11407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 1158d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey public static ArrayList<EditType> getValidTypes(EntityDelta state, DataKind kind, 1162ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey EditType forceInclude) { 11707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return getValidTypes(state, kind, forceInclude, true, null); 11807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 11907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 12007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 1218d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * For the given {@link EntityDelta} and {@link DataKind}, return the 12207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * list possible {@link EditType} options available based on 12335769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann * {@link BaseAccountType}. 12407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * 12507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * @param forceInclude Always include this {@link EditType} in the returned 12607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * list, even when an otherwise-invalid choice. This is useful 12707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * when showing a dialog that includes the current type. 12807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * @param includeSecondary If true, include any valid types marked as 12907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * {@link EditType#secondary}. 13007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * @param typeCount When provided, will be used for the frequency count of 13107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * each {@link EditType}, otherwise built using 1328d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * {@link #getTypeFrequencies(EntityDelta, DataKind)}. 13307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 1348d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey private static ArrayList<EditType> getValidTypes(EntityDelta state, DataKind kind, 13507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey EditType forceInclude, boolean includeSecondary, SparseIntArray typeCount) { 136aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey final ArrayList<EditType> validTypes = Lists.newArrayList(); 13707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 13807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Bail early if no types provided 13907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (!hasEditTypes(kind)) return validTypes; 14007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 14107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (typeCount == null) { 14207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Build frequency counts if not provided 14307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey typeCount = getTypeFrequencies(state, kind); 14407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 14507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 14607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Build list of valid types 14707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final int overallCount = typeCount.get(FREQUENCY_TOTAL); 14807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey for (EditType type : kind.typeList) { 149d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final boolean validOverall = (kind.typeOverallMax == -1 ? true 150d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey : overallCount < kind.typeOverallMax); 151d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final boolean validSpecific = (type.specificMax == -1 ? true : typeCount 152d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey .get(type.rawValue) < type.specificMax); 15307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final boolean validSecondary = (includeSecondary ? true : !type.secondary); 15407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final boolean forcedInclude = type.equals(forceInclude); 155d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey if (forcedInclude || (validOverall && validSpecific && validSecondary)) { 15607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Type is valid when no limit, under limit, or forced include 15707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey validTypes.add(type); 15807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 15907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 16007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 16107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return validTypes; 16207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 16307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 16407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey private static final int FREQUENCY_TOTAL = Integer.MIN_VALUE; 16507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 16607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 16707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * Count up the frequency that each {@link EditType} appears in the given 1688d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * {@link EntityDelta}. The returned {@link SparseIntArray} maps from 16907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * {@link EditType#rawValue} to counts, with the total overall count stored 17007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * as {@link #FREQUENCY_TOTAL}. 17107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 1728d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey private static SparseIntArray getTypeFrequencies(EntityDelta state, DataKind kind) { 17307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final SparseIntArray typeCount = new SparseIntArray(); 17407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 17507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Find all entries for this kind, bailing early if none found 1768d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey final List<ValuesDelta> mimeEntries = state.getMimeEntries(kind.mimeType); 17707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (mimeEntries == null) return typeCount; 17807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 17907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey int totalCount = 0; 1808d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey for (ValuesDelta entry : mimeEntries) { 18107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Only count visible entries 18207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (!entry.isVisible()) continue; 18307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey totalCount++; 18407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 18507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final EditType type = getCurrentType(entry, kind); 18607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (type != null) { 18707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final int count = typeCount.get(type.rawValue); 18807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey typeCount.put(type.rawValue, count + 1); 18907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 19007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 19107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey typeCount.put(FREQUENCY_TOTAL, totalCount); 19207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return typeCount; 1932ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 1942ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 1952ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 1962ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Check if the given {@link DataKind} has multiple types that should be 1972ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * displayed for users to pick. 1982ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 1992ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey public static boolean hasEditTypes(DataKind kind) { 2002ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey return kind.typeList != null && kind.typeList.size() > 0; 2012ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 2022ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 2032ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 2042ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Find the {@link EditType} that describes the given 2058d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * {@link ValuesDelta} row, assuming the given {@link DataKind} dictates 2062ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * the possible types. 2072ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 2088d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey public static EditType getCurrentType(ValuesDelta entry, DataKind kind) { 20907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final Long rawValue = entry.getAsLong(kind.typeColumn); 21007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (rawValue == null) return null; 21107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return getType(kind, rawValue.intValue()); 21207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 21307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 21407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 21511d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar * Find the {@link EditType} that describes the given {@link ContentValues} row, 21611d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar * assuming the given {@link DataKind} dictates the possible types. 21711d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar */ 21811d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar public static EditType getCurrentType(ContentValues entry, DataKind kind) { 21911d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar if (kind.typeColumn == null) return null; 22049d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey final Integer rawValue = entry.getAsInteger(kind.typeColumn); 22149d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey if (rawValue == null) return null; 22211d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar return getType(kind, rawValue); 22311d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar } 22411d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar 22511d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar /** 226802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey * Find the {@link EditType} that describes the given {@link Cursor} row, 227802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey * assuming the given {@link DataKind} dictates the possible types. 228802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey */ 229802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey public static EditType getCurrentType(Cursor cursor, DataKind kind) { 230ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey if (kind.typeColumn == null) return null; 231802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey final int index = cursor.getColumnIndex(kind.typeColumn); 23249d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey if (index == -1) return null; 233802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey final int rawValue = cursor.getInt(index); 234802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey return getType(kind, rawValue); 235802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey } 236802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey 237802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey /** 23807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * Find the {@link EditType} with the given {@link EditType#rawValue}. 23907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 24007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey public static EditType getType(DataKind kind, int rawValue) { 2412ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey for (EditType type : kind.typeList) { 2422ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey if (type.rawValue == rawValue) { 2432ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey return type; 2442ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 2452ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 2462ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey return null; 2472ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 2482ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 2492ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 25049d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey * Return the precedence for the the given {@link EditType#rawValue}, where 25149d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey * lower numbers are higher precedence. 25249d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey */ 25349d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey public static int getTypePrecedence(DataKind kind, int rawValue) { 25449d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey for (int i = 0; i < kind.typeList.size(); i++) { 25549d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey final EditType type = kind.typeList.get(i); 25649d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey if (type.rawValue == rawValue) { 25749d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey return i; 25849d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey } 25949d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey } 26049d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey return Integer.MAX_VALUE; 26149d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey } 26249d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey 26349d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey /** 26407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * Find the best {@link EditType} for a potential insert. The "best" is the 26507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * first primary type that doesn't already exist. When all valid types 26607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * exist, we pick the last valid option. 26707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 2688d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey public static EditType getBestValidType(EntityDelta state, DataKind kind, 269d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey boolean includeSecondary, int exactValue) { 27007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Shortcut when no types 27107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (kind.typeColumn == null) return null; 27207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 27307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Find type counts and valid primary types, bail if none 27407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final SparseIntArray typeCount = getTypeFrequencies(state, kind); 27507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final ArrayList<EditType> validTypes = getValidTypes(state, kind, null, includeSecondary, 27607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey typeCount); 27707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (validTypes.size() == 0) return null; 27807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 27907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Keep track of the last valid type 28007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final EditType lastType = validTypes.get(validTypes.size() - 1); 28107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 28207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Remove any types that already exist 28307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey Iterator<EditType> iterator = validTypes.iterator(); 28407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey while (iterator.hasNext()) { 28507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final EditType type = iterator.next(); 28607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final int count = typeCount.get(type.rawValue); 28707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 288e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey if (exactValue == type.rawValue) { 289d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Found exact value match 290d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return type; 291d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 292d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 29307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (count > 0) { 29407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Type already appears, so don't consider 29507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey iterator.remove(); 29607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 29707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 29807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 29907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Use the best remaining, otherwise the last valid 30007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (validTypes.size() > 0) { 30107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return validTypes.get(0); 30207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } else { 30307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return lastType; 30407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 30507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 30607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 30707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 3082ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Insert a new child of kind {@link DataKind} into the given 309d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * {@link EntityDelta}. Tries using the best {@link EditType} found using 310d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * {@link #getBestValidType(EntityDelta, DataKind, boolean, int)}. 3112ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 312d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey public static ValuesDelta insertChild(EntityDelta state, DataKind kind) { 31307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // First try finding a valid primary 314d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey EditType bestType = getBestValidType(state, kind, false, Integer.MIN_VALUE); 31507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (bestType == null) { 31607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // No valid primary found, so expand search to secondary 317d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey bestType = getBestValidType(state, kind, true, Integer.MIN_VALUE); 31807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 319d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return insertChild(state, kind, bestType); 32007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 32107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 32207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 32307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * Insert a new child of kind {@link DataKind} into the given 3248d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * {@link EntityDelta}, marked with the given {@link EditType}. 32507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 326d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey public static ValuesDelta insertChild(EntityDelta state, DataKind kind, EditType type) { 327d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Bail early if invalid kind 328d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey if (kind == null) return null; 3292ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey final ContentValues after = new ContentValues(); 3302ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 331e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey // Our parent CONTACT_ID is provided later 3322ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey after.put(Data.MIMETYPE, kind.mimeType); 333e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey 334e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey // Fill-in with any requested default values 335e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey if (kind.defaultValues != null) { 336e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey after.putAll(kind.defaultValues); 337e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey } 338e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey 33907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (kind.typeColumn != null && type != null) { 34007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Set type, if provided 34107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey after.put(kind.typeColumn, type.rawValue); 342e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey } 3432ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 344d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final ValuesDelta child = ValuesDelta.fromAfter(after); 345e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi state.addEntry(child); 346d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return child; 3472ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 3482ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 349d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey /** 3507f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey * Processing to trim any empty {@link ValuesDelta} and {@link EntityDelta} 35135769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann * from the given {@link EntityDeltaList}, assuming the given {@link AccountTypes} 3527f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey * dictates the structure for various fields. This method ignores rows not 35335769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann * described by the {@link BaseAccountType}. 3547f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey */ 35535769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann public static void trimEmpty(EntityDeltaList set, AccountTypes sources) { 3567f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey for (EntityDelta state : set) { 3577f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey final String accountType = state.getValues().getAsString(RawContacts.ACCOUNT_TYPE); 35835769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann final BaseAccountType source = sources.getInflatedSource(accountType, 35935769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann BaseAccountType.LEVEL_MIMETYPES); 3607f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey trimEmpty(state, source); 3617f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey } 3627f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey } 3637f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey 3647f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey /** 3656f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey * Processing to trim any empty {@link ValuesDelta} rows from the given 36635769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann * {@link EntityDelta}, assuming the given {@link BaseAccountType} dictates 3676f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey * the structure for various fields. This method ignores rows not described 36835769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann * by the {@link BaseAccountType}. 3696f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey */ 37035769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann public static void trimEmpty(EntityDelta state, BaseAccountType source) { 3717f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey boolean hasValues = false; 3727f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey 3736f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey // Walk through entries for each well-known kind 3746f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey for (DataKind kind : source.getSortedDataKinds()) { 3756f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey final String mimeType = kind.mimeType; 3766f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey final ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType); 3776f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey if (entries == null) continue; 3786f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 3796f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey for (ValuesDelta entry : entries) { 3807f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey // Skip any values that haven't been touched 3816f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey final boolean touched = entry.isInsert() || entry.isUpdate(); 3827f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey if (!touched) { 3837f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey hasValues = true; 3847f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey continue; 3857f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey } 3867f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey 387a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana // Test and remove this row if empty and it isn't a photo from google 38835769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann final boolean isGoogleSource = TextUtils.equals(GoogleAccountType.ACCOUNT_TYPE, 389a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana state.getValues().getAsString(RawContacts.ACCOUNT_TYPE)); 390a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana final boolean isPhoto = TextUtils.equals(Photo.CONTENT_ITEM_TYPE, kind.mimeType); 391a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana final boolean isGooglePhoto = isPhoto && isGoogleSource; 392e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi 393a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana if (EntityModifier.isEmpty(entry, kind) && !isGooglePhoto) { 3946f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey // TODO: remove this verbose logging 3956f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey Log.w(TAG, "Trimming: " + entry.toString()); 3966f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey entry.markDeleted(); 397d2fdb9095e0009408b89e24048154ea7a8a14413Megha Joshi } else if (!entry.isFromTemplate()) { 3987f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey hasValues = true; 3996f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4006f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4016f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4027f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey if (!hasValues) { 4037f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey // Trim overall entity if no children exist 4047f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey state.markDeleted(); 4057f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey } 4066f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4076f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 4086f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey /** 4096f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey * Test if the given {@link ValuesDelta} would be considered "empty" in 4106f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey * terms of {@link DataKind#fieldList}. 4116f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey */ 4126f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey public static boolean isEmpty(ValuesDelta values, DataKind kind) { 413dbeb965d19a3c03e78cd54d9fbe7337b4fbb06f2Jeff Sharkey // No defined fields mean this row is always empty 414dbeb965d19a3c03e78cd54d9fbe7337b4fbb06f2Jeff Sharkey if (kind.fieldList == null) return true; 415dbeb965d19a3c03e78cd54d9fbe7337b4fbb06f2Jeff Sharkey 4166f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey boolean hasValues = false; 4176f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey for (EditField field : kind.fieldList) { 4186f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey // If any field has values, we're not empty 4196f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey final String value = values.getAsString(field.column); 420e31dac84479a4c7b356edfc062a447cdfb5efc69Jeff Sharkey if (ContactsUtils.isGraphic(value)) { 4216f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey hasValues = true; 4226f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4236f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4246f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 4256f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey return !hasValues; 4266f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4276f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 4286f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey /** 429d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * Parse the given {@link Bundle} into the given {@link EntityDelta} state, 430d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * assuming the extras defined through {@link Intents}. 431d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey */ 43235769b804fbfd5a1fc0b2c36cd0a786d662c4334Daniel Lehmann public static void parseExtras(Context context, BaseAccountType source, EntityDelta state, 433aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey Bundle extras) { 434d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey if (extras == null || extras.size() == 0) { 435d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey // Bail early if no useful data 436d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey return; 437d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey } 438d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey 439d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey { 440d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // StructuredName 441ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey EntityModifier.ensureKindExists(state, source, StructuredName.CONTENT_ITEM_TYPE); 442ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey final ValuesDelta child = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE); 443ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey 444d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final String name = extras.getString(Insert.NAME); 445e31dac84479a4c7b356edfc062a447cdfb5efc69Jeff Sharkey if (ContactsUtils.isGraphic(name)) { 446e17b1e52e6895f8177b4192f34126000fcb3f983Daniel Lehmann child.put(StructuredName.DISPLAY_NAME, name); 447ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey } 448ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey 449d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final String phoneticName = extras.getString(Insert.PHONETIC_NAME); 450e31dac84479a4c7b356edfc062a447cdfb5efc69Jeff Sharkey if (ContactsUtils.isGraphic(phoneticName)) { 451d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey child.put(StructuredName.PHONETIC_GIVEN_NAME, phoneticName); 452d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 453d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 454d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 455d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey { 456d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // StructuredPostal 457d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final DataKind kind = source.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE); 458d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.POSTAL_TYPE, Insert.POSTAL, 459e17b1e52e6895f8177b4192f34126000fcb3f983Daniel Lehmann StructuredPostal.FORMATTED_ADDRESS); 460d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 461d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 462d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey { 463d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Phone 464d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final DataKind kind = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); 465d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.PHONE_TYPE, Insert.PHONE, Phone.NUMBER); 466d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.SECONDARY_PHONE_TYPE, Insert.SECONDARY_PHONE, 467d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey Phone.NUMBER); 468d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.TERTIARY_PHONE_TYPE, Insert.TERTIARY_PHONE, 469d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey Phone.NUMBER); 470d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 471d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 472d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey { 473d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Email 474d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final DataKind kind = source.getKindForMimetype(Email.CONTENT_ITEM_TYPE); 475d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey parseExtras(state, kind, extras, Insert.EMAIL_TYPE, Insert.EMAIL, Email.DATA); 476d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.SECONDARY_EMAIL_TYPE, Insert.SECONDARY_EMAIL, 477d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey Email.DATA); 478d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.TERTIARY_EMAIL_TYPE, Insert.TERTIARY_EMAIL, 479d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey Email.DATA); 480d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 481d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 482d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey { 483d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Im 484d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final DataKind kind = source.getKindForMimetype(Im.CONTENT_ITEM_TYPE); 485e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey fixupLegacyImType(extras); 486d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.IM_PROTOCOL, Insert.IM_HANDLE, Im.DATA); 487d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 48816c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey 48916c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey // Organization 49016c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final boolean hasOrg = extras.containsKey(Insert.COMPANY) 49116c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey || extras.containsKey(Insert.JOB_TITLE); 49216c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final DataKind kindOrg = source.getKindForMimetype(Organization.CONTENT_ITEM_TYPE); 49316c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey if (hasOrg && EntityModifier.canInsert(state, kindOrg)) { 49416c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final ValuesDelta child = EntityModifier.insertChild(state, kindOrg); 49516c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey 49616c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final String company = extras.getString(Insert.COMPANY); 49716c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey if (ContactsUtils.isGraphic(company)) { 49816c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey child.put(Organization.COMPANY, company); 49916c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey } 50016c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey 50116c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final String title = extras.getString(Insert.JOB_TITLE); 50216c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey if (ContactsUtils.isGraphic(title)) { 50316c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey child.put(Organization.TITLE, title); 50416c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey } 50516c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey } 50616c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey 50716c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey // Notes 50816c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final boolean hasNotes = extras.containsKey(Insert.NOTES); 50916c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final DataKind kindNotes = source.getKindForMimetype(Note.CONTENT_ITEM_TYPE); 51016c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey if (hasNotes && EntityModifier.canInsert(state, kindNotes)) { 51116c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final ValuesDelta child = EntityModifier.insertChild(state, kindNotes); 51216c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey 51316c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final String notes = extras.getString(Insert.NOTES); 51416c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey if (ContactsUtils.isGraphic(notes)) { 51516c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey child.put(Note.NOTE, notes); 51616c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey } 51716c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey } 5186a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov 5196a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov // Arbitrary additional data 5206a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov { 5216a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov// ArrayList<ContentValues> values = extras.getParcelableArrayList(Insert.DATA); 5226a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov// if (values != null) { 5236a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov// parseValues(state, values); 5246a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov// } 5256a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov } 5266a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov } 5276a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov 5286a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov private static void parseValues(EntityDelta state, ArrayList<ContentValues> values) { 5296a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov for (ContentValues contentValues : values) { 5306a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov String mimeType = contentValues.getAsString(Data.MIMETYPE); 5316a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov } 532d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 533d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 534d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey /** 535e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey * Attempt to parse legacy {@link Insert#IM_PROTOCOL} values, replacing them 536e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey * with updated values. 537e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey */ 538e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey private static void fixupLegacyImType(Bundle bundle) { 539e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey final String encodedString = bundle.getString(Insert.IM_PROTOCOL); 540e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey if (encodedString == null) return; 541e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey 542e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey try { 543e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey final Object protocol = android.provider.Contacts.ContactMethods 544e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey .decodeImProtocol(encodedString); 545e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey if (protocol instanceof Integer) { 546e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey bundle.putInt(Insert.IM_PROTOCOL, (Integer)protocol); 547e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } else { 548e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey bundle.putString(Insert.IM_PROTOCOL, (String)protocol); 549e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } 550e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } catch (IllegalArgumentException e) { 551e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey // Ignore exception when legacy parser fails 552e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } 553e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } 554e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey 555e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey /** 556d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * Parse a specific entry from the given {@link Bundle} and insert into the 557d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * given {@link EntityDelta}. Silently skips the insert when missing value 558d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * or no valid {@link EditType} found. 559d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * 560d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * @param typeExtra {@link Bundle} key that holds the incoming 561d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * {@link EditType#rawValue} value. 562d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * @param valueExtra {@link Bundle} key that holds the incoming value. 563d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * @param valueColumn Column to write value into {@link ValuesDelta}. 564d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey */ 565d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey public static void parseExtras(EntityDelta state, DataKind kind, Bundle extras, 566d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey String typeExtra, String valueExtra, String valueColumn) { 5677f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey final CharSequence value = extras.getCharSequence(valueExtra); 5687f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey 5697f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey // Bail early if source doesn't handle this type 5707f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey if (kind == null) return; 571d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 572d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Bail when can't insert type, or value missing 573d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final boolean canInsert = EntityModifier.canInsert(state, kind); 574d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey final boolean validValue = (value != null && TextUtils.isGraphic(value)); 575d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey if (!validValue || !canInsert) return; 576d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 5776164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey // Find exact type when requested, otherwise best available type 5786164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey final boolean hasType = extras.containsKey(typeExtra); 5796164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey final int typeValue = extras.getInt(typeExtra, hasType ? BaseTypes.TYPE_CUSTOM 5806164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey : Integer.MIN_VALUE); 581d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final EditType editType = EntityModifier.getBestValidType(state, kind, true, typeValue); 582d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 583d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Create data row and fill with value 584d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final ValuesDelta child = EntityModifier.insertChild(state, kind, editType); 5857f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey child.put(valueColumn, value.toString()); 586d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 587d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey if (editType != null && editType.customColumn != null) { 588d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Write down label when custom type picked 589e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey final String customType = extras.getString(typeExtra); 590e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey child.put(editType.customColumn, customType); 591d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 592d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 5932ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey} 594