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