EntityModifier.java revision d2fdb9095e0009408b89e24048154ea7a8a14413
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; 36a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintanaimport android.provider.ContactsContract.CommonDataKinds.Photo; 37d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredName; 38d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal; 39d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.Intents.Insert; 40a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintanaimport android.provider.ContactsContract; 41d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.text.TextUtils; 426f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkeyimport android.util.Log; 4307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport android.util.SparseIntArray; 442ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 4507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport java.util.ArrayList; 4607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport java.util.Iterator; 472ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeyimport java.util.List; 482ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 492ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey/** 508d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * Helper methods for modifying an {@link EntityDelta}, such as inserting 512ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * new rows, or enforcing {@link ContactsSource}. 522ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 532ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeypublic class EntityModifier { 546f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey private static final String TAG = "EntityModifier"; 556f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 562ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 578d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * For the given {@link EntityDelta}, determine if the given 582ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * {@link DataKind} could be inserted under specific 592ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * {@link ContactsSource}. 602ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 618d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey public static boolean canInsert(EntityDelta state, DataKind kind) { 6207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Insert possible when have valid types and under overall maximum 63ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey final int visibleCount = state.getMimeEntriesCount(kind.mimeType, true); 6407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final boolean validTypes = hasValidTypes(state, kind); 6507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final boolean validOverall = (kind.typeOverallMax == -1) 66ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey || (visibleCount < kind.typeOverallMax); 6707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return (validTypes && validOverall); 6807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 6907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 708d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey public static boolean hasValidTypes(EntityDelta state, DataKind kind) { 71d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey if (EntityModifier.hasEditTypes(kind)) { 72d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return (getValidTypes(state, kind).size() > 0); 73d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } else { 74d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return true; 75d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 762ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 772ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 782ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 79aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * Ensure that at least one of the given {@link DataKind} exists in the 80aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * given {@link EntityDelta} state, and try creating one if none exist. 81aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey */ 82aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey public static void ensureKindExists(EntityDelta state, ContactsSource source, String mimeType) { 83aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey final DataKind kind = source.getKindForMimetype(mimeType); 84ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey final boolean hasChild = state.getMimeEntriesCount(mimeType, true) > 0; 85aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey 86aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey if (!hasChild && kind != null) { 87aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey // Create child when none exists and valid kind 88aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey insertChild(state, kind); 89aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey } 90aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey } 91aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey 92aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey /** 938d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * For the given {@link EntityDelta} and {@link DataKind}, return the 942ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * list possible {@link EditType} options available based on 952ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * {@link ContactsSource}. 962ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 978d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey public static ArrayList<EditType> getValidTypes(EntityDelta state, DataKind kind) { 9807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return getValidTypes(state, kind, null, true, null); 9907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 10007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 10107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 1028d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * For the given {@link EntityDelta} and {@link DataKind}, return the 10307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * list possible {@link EditType} options available based on 10407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * {@link ContactsSource}. 10507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * 10607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * @param forceInclude Always include this {@link EditType} in the returned 10707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * list, even when an otherwise-invalid choice. This is useful 10807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * when showing a dialog that includes the current type. 10907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 1108d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey public static ArrayList<EditType> getValidTypes(EntityDelta state, DataKind kind, 1112ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey EditType forceInclude) { 11207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return getValidTypes(state, kind, forceInclude, true, null); 11307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 11407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 11507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 1168d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * For the given {@link EntityDelta} and {@link DataKind}, return the 11707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * list possible {@link EditType} options available based on 11807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * {@link ContactsSource}. 11907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * 12007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * @param forceInclude Always include this {@link EditType} in the returned 12107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * list, even when an otherwise-invalid choice. This is useful 12207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * when showing a dialog that includes the current type. 12307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * @param includeSecondary If true, include any valid types marked as 12407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * {@link EditType#secondary}. 12507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * @param typeCount When provided, will be used for the frequency count of 12607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * each {@link EditType}, otherwise built using 1278d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * {@link #getTypeFrequencies(EntityDelta, DataKind)}. 12807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 1298d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey private static ArrayList<EditType> getValidTypes(EntityDelta state, DataKind kind, 13007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey EditType forceInclude, boolean includeSecondary, SparseIntArray typeCount) { 131aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey final ArrayList<EditType> validTypes = Lists.newArrayList(); 13207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 13307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Bail early if no types provided 13407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (!hasEditTypes(kind)) return validTypes; 13507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 13607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (typeCount == null) { 13707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Build frequency counts if not provided 13807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey typeCount = getTypeFrequencies(state, kind); 13907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 14007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 14107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Build list of valid types 14207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final int overallCount = typeCount.get(FREQUENCY_TOTAL); 14307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey for (EditType type : kind.typeList) { 144d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final boolean validOverall = (kind.typeOverallMax == -1 ? true 145d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey : overallCount < kind.typeOverallMax); 146d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final boolean validSpecific = (type.specificMax == -1 ? true : typeCount 147d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey .get(type.rawValue) < type.specificMax); 14807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final boolean validSecondary = (includeSecondary ? true : !type.secondary); 14907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final boolean forcedInclude = type.equals(forceInclude); 150d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey if (forcedInclude || (validOverall && validSpecific && validSecondary)) { 15107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Type is valid when no limit, under limit, or forced include 15207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey validTypes.add(type); 15307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 15407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 15507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 15607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return validTypes; 15707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 15807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 15907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey private static final int FREQUENCY_TOTAL = Integer.MIN_VALUE; 16007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 16107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 16207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * Count up the frequency that each {@link EditType} appears in the given 1638d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * {@link EntityDelta}. The returned {@link SparseIntArray} maps from 16407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * {@link EditType#rawValue} to counts, with the total overall count stored 16507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * as {@link #FREQUENCY_TOTAL}. 16607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 1678d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey private static SparseIntArray getTypeFrequencies(EntityDelta state, DataKind kind) { 16807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final SparseIntArray typeCount = new SparseIntArray(); 16907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 17007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Find all entries for this kind, bailing early if none found 1718d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey final List<ValuesDelta> mimeEntries = state.getMimeEntries(kind.mimeType); 17207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (mimeEntries == null) return typeCount; 17307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 17407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey int totalCount = 0; 1758d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey for (ValuesDelta entry : mimeEntries) { 17607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Only count visible entries 17707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (!entry.isVisible()) continue; 17807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey totalCount++; 17907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 18007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final EditType type = getCurrentType(entry, kind); 18107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (type != null) { 18207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final int count = typeCount.get(type.rawValue); 18307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey typeCount.put(type.rawValue, count + 1); 18407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 18507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 18607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey typeCount.put(FREQUENCY_TOTAL, totalCount); 18707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return typeCount; 1882ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 1892ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 1902ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 1912ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Check if the given {@link DataKind} has multiple types that should be 1922ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * displayed for users to pick. 1932ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 1942ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey public static boolean hasEditTypes(DataKind kind) { 1952ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey return kind.typeList != null && kind.typeList.size() > 0; 1962ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 1972ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 1982ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 1992ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Find the {@link EditType} that describes the given 2008d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * {@link ValuesDelta} row, assuming the given {@link DataKind} dictates 2012ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * the possible types. 2022ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 2038d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey public static EditType getCurrentType(ValuesDelta entry, DataKind kind) { 20407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final Long rawValue = entry.getAsLong(kind.typeColumn); 20507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (rawValue == null) return null; 20607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return getType(kind, rawValue.intValue()); 20707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 20807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 20907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 21011d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar * Find the {@link EditType} that describes the given {@link ContentValues} row, 21111d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar * assuming the given {@link DataKind} dictates the possible types. 21211d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar */ 21311d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar public static EditType getCurrentType(ContentValues entry, DataKind kind) { 21411d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar if (kind.typeColumn == null) return null; 21549d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey final Integer rawValue = entry.getAsInteger(kind.typeColumn); 21649d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey if (rawValue == null) return null; 21711d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar return getType(kind, rawValue); 21811d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar } 21911d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar 22011d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar /** 221802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey * Find the {@link EditType} that describes the given {@link Cursor} row, 222802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey * assuming the given {@link DataKind} dictates the possible types. 223802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey */ 224802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey public static EditType getCurrentType(Cursor cursor, DataKind kind) { 225ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey if (kind.typeColumn == null) return null; 226802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey final int index = cursor.getColumnIndex(kind.typeColumn); 22749d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey if (index == -1) return null; 228802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey final int rawValue = cursor.getInt(index); 229802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey return getType(kind, rawValue); 230802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey } 231802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey 232802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey /** 23307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * Find the {@link EditType} with the given {@link EditType#rawValue}. 23407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 23507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey public static EditType getType(DataKind kind, int rawValue) { 2362ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey for (EditType type : kind.typeList) { 2372ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey if (type.rawValue == rawValue) { 2382ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey return type; 2392ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 2402ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 2412ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey return null; 2422ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 2432ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 2442ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 24549d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey * Return the precedence for the the given {@link EditType#rawValue}, where 24649d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey * lower numbers are higher precedence. 24749d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey */ 24849d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey public static int getTypePrecedence(DataKind kind, int rawValue) { 24949d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey for (int i = 0; i < kind.typeList.size(); i++) { 25049d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey final EditType type = kind.typeList.get(i); 25149d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey if (type.rawValue == rawValue) { 25249d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey return i; 25349d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey } 25449d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey } 25549d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey return Integer.MAX_VALUE; 25649d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey } 25749d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey 25849d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey /** 25907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * Find the best {@link EditType} for a potential insert. The "best" is the 26007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * first primary type that doesn't already exist. When all valid types 26107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * exist, we pick the last valid option. 26207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 2638d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey public static EditType getBestValidType(EntityDelta state, DataKind kind, 264d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey boolean includeSecondary, int exactValue) { 26507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Shortcut when no types 26607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (kind.typeColumn == null) return null; 26707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 26807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Find type counts and valid primary types, bail if none 26907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final SparseIntArray typeCount = getTypeFrequencies(state, kind); 27007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final ArrayList<EditType> validTypes = getValidTypes(state, kind, null, includeSecondary, 27107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey typeCount); 27207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (validTypes.size() == 0) return null; 27307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 27407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Keep track of the last valid type 27507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final EditType lastType = validTypes.get(validTypes.size() - 1); 27607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 27707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Remove any types that already exist 27807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey Iterator<EditType> iterator = validTypes.iterator(); 27907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey while (iterator.hasNext()) { 28007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final EditType type = iterator.next(); 28107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final int count = typeCount.get(type.rawValue); 28207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 283e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey if (exactValue == type.rawValue) { 284d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Found exact value match 285d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return type; 286d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 287d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 28807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (count > 0) { 28907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Type already appears, so don't consider 29007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey iterator.remove(); 29107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 29207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 29307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 29407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Use the best remaining, otherwise the last valid 29507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (validTypes.size() > 0) { 29607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return validTypes.get(0); 29707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } else { 29807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return lastType; 29907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 30007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 30107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 30207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 3032ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Insert a new child of kind {@link DataKind} into the given 304d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * {@link EntityDelta}. Tries using the best {@link EditType} found using 305d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * {@link #getBestValidType(EntityDelta, DataKind, boolean, int)}. 3062ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 307d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey public static ValuesDelta insertChild(EntityDelta state, DataKind kind) { 30807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // First try finding a valid primary 309d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey EditType bestType = getBestValidType(state, kind, false, Integer.MIN_VALUE); 31007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (bestType == null) { 31107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // No valid primary found, so expand search to secondary 312d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey bestType = getBestValidType(state, kind, true, Integer.MIN_VALUE); 31307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 314d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return insertChild(state, kind, bestType); 31507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 31607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 31707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 31807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * Insert a new child of kind {@link DataKind} into the given 3198d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * {@link EntityDelta}, marked with the given {@link EditType}. 32007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 321d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey public static ValuesDelta insertChild(EntityDelta state, DataKind kind, EditType type) { 322d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Bail early if invalid kind 323d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey if (kind == null) return null; 324d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 3252ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey final ContentValues after = new ContentValues(); 3262ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 327e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey // Our parent CONTACT_ID is provided later 3282ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey after.put(Data.MIMETYPE, kind.mimeType); 329e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey 330e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey // Fill-in with any requested default values 331e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey if (kind.defaultValues != null) { 332e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey after.putAll(kind.defaultValues); 333e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey } 334e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey 33507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (kind.typeColumn != null && type != null) { 33607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Set type, if provided 33707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey after.put(kind.typeColumn, type.rawValue); 338e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey } 3392ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 340d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final ValuesDelta child = ValuesDelta.fromAfter(after); 341d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey state.addEntry(child); 342d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return child; 3432ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 3442ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 345d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey /** 3467f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey * Processing to trim any empty {@link ValuesDelta} and {@link EntityDelta} 3477f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey * from the given {@link EntitySet}, assuming the given {@link Sources} 3487f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey * dictates the structure for various fields. This method ignores rows not 3497f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey * described by the {@link ContactsSource}. 3507f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey */ 3517f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey public static void trimEmpty(EntitySet set, Sources sources) { 3527f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey for (EntityDelta state : set) { 3537f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey final String accountType = state.getValues().getAsString(RawContacts.ACCOUNT_TYPE); 3547f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey final ContactsSource source = sources.getInflatedSource(accountType, 3557f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey ContactsSource.LEVEL_MIMETYPES); 3567f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey trimEmpty(state, source); 3577f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey } 3587f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey } 3597f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey 3607f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey /** 3616f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey * Processing to trim any empty {@link ValuesDelta} rows from the given 3626f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey * {@link EntityDelta}, assuming the given {@link ContactsSource} dictates 3636f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey * the structure for various fields. This method ignores rows not described 3646f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey * by the {@link ContactsSource}. 3656f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey */ 3667f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey public static void trimEmpty(EntityDelta state, ContactsSource source) { 3677f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey boolean hasValues = false; 3687f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey 3696f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey // Walk through entries for each well-known kind 3706f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey for (DataKind kind : source.getSortedDataKinds()) { 3716f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey final String mimeType = kind.mimeType; 3726f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey final ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType); 3736f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey if (entries == null) continue; 3746f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 3756f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey for (ValuesDelta entry : entries) { 3767f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey // Skip any values that haven't been touched 3776f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey final boolean touched = entry.isInsert() || entry.isUpdate(); 3787f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey if (!touched) { 3797f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey hasValues = true; 3807f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey continue; 3817f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey } 3827f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey 383a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana // Test and remove this row if empty and it isn't a photo from google 384a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana final boolean isGoogleSource = TextUtils.equals(GoogleSource.ACCOUNT_TYPE, 385a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana state.getValues().getAsString(RawContacts.ACCOUNT_TYPE)); 386a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana final boolean isPhoto = TextUtils.equals(Photo.CONTENT_ITEM_TYPE, kind.mimeType); 387a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana final boolean isGooglePhoto = isPhoto && isGoogleSource; 388a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana if (EntityModifier.isEmpty(entry, kind) && !isGooglePhoto) { 3896f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey // TODO: remove this verbose logging 3906f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey Log.w(TAG, "Trimming: " + entry.toString()); 3916f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey entry.markDeleted(); 392d2fdb9095e0009408b89e24048154ea7a8a14413Megha Joshi } else if (!entry.isFromTemplate()) { 3937f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey hasValues = true; 3946f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 3956f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 3966f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 3977f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey 3987f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey if (!hasValues) { 3997f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey // Trim overall entity if no children exist 4007f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey state.markDeleted(); 4017f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey } 4026f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4036f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 4046f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey /** 4056f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey * Test if the given {@link ValuesDelta} would be considered "empty" in 4066f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey * terms of {@link DataKind#fieldList}. 4076f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey */ 4086f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey public static boolean isEmpty(ValuesDelta values, DataKind kind) { 4096f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey boolean hasValues = false; 4106f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey for (EditField field : kind.fieldList) { 4116f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey // If any field has values, we're not empty 4126f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey final String value = values.getAsString(field.column); 4136f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey if (!TextUtils.isEmpty(value)) { 4146f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey hasValues = true; 4156f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4166f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4176f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 4186f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey return !hasValues; 4196f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4206f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 4216f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey /** 422d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * Parse the given {@link Bundle} into the given {@link EntityDelta} state, 423d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * assuming the extras defined through {@link Intents}. 424d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey */ 425aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey public static void parseExtras(Context context, ContactsSource source, EntityDelta state, 426aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey Bundle extras) { 427d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey if (extras == null || extras.size() == 0) { 428d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey // Bail early if no useful data 429d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey return; 430d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey } 431d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey 432d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey { 433d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // StructuredName 434ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey EntityModifier.ensureKindExists(state, source, StructuredName.CONTENT_ITEM_TYPE); 435ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey final ValuesDelta child = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE); 436ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey 437d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final String name = extras.getString(Insert.NAME); 438ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey if (!TextUtils.isEmpty(name) && TextUtils.isGraphic(name)) { 439ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey child.put(StructuredName.GIVEN_NAME, name); 440ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey } 441ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey 442d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final String phoneticName = extras.getString(Insert.PHONETIC_NAME); 443ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey if (!TextUtils.isEmpty(phoneticName) && TextUtils.isGraphic(phoneticName)) { 444d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey child.put(StructuredName.PHONETIC_GIVEN_NAME, phoneticName); 445d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 446d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 447d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 448d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey { 449d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // StructuredPostal 450d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final DataKind kind = source.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE); 451d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.POSTAL_TYPE, Insert.POSTAL, 452a62e6db031a02bb04a57049839545e378d3b72c2Neel Parekh StructuredPostal.STREET); 453d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 454d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 455d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey { 456d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Phone 457d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final DataKind kind = source.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); 458d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.PHONE_TYPE, Insert.PHONE, Phone.NUMBER); 459d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.SECONDARY_PHONE_TYPE, Insert.SECONDARY_PHONE, 460d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey Phone.NUMBER); 461d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.TERTIARY_PHONE_TYPE, Insert.TERTIARY_PHONE, 462d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey Phone.NUMBER); 463d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 464d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 465d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey { 466d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Email 467d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final DataKind kind = source.getKindForMimetype(Email.CONTENT_ITEM_TYPE); 468d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey parseExtras(state, kind, extras, Insert.EMAIL_TYPE, Insert.EMAIL, Email.DATA); 469d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.SECONDARY_EMAIL_TYPE, Insert.SECONDARY_EMAIL, 470d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey Email.DATA); 471d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.TERTIARY_EMAIL_TYPE, Insert.TERTIARY_EMAIL, 472d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey Email.DATA); 473d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 474d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 475d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey { 476d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Im 477d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final DataKind kind = source.getKindForMimetype(Im.CONTENT_ITEM_TYPE); 478e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey fixupLegacyImType(extras); 479d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.IM_PROTOCOL, Insert.IM_HANDLE, Im.DATA); 480d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 481d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 482d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 483d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey /** 484e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey * Attempt to parse legacy {@link Insert#IM_PROTOCOL} values, replacing them 485e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey * with updated values. 486e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey */ 487e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey private static void fixupLegacyImType(Bundle bundle) { 488e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey final String encodedString = bundle.getString(Insert.IM_PROTOCOL); 489e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey if (encodedString == null) return; 490e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey 491e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey try { 492e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey final Object protocol = android.provider.Contacts.ContactMethods 493e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey .decodeImProtocol(encodedString); 494e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey if (protocol instanceof Integer) { 495e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey bundle.putInt(Insert.IM_PROTOCOL, (Integer)protocol); 496e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } else { 497e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey bundle.putString(Insert.IM_PROTOCOL, (String)protocol); 498e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } 499e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } catch (IllegalArgumentException e) { 500e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey // Ignore exception when legacy parser fails 501e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } 502e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } 503e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey 504e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey /** 505d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * Parse a specific entry from the given {@link Bundle} and insert into the 506d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * given {@link EntityDelta}. Silently skips the insert when missing value 507d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * or no valid {@link EditType} found. 508d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * 509d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * @param typeExtra {@link Bundle} key that holds the incoming 510d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * {@link EditType#rawValue} value. 511d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * @param valueExtra {@link Bundle} key that holds the incoming value. 512d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * @param valueColumn Column to write value into {@link ValuesDelta}. 513d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey */ 514d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey public static void parseExtras(EntityDelta state, DataKind kind, Bundle extras, 515d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey String typeExtra, String valueExtra, String valueColumn) { 5167f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey final CharSequence value = extras.getCharSequence(valueExtra); 5177f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey 5187f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey // Bail early if source doesn't handle this type 5197f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey if (kind == null) return; 520d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 521d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Bail when can't insert type, or value missing 522d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final boolean canInsert = EntityModifier.canInsert(state, kind); 523d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey final boolean validValue = (value != null && TextUtils.isGraphic(value)); 524d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey if (!validValue || !canInsert) return; 525d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 5266164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey // Find exact type when requested, otherwise best available type 5276164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey final boolean hasType = extras.containsKey(typeExtra); 5286164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey final int typeValue = extras.getInt(typeExtra, hasType ? BaseTypes.TYPE_CUSTOM 5296164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey : Integer.MIN_VALUE); 530d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final EditType editType = EntityModifier.getBestValidType(state, kind, true, typeValue); 531d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 532d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Create data row and fill with value 533d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final ValuesDelta child = EntityModifier.insertChild(state, kind, editType); 5347f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey child.put(valueColumn, value.toString()); 535d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 536d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey if (editType != null && editType.customColumn != null) { 537d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Write down label when custom type picked 538e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey final String customType = extras.getString(typeExtra); 539e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey child.put(editType.customColumn, customType); 540d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 541d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 5422ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey} 543