12ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey/* 22ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Copyright (C) 2009 The Android Open Source Project 32ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * 42ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License"); 52ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * you may not use this file except in compliance with the License. 62ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * You may obtain a copy of the License at 72ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * 82ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * http://www.apache.org/licenses/LICENSE-2.0 92ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * 102ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Unless required by applicable law or agreed to in writing, software 112ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS, 122ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * See the License for the specific language governing permissions and 142ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * limitations under the License. 152ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 162ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 172ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeypackage com.android.contacts.model; 182ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 19e31dac84479a4c7b356edfc062a447cdfb5efc69Jeff Sharkeyimport com.android.contacts.ContactsUtils; 20d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport com.android.contacts.editor.EventFieldEditorView; 21da5bf1cf60beef3de5e651a569fa544293683926Dave Santoroimport com.android.contacts.util.NameConverter; 22d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport com.android.contacts.editor.PhoneticNameEditorView; 234597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikovimport com.android.contacts.model.AccountType.EditField; 244597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikovimport com.android.contacts.model.AccountType.EditType; 25d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport com.android.contacts.model.AccountType.EventEditType; 26d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport com.android.contacts.model.EntityDelta.ValuesDelta; 27d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport com.android.contacts.util.DateUtils; 282ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 292ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeyimport android.content.ContentValues; 30802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkeyimport android.content.Context; 31802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkeyimport android.database.Cursor; 32c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.net.Uri; 33d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.os.Bundle; 34c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract; 357f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.BaseTypes; 36d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Email; 37d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Event; 38c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership; 39d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Im; 40d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Nickname; 4116c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Note; 4216c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Organization; 43d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Phone; 44a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintanaimport android.provider.ContactsContract.CommonDataKinds.Photo; 45d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Relation; 46d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.SipAddress; 47d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredName; 48d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal; 49d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Website; 50c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract.Data; 51c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract.Intents; 52d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.Intents.Insert; 53c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract.RawContacts; 54d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.text.TextUtils; 556f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkeyimport android.util.Log; 5607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport android.util.SparseIntArray; 572ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 58d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.text.ParsePosition; 5907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport java.util.ArrayList; 60d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Arrays; 61d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Calendar; 62d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Date; 63d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.HashMap; 64d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.HashSet; 6507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport java.util.Iterator; 662ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeyimport java.util.List; 67d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Locale; 68d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Map; 69d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Set; 702ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 712ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey/** 728d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * Helper methods for modifying an {@link EntityDelta}, such as inserting 734597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov * new rows, or enforcing {@link AccountType}. 742ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 752ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeypublic class EntityModifier { 766f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey private static final String TAG = "EntityModifier"; 776f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 78a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan /** Set to true in order to view logs on entity operations */ 79a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan private static final boolean DEBUG = false; 80a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan 812ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 828d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * For the given {@link EntityDelta}, determine if the given 832ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * {@link DataKind} could be inserted under specific 844597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov * {@link AccountType}. 852ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 868d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey public static boolean canInsert(EntityDelta state, DataKind kind) { 8707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Insert possible when have valid types and under overall maximum 88ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey final int visibleCount = state.getMimeEntriesCount(kind.mimeType, true); 8907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final boolean validTypes = hasValidTypes(state, kind); 9007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final boolean validOverall = (kind.typeOverallMax == -1) 91ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey || (visibleCount < kind.typeOverallMax); 9207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return (validTypes && validOverall); 9307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 9407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 958d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey public static boolean hasValidTypes(EntityDelta state, DataKind kind) { 96d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey if (EntityModifier.hasEditTypes(kind)) { 97d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return (getValidTypes(state, kind).size() > 0); 98d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } else { 99d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return true; 100d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 1012ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 1022ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 1032ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 104aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * Ensure that at least one of the given {@link DataKind} exists in the 105aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * given {@link EntityDelta} state, and try creating one if none exist. 106aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey */ 10769f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov public static void ensureKindExists( 10869f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov EntityDelta state, AccountType accountType, String mimeType) { 10969f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov final DataKind kind = accountType.getKindForMimetype(mimeType); 110ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey final boolean hasChild = state.getMimeEntriesCount(mimeType, true) > 0; 111aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey 112aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey if (!hasChild && kind != null) { 113aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey // Create child when none exists and valid kind 114e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi final ValuesDelta child = insertChild(state, kind); 115e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi if (kind.mimeType.equals(Photo.CONTENT_ITEM_TYPE)) { 116e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi child.setFromTemplate(true); 117e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi } 118aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey } 119aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey } 120aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey 121aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey /** 1228d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * For the given {@link EntityDelta} and {@link DataKind}, return the 1232ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * list possible {@link EditType} options available based on 1244597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov * {@link AccountType}. 1252ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 1268d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey public static ArrayList<EditType> getValidTypes(EntityDelta state, DataKind kind) { 12707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return getValidTypes(state, kind, null, true, null); 12807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 12907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 13007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 1318d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * For the given {@link EntityDelta} and {@link DataKind}, return the 13207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * list possible {@link EditType} options available based on 1334597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov * {@link AccountType}. 13407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * 13507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * @param forceInclude Always include this {@link EditType} in the returned 13607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * list, even when an otherwise-invalid choice. This is useful 13707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * when showing a dialog that includes the current type. 13807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 1398d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey public static ArrayList<EditType> getValidTypes(EntityDelta state, DataKind kind, 1402ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey EditType forceInclude) { 14107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return getValidTypes(state, kind, forceInclude, true, null); 14207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 14307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 14407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 1458d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * For the given {@link EntityDelta} and {@link DataKind}, return the 14607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * list possible {@link EditType} options available based on 1474597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov * {@link AccountType}. 14807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * 14907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * @param forceInclude Always include this {@link EditType} in the returned 15007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * list, even when an otherwise-invalid choice. This is useful 15107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * when showing a dialog that includes the current type. 15207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * @param includeSecondary If true, include any valid types marked as 15307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * {@link EditType#secondary}. 15407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * @param typeCount When provided, will be used for the frequency count of 15507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * each {@link EditType}, otherwise built using 1568d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * {@link #getTypeFrequencies(EntityDelta, DataKind)}. 15707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 1588d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey private static ArrayList<EditType> getValidTypes(EntityDelta state, DataKind kind, 15907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey EditType forceInclude, boolean includeSecondary, SparseIntArray typeCount) { 160da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro final ArrayList<EditType> validTypes = new ArrayList<EditType>(); 16107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 16207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Bail early if no types provided 16307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (!hasEditTypes(kind)) return validTypes; 16407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 16507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (typeCount == null) { 16607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Build frequency counts if not provided 16707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey typeCount = getTypeFrequencies(state, kind); 16807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 16907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 17007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Build list of valid types 17107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final int overallCount = typeCount.get(FREQUENCY_TOTAL); 17207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey for (EditType type : kind.typeList) { 173d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final boolean validOverall = (kind.typeOverallMax == -1 ? true 174d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey : overallCount < kind.typeOverallMax); 175d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final boolean validSpecific = (type.specificMax == -1 ? true : typeCount 176d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey .get(type.rawValue) < type.specificMax); 17707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final boolean validSecondary = (includeSecondary ? true : !type.secondary); 17807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final boolean forcedInclude = type.equals(forceInclude); 179d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey if (forcedInclude || (validOverall && validSpecific && validSecondary)) { 18007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Type is valid when no limit, under limit, or forced include 18107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey validTypes.add(type); 18207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 18307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 18407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 18507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return validTypes; 18607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 18707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 18807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey private static final int FREQUENCY_TOTAL = Integer.MIN_VALUE; 18907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 19007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 19107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * Count up the frequency that each {@link EditType} appears in the given 1928d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * {@link EntityDelta}. The returned {@link SparseIntArray} maps from 19307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * {@link EditType#rawValue} to counts, with the total overall count stored 19407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * as {@link #FREQUENCY_TOTAL}. 19507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 1968d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey private static SparseIntArray getTypeFrequencies(EntityDelta state, DataKind kind) { 19707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final SparseIntArray typeCount = new SparseIntArray(); 19807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 19907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Find all entries for this kind, bailing early if none found 2008d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey final List<ValuesDelta> mimeEntries = state.getMimeEntries(kind.mimeType); 20107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (mimeEntries == null) return typeCount; 20207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 20307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey int totalCount = 0; 2048d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey for (ValuesDelta entry : mimeEntries) { 20507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Only count visible entries 20607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (!entry.isVisible()) continue; 20707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey totalCount++; 20807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 20907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final EditType type = getCurrentType(entry, kind); 21007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (type != null) { 21107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final int count = typeCount.get(type.rawValue); 21207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey typeCount.put(type.rawValue, count + 1); 21307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 21407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 21507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey typeCount.put(FREQUENCY_TOTAL, totalCount); 21607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return typeCount; 2172ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 2182ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 2192ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 2202ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Check if the given {@link DataKind} has multiple types that should be 2212ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * displayed for users to pick. 2222ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 2232ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey public static boolean hasEditTypes(DataKind kind) { 2242ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey return kind.typeList != null && kind.typeList.size() > 0; 2252ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 2262ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 2272ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 2282ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Find the {@link EditType} that describes the given 2298d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * {@link ValuesDelta} row, assuming the given {@link DataKind} dictates 2302ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * the possible types. 2312ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 2328d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey public static EditType getCurrentType(ValuesDelta entry, DataKind kind) { 23307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final Long rawValue = entry.getAsLong(kind.typeColumn); 23407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (rawValue == null) return null; 23507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return getType(kind, rawValue.intValue()); 23607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 23707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 23807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 23911d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar * Find the {@link EditType} that describes the given {@link ContentValues} row, 24011d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar * assuming the given {@link DataKind} dictates the possible types. 24111d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar */ 24211d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar public static EditType getCurrentType(ContentValues entry, DataKind kind) { 24311d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar if (kind.typeColumn == null) return null; 24449d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey final Integer rawValue = entry.getAsInteger(kind.typeColumn); 24549d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey if (rawValue == null) return null; 24611d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar return getType(kind, rawValue); 24711d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar } 24811d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar 24911d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar /** 250802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey * Find the {@link EditType} that describes the given {@link Cursor} row, 251802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey * assuming the given {@link DataKind} dictates the possible types. 252802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey */ 253802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey public static EditType getCurrentType(Cursor cursor, DataKind kind) { 254ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey if (kind.typeColumn == null) return null; 255802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey final int index = cursor.getColumnIndex(kind.typeColumn); 25649d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey if (index == -1) return null; 257802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey final int rawValue = cursor.getInt(index); 258802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey return getType(kind, rawValue); 259802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey } 260802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey 261802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey /** 26207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * Find the {@link EditType} with the given {@link EditType#rawValue}. 26307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 26407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey public static EditType getType(DataKind kind, int rawValue) { 2652ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey for (EditType type : kind.typeList) { 2662ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey if (type.rawValue == rawValue) { 2672ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey return type; 2682ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 2692ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 2702ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey return null; 2712ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 2722ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 2732ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 27449d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey * Return the precedence for the the given {@link EditType#rawValue}, where 27549d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey * lower numbers are higher precedence. 27649d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey */ 27749d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey public static int getTypePrecedence(DataKind kind, int rawValue) { 27849d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey for (int i = 0; i < kind.typeList.size(); i++) { 27949d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey final EditType type = kind.typeList.get(i); 28049d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey if (type.rawValue == rawValue) { 28149d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey return i; 28249d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey } 28349d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey } 28449d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey return Integer.MAX_VALUE; 28549d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey } 28649d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey 28749d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey /** 28807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * Find the best {@link EditType} for a potential insert. The "best" is the 28907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * first primary type that doesn't already exist. When all valid types 29007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * exist, we pick the last valid option. 29107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 2928d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey public static EditType getBestValidType(EntityDelta state, DataKind kind, 293d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey boolean includeSecondary, int exactValue) { 29407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Shortcut when no types 29507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (kind.typeColumn == null) return null; 29607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 29707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Find type counts and valid primary types, bail if none 29807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final SparseIntArray typeCount = getTypeFrequencies(state, kind); 29907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final ArrayList<EditType> validTypes = getValidTypes(state, kind, null, includeSecondary, 30007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey typeCount); 30107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (validTypes.size() == 0) return null; 30207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 30307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Keep track of the last valid type 30407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final EditType lastType = validTypes.get(validTypes.size() - 1); 30507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 30607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Remove any types that already exist 30707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey Iterator<EditType> iterator = validTypes.iterator(); 30807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey while (iterator.hasNext()) { 30907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final EditType type = iterator.next(); 31007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final int count = typeCount.get(type.rawValue); 31107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 312e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey if (exactValue == type.rawValue) { 313d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Found exact value match 314d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return type; 315d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 316d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 31707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (count > 0) { 31807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Type already appears, so don't consider 31907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey iterator.remove(); 32007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 32107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 32207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 32307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Use the best remaining, otherwise the last valid 32407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (validTypes.size() > 0) { 32507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return validTypes.get(0); 32607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } else { 32707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return lastType; 32807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 32907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 33007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 33107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 3322ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Insert a new child of kind {@link DataKind} into the given 333d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * {@link EntityDelta}. Tries using the best {@link EditType} found using 334d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * {@link #getBestValidType(EntityDelta, DataKind, boolean, int)}. 3352ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 336d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey public static ValuesDelta insertChild(EntityDelta state, DataKind kind) { 33707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // First try finding a valid primary 338d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey EditType bestType = getBestValidType(state, kind, false, Integer.MIN_VALUE); 33907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (bestType == null) { 34007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // No valid primary found, so expand search to secondary 341d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey bestType = getBestValidType(state, kind, true, Integer.MIN_VALUE); 34207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 343d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return insertChild(state, kind, bestType); 34407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 34507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 34607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 34707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * Insert a new child of kind {@link DataKind} into the given 3488d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * {@link EntityDelta}, marked with the given {@link EditType}. 34907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 350d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey public static ValuesDelta insertChild(EntityDelta state, DataKind kind, EditType type) { 351d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Bail early if invalid kind 352d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey if (kind == null) return null; 3532ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey final ContentValues after = new ContentValues(); 3542ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 355e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey // Our parent CONTACT_ID is provided later 3562ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey after.put(Data.MIMETYPE, kind.mimeType); 357e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey 358e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey // Fill-in with any requested default values 359e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey if (kind.defaultValues != null) { 360e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey after.putAll(kind.defaultValues); 361e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey } 362e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey 36307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (kind.typeColumn != null && type != null) { 36407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Set type, if provided 36507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey after.put(kind.typeColumn, type.rawValue); 366e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey } 3672ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 368d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final ValuesDelta child = ValuesDelta.fromAfter(after); 369a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov state.addEntry(child); 370d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return child; 3712ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 3722ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 373d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey /** 3747f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey * Processing to trim any empty {@link ValuesDelta} and {@link EntityDelta} 375a07fa5f37031e4c5cd2933de02d2db41ec153e2bDmitri Plotnikov * from the given {@link EntityDeltaList}, assuming the given {@link AccountTypeManager} 3767f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey * dictates the structure for various fields. This method ignores rows not 3774597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov * described by the {@link AccountType}. 3787f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey */ 379a07fa5f37031e4c5cd2933de02d2db41ec153e2bDmitri Plotnikov public static void trimEmpty(EntityDeltaList set, AccountTypeManager accountTypes) { 3807f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey for (EntityDelta state : set) { 3812b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro ValuesDelta values = state.getValues(); 3822b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE); 3832b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro final String dataSet = values.getAsString(RawContacts.DATA_SET); 3842b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro final AccountType type = accountTypes.getAccountType(accountType, dataSet); 385a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov trimEmpty(state, type); 3867f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey } 3877f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey } 3887f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey 389a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov public static boolean hasChanges(EntityDeltaList set, AccountTypeManager accountTypes) { 390a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov if (set.isMarkedForSplitting() || set.isMarkedForJoining()) { 391a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov return true; 392a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 393a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 394a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov for (EntityDelta state : set) { 3952b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro ValuesDelta values = state.getValues(); 3962b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE); 3972b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro final String dataSet = values.getAsString(RawContacts.DATA_SET); 3982b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro final AccountType type = accountTypes.getAccountType(accountType, dataSet); 399a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov if (hasChanges(state, type)) { 400a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov return true; 401a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 402a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 403a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov return false; 404a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 405a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 4067f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey /** 4076f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey * Processing to trim any empty {@link ValuesDelta} rows from the given 4084597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov * {@link EntityDelta}, assuming the given {@link AccountType} dictates 4096f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey * the structure for various fields. This method ignores rows not described 4104597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov * by the {@link AccountType}. 4116f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey */ 41269f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov public static void trimEmpty(EntityDelta state, AccountType accountType) { 4137f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey boolean hasValues = false; 4147f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey 4156f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey // Walk through entries for each well-known kind 41669f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov for (DataKind kind : accountType.getSortedDataKinds()) { 4176f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey final String mimeType = kind.mimeType; 4186f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey final ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType); 4196f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey if (entries == null) continue; 4206f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 4216f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey for (ValuesDelta entry : entries) { 4227f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey // Skip any values that haven't been touched 4236f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey final boolean touched = entry.isInsert() || entry.isUpdate(); 4247f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey if (!touched) { 4257f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey hasValues = true; 4267f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey continue; 4277f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey } 4287f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey 429a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana // Test and remove this row if empty and it isn't a photo from google 43069f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov final boolean isGoogleAccount = TextUtils.equals(GoogleAccountType.ACCOUNT_TYPE, 431a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana state.getValues().getAsString(RawContacts.ACCOUNT_TYPE)); 432a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana final boolean isPhoto = TextUtils.equals(Photo.CONTENT_ITEM_TYPE, kind.mimeType); 43369f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov final boolean isGooglePhoto = isPhoto && isGoogleAccount; 434e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi 435a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana if (EntityModifier.isEmpty(entry, kind) && !isGooglePhoto) { 436a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan if (DEBUG) { 437a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan Log.v(TAG, "Trimming: " + entry.toString()); 438a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan } 4396f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey entry.markDeleted(); 440d2fdb9095e0009408b89e24048154ea7a8a14413Megha Joshi } else if (!entry.isFromTemplate()) { 4417f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey hasValues = true; 4426f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4436f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4446f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4457f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey if (!hasValues) { 4467f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey // Trim overall entity if no children exist 4477f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey state.markDeleted(); 4487f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey } 4496f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4506f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 451a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov private static boolean hasChanges(EntityDelta state, AccountType accountType) { 452a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov for (DataKind kind : accountType.getSortedDataKinds()) { 453a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov final String mimeType = kind.mimeType; 454a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov final ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType); 455a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov if (entries == null) continue; 456a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 457a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov for (ValuesDelta entry : entries) { 458de0898f0337d3c6ea818237b0a94acf1f15b6299Daniel Lehmann // An empty Insert must be ignored, because it won't save anything (an example 459de0898f0337d3c6ea818237b0a94acf1f15b6299Daniel Lehmann // is an empty name that stays empty) 460de0898f0337d3c6ea818237b0a94acf1f15b6299Daniel Lehmann final boolean isRealInsert = entry.isInsert() && !isEmpty(entry, kind); 461de0898f0337d3c6ea818237b0a94acf1f15b6299Daniel Lehmann if (isRealInsert || entry.isUpdate() || entry.isDelete()) { 462a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov return true; 463a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 464a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 465a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 466a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov return false; 467a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 468a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 4696f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey /** 4706f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey * Test if the given {@link ValuesDelta} would be considered "empty" in 4716f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey * terms of {@link DataKind#fieldList}. 4726f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey */ 4736f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey public static boolean isEmpty(ValuesDelta values, DataKind kind) { 474f3cdeca5e0b3a7f9411b6babad5de2925623372aDmitri Plotnikov if (Photo.CONTENT_ITEM_TYPE.equals(kind.mimeType)) { 475c863b0171f2a54a0ee71b34b33034aeea2464ccaDmitri Plotnikov return values.isInsert() && values.getAsByteArray(Photo.PHOTO) == null; 476f3cdeca5e0b3a7f9411b6babad5de2925623372aDmitri Plotnikov } 477f3cdeca5e0b3a7f9411b6babad5de2925623372aDmitri Plotnikov 478dbeb965d19a3c03e78cd54d9fbe7337b4fbb06f2Jeff Sharkey // No defined fields mean this row is always empty 479dbeb965d19a3c03e78cd54d9fbe7337b4fbb06f2Jeff Sharkey if (kind.fieldList == null) return true; 480dbeb965d19a3c03e78cd54d9fbe7337b4fbb06f2Jeff Sharkey 4816f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey for (EditField field : kind.fieldList) { 4826f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey // If any field has values, we're not empty 4836f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey final String value = values.getAsString(field.column); 484e31dac84479a4c7b356edfc062a447cdfb5efc69Jeff Sharkey if (ContactsUtils.isGraphic(value)) { 485c863b0171f2a54a0ee71b34b33034aeea2464ccaDmitri Plotnikov return false; 4866f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4876f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4886f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 489c863b0171f2a54a0ee71b34b33034aeea2464ccaDmitri Plotnikov return true; 4906f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4916f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 4926f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey /** 493916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov * Compares corresponding fields in values1 and values2. Only the fields 494916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov * declared by the DataKind are taken into consideration. 495916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov */ 496916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov protected static boolean areEqual(ValuesDelta values1, ContentValues values2, DataKind kind) { 497916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (kind.fieldList == null) return false; 498916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 499916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov for (EditField field : kind.fieldList) { 500916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov final String value1 = values1.getAsString(field.column); 501916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov final String value2 = values2.getAsString(field.column); 502916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (!TextUtils.equals(value1, value2)) { 503916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return false; 504916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 505916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 506916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 507916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return true; 508916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 509916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 510916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov /** 511d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * Parse the given {@link Bundle} into the given {@link EntityDelta} state, 512d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * assuming the extras defined through {@link Intents}. 513d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey */ 51469f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov public static void parseExtras(Context context, AccountType accountType, EntityDelta state, 515aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey Bundle extras) { 516d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey if (extras == null || extras.size() == 0) { 517d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey // Bail early if no useful data 518d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey return; 519d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey } 520d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey 521c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov parseStructuredNameExtra(context, accountType, state, extras); 522c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov parseStructuredPostalExtra(accountType, state, extras); 523d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 524d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey { 525d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Phone 52669f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov final DataKind kind = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); 527d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.PHONE_TYPE, Insert.PHONE, Phone.NUMBER); 528d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.SECONDARY_PHONE_TYPE, Insert.SECONDARY_PHONE, 529d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey Phone.NUMBER); 530d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.TERTIARY_PHONE_TYPE, Insert.TERTIARY_PHONE, 531d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey Phone.NUMBER); 532d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 533d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 534d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey { 535d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Email 53669f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov final DataKind kind = accountType.getKindForMimetype(Email.CONTENT_ITEM_TYPE); 537d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey parseExtras(state, kind, extras, Insert.EMAIL_TYPE, Insert.EMAIL, Email.DATA); 538d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.SECONDARY_EMAIL_TYPE, Insert.SECONDARY_EMAIL, 539d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey Email.DATA); 540d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.TERTIARY_EMAIL_TYPE, Insert.TERTIARY_EMAIL, 541d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey Email.DATA); 542d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 543d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 544d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey { 545d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Im 54669f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov final DataKind kind = accountType.getKindForMimetype(Im.CONTENT_ITEM_TYPE); 547e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey fixupLegacyImType(extras); 548d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.IM_PROTOCOL, Insert.IM_HANDLE, Im.DATA); 549d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 55016c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey 55116c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey // Organization 55216c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final boolean hasOrg = extras.containsKey(Insert.COMPANY) 55316c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey || extras.containsKey(Insert.JOB_TITLE); 55469f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov final DataKind kindOrg = accountType.getKindForMimetype(Organization.CONTENT_ITEM_TYPE); 55516c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey if (hasOrg && EntityModifier.canInsert(state, kindOrg)) { 55616c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final ValuesDelta child = EntityModifier.insertChild(state, kindOrg); 55716c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey 55816c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final String company = extras.getString(Insert.COMPANY); 55916c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey if (ContactsUtils.isGraphic(company)) { 56016c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey child.put(Organization.COMPANY, company); 56116c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey } 56216c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey 56316c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final String title = extras.getString(Insert.JOB_TITLE); 56416c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey if (ContactsUtils.isGraphic(title)) { 56516c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey child.put(Organization.TITLE, title); 56616c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey } 56716c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey } 56816c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey 56916c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey // Notes 57016c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final boolean hasNotes = extras.containsKey(Insert.NOTES); 57169f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov final DataKind kindNotes = accountType.getKindForMimetype(Note.CONTENT_ITEM_TYPE); 57216c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey if (hasNotes && EntityModifier.canInsert(state, kindNotes)) { 57316c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final ValuesDelta child = EntityModifier.insertChild(state, kindNotes); 57416c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey 57516c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final String notes = extras.getString(Insert.NOTES); 57616c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey if (ContactsUtils.isGraphic(notes)) { 57716c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey child.put(Note.NOTE, notes); 57816c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey } 57916c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey } 5806a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov 5816a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov // Arbitrary additional data 582916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov ArrayList<ContentValues> values = extras.getParcelableArrayList(Insert.DATA); 583916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (values != null) { 58469f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov parseValues(state, accountType, values); 585916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 586916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 587916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 588c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov private static void parseStructuredNameExtra( 589c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov Context context, AccountType accountType, EntityDelta state, Bundle extras) { 590c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov // StructuredName 591c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov EntityModifier.ensureKindExists(state, accountType, StructuredName.CONTENT_ITEM_TYPE); 592c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov final ValuesDelta child = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE); 593c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov 594c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov final String name = extras.getString(Insert.NAME); 595c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (ContactsUtils.isGraphic(name)) { 596c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov final DataKind kind = accountType.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE); 597c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov boolean supportsDisplayName = false; 598c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (kind.fieldList != null) { 599c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov for (EditField field : kind.fieldList) { 600c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (StructuredName.DISPLAY_NAME.equals(field.column)) { 601c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov supportsDisplayName = true; 602c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov break; 603c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 604c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 605c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 606c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov 607c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (supportsDisplayName) { 608c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov child.put(StructuredName.DISPLAY_NAME, name); 609c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } else { 610c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov Uri uri = ContactsContract.AUTHORITY_URI.buildUpon() 611c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov .appendPath("complete_name") 612c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov .appendQueryParameter(StructuredName.DISPLAY_NAME, name) 613c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov .build(); 614c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov Cursor cursor = context.getContentResolver().query(uri, 615c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov new String[]{ 616c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov StructuredName.PREFIX, 617c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov StructuredName.GIVEN_NAME, 618c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov StructuredName.MIDDLE_NAME, 619c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov StructuredName.FAMILY_NAME, 620c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov StructuredName.SUFFIX, 621c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov }, null, null, null); 622c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov 623c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov try { 624c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (cursor.moveToFirst()) { 625c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov child.put(StructuredName.PREFIX, cursor.getString(0)); 626c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov child.put(StructuredName.GIVEN_NAME, cursor.getString(1)); 627c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov child.put(StructuredName.MIDDLE_NAME, cursor.getString(2)); 628c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov child.put(StructuredName.FAMILY_NAME, cursor.getString(3)); 629c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov child.put(StructuredName.SUFFIX, cursor.getString(4)); 630c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 631c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } finally { 632c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov cursor.close(); 633c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 634c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 635c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 636c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov 637c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov final String phoneticName = extras.getString(Insert.PHONETIC_NAME); 638c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (ContactsUtils.isGraphic(phoneticName)) { 639c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov child.put(StructuredName.PHONETIC_GIVEN_NAME, phoneticName); 640c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 641c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 642c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov 643c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov private static void parseStructuredPostalExtra( 644c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov AccountType accountType, EntityDelta state, Bundle extras) { 645c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov // StructuredPostal 646c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov final DataKind kind = accountType.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE); 647c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov final ValuesDelta child = parseExtras(state, kind, extras, Insert.POSTAL_TYPE, 648c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov Insert.POSTAL, StructuredPostal.FORMATTED_ADDRESS); 649c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov String address = child == null ? null 650c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov : child.getAsString(StructuredPostal.FORMATTED_ADDRESS); 651c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (!TextUtils.isEmpty(address)) { 652c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov boolean supportsFormatted = false; 653c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (kind.fieldList != null) { 654c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov for (EditField field : kind.fieldList) { 655c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (StructuredPostal.FORMATTED_ADDRESS.equals(field.column)) { 656c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov supportsFormatted = true; 657c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov break; 658c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 659c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 660c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 661c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov 662c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (!supportsFormatted) { 663c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov child.put(StructuredPostal.STREET, address); 664c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov child.putNull(StructuredPostal.FORMATTED_ADDRESS); 665c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 666c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 667c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 668c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov 669916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov private static void parseValues( 67069f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov EntityDelta state, AccountType accountType, ArrayList<ContentValues> dataValueList) { 671916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov for (ContentValues values : dataValueList) { 672916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov String mimeType = values.getAsString(Data.MIMETYPE); 673916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (TextUtils.isEmpty(mimeType)) { 674916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov Log.e(TAG, "Mimetype is required. Ignoring: " + values); 675916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov continue; 676916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 677916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 678916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov // Won't override the contact name 679916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) { 680916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov continue; 681916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 682916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 68369f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov DataKind kind = accountType.getKindForMimetype(mimeType); 684916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (kind == null) { 6852b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro Log.e(TAG, "Mimetype not supported for account type " 6862b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro + accountType.getAccountTypeAndDataSet() + ". Ignoring: " + values); 687916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov continue; 688916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 689916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 690916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov ValuesDelta entry = ValuesDelta.fromAfter(values); 691916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (isEmpty(entry, kind)) { 692916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov continue; 693916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 694916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 695916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType); 696916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 69750a27b70581b0191995969c63edd1f6a3db3d1b7Makoto Onuki if ((kind.typeOverallMax != 1) || GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) { 698916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov // Check for duplicates 699916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov boolean addEntry = true; 700916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov int count = 0; 701916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (entries != null && entries.size() > 0) { 702916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov for (ValuesDelta delta : entries) { 703916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (!delta.isDelete()) { 704916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (areEqual(delta, values, kind)) { 705916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov addEntry = false; 706916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov break; 707916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 708916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov count++; 709916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 710916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 711916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 712916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 713916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (kind.typeOverallMax != -1 && count >= kind.typeOverallMax) { 714916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov Log.e(TAG, "Mimetype allows at most " + kind.typeOverallMax 715916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov + " entries. Ignoring: " + values); 716916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov addEntry = false; 717916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 718916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 719916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (addEntry) { 720916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov addEntry = adjustType(entry, entries, kind); 721916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 722916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 723916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (addEntry) { 724916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov state.addEntry(entry); 725916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 726916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } else { 727916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov // Non-list entries should not be overridden 728916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov boolean addEntry = true; 729916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (entries != null && entries.size() > 0) { 730916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov for (ValuesDelta delta : entries) { 731916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (!delta.isDelete() && !isEmpty(delta, kind)) { 732916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov addEntry = false; 733916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov break; 734916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 735916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 736916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (addEntry) { 737916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov for (ValuesDelta delta : entries) { 738916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov delta.markDeleted(); 739916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 740916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 741916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 742916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 743916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (addEntry) { 744916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov addEntry = adjustType(entry, entries, kind); 745916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 746916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 747916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (addEntry) { 748916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov state.addEntry(entry); 749916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType)){ 750916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov // Note is most likely to contain large amounts of text 751916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov // that we don't want to drop on the ground. 752916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov for (ValuesDelta delta : entries) { 753916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (!isEmpty(delta, kind)) { 754916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov delta.put(Note.NOTE, delta.getAsString(Note.NOTE) + "\n" 755916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov + values.getAsString(Note.NOTE)); 756916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov break; 757916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 758916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 759916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } else { 760916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov Log.e(TAG, "Will not override mimetype " + mimeType + ". Ignoring: " 761916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov + values); 762916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 763916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 764916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 765916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 766916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 767916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov /** 768916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov * Checks if the data kind allows addition of another entry (e.g. Exchange only 769916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov * supports two "work" phone numbers). If not, tries to switch to one of the 770916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov * unused types. If successful, returns true. 771916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov */ 772916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov private static boolean adjustType( 773916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov ValuesDelta entry, ArrayList<ValuesDelta> entries, DataKind kind) { 774916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (kind.typeColumn == null || kind.typeList == null || kind.typeList.size() == 0) { 775916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return true; 776916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 777916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 778916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov Integer typeInteger = entry.getAsInteger(kind.typeColumn); 779916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov int type = typeInteger != null ? typeInteger : kind.typeList.get(0).rawValue; 780916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 781916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (isTypeAllowed(type, entries, kind)) { 782916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov entry.put(kind.typeColumn, type); 783916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return true; 784916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 785916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 786916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov // Specified type is not allowed - choose the first available type that is allowed 787916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov int size = kind.typeList.size(); 788916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov for (int i = 0; i < size; i++) { 789916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov EditType editType = kind.typeList.get(i); 790916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (isTypeAllowed(editType.rawValue, entries, kind)) { 791916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov entry.put(kind.typeColumn, editType.rawValue); 792916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return true; 793916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 7946a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov } 795916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 796916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return false; 797916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 798916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 799916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov /** 800916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov * Checks if a new entry of the specified type can be added to the raw 801916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov * contact. For example, Exchange only supports two "work" phone numbers, so 802916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov * addition of a third would not be allowed. 803916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov */ 804916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov private static boolean isTypeAllowed(int type, ArrayList<ValuesDelta> entries, DataKind kind) { 805916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov int max = 0; 806916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov int size = kind.typeList.size(); 807916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov for (int i = 0; i < size; i++) { 808916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov EditType editType = kind.typeList.get(i); 809916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (editType.rawValue == type) { 810916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov max = editType.specificMax; 811916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov break; 812916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 813916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 814916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 815916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (max == 0) { 816916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov // This type is not allowed at all 817916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return false; 818916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 819916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 820916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (max == -1) { 821916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov // Unlimited instances of this type are allowed 822916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return true; 823916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 824916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 825916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return getEntryCountByType(entries, kind.typeColumn, type) < max; 8266a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov } 8276a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov 828916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov /** 829916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov * Counts occurrences of the specified type in the supplied entry list. 830916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov */ 831916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov private static int getEntryCountByType( 832916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov ArrayList<ValuesDelta> entries, String typeColumn, int type) { 833916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov int count = 0; 834916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov int size = entries.size(); 835916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov for (int i = 0; i < size; i++) { 836916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov Integer typeInteger = entries.get(i).getAsInteger(typeColumn); 837916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (typeInteger != null && typeInteger == type) { 838916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov count++; 839916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 8406a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov } 841916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return count; 842d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 843d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 844d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey /** 845e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey * Attempt to parse legacy {@link Insert#IM_PROTOCOL} values, replacing them 846e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey * with updated values. 847e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey */ 848c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov @SuppressWarnings("deprecation") 849e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey private static void fixupLegacyImType(Bundle bundle) { 850e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey final String encodedString = bundle.getString(Insert.IM_PROTOCOL); 851e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey if (encodedString == null) return; 852e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey 853e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey try { 854e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey final Object protocol = android.provider.Contacts.ContactMethods 855e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey .decodeImProtocol(encodedString); 856e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey if (protocol instanceof Integer) { 857e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey bundle.putInt(Insert.IM_PROTOCOL, (Integer)protocol); 858e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } else { 859e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey bundle.putString(Insert.IM_PROTOCOL, (String)protocol); 860e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } 861e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } catch (IllegalArgumentException e) { 862e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey // Ignore exception when legacy parser fails 863e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } 864e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } 865e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey 866e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey /** 867d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * Parse a specific entry from the given {@link Bundle} and insert into the 868d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * given {@link EntityDelta}. Silently skips the insert when missing value 869d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * or no valid {@link EditType} found. 870d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * 871d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * @param typeExtra {@link Bundle} key that holds the incoming 872d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * {@link EditType#rawValue} value. 873d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * @param valueExtra {@link Bundle} key that holds the incoming value. 874d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * @param valueColumn Column to write value into {@link ValuesDelta}. 875d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey */ 876c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov public static ValuesDelta parseExtras(EntityDelta state, DataKind kind, Bundle extras, 877d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey String typeExtra, String valueExtra, String valueColumn) { 8787f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey final CharSequence value = extras.getCharSequence(valueExtra); 8797f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey 88069f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov // Bail early if account type doesn't handle this MIME type 881c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (kind == null) return null; 882d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 883d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Bail when can't insert type, or value missing 884d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final boolean canInsert = EntityModifier.canInsert(state, kind); 885d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey final boolean validValue = (value != null && TextUtils.isGraphic(value)); 886c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (!validValue || !canInsert) return null; 887d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 8886164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey // Find exact type when requested, otherwise best available type 8896164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey final boolean hasType = extras.containsKey(typeExtra); 8906164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey final int typeValue = extras.getInt(typeExtra, hasType ? BaseTypes.TYPE_CUSTOM 8916164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey : Integer.MIN_VALUE); 892d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final EditType editType = EntityModifier.getBestValidType(state, kind, true, typeValue); 893d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 894d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Create data row and fill with value 895d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final ValuesDelta child = EntityModifier.insertChild(state, kind, editType); 8967f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey child.put(valueColumn, value.toString()); 897d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 898d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey if (editType != null && editType.customColumn != null) { 899d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Write down label when custom type picked 900e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey final String customType = extras.getString(typeExtra); 901e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey child.put(editType.customColumn, customType); 902d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 903c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov 904c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov return child; 905d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 906d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 907d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa /** 908d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa * Generic mime types with type support (e.g. TYPE_HOME). 909d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa * Here, "type support" means if the data kind has CommonColumns#TYPE or not. Data kinds which 910d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa * have their own migrate methods aren't listed here. 911d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa */ 912d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa private static final Set<String> sGenericMimeTypesWithTypeSupport = new HashSet<String>( 913d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Arrays.asList(Phone.CONTENT_ITEM_TYPE, 914d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Email.CONTENT_ITEM_TYPE, 915d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Im.CONTENT_ITEM_TYPE, 916d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Nickname.CONTENT_ITEM_TYPE, 917d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Website.CONTENT_ITEM_TYPE, 918d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Relation.CONTENT_ITEM_TYPE, 919d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa SipAddress.CONTENT_ITEM_TYPE)); 920d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa private static final Set<String> sGenericMimeTypesWithoutTypeSupport = new HashSet<String>( 921d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Arrays.asList(Organization.CONTENT_ITEM_TYPE, 922d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Note.CONTENT_ITEM_TYPE, 923d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Photo.CONTENT_ITEM_TYPE, 924d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa GroupMembership.CONTENT_ITEM_TYPE)); 925d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // CommonColumns.TYPE cannot be accessed as it is protected interface, so use 926d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Phone.TYPE instead. 927d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa private static final String COLUMN_FOR_TYPE = Phone.TYPE; 928d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa private static final String COLUMN_FOR_LABEL = Phone.LABEL; 929d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa private static final int TYPE_CUSTOM = Phone.TYPE_CUSTOM; 930d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 931d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa /** 932d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa * Migrates old EntityDelta to newly created one with a new restriction supplied from 933d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa * newAccountType. 934d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa * 935d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa * This is only for account switch during account creation (which must be insert operation). 936d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa */ 937d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa public static void migrateStateForNewContact(Context context, 938d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa EntityDelta oldState, EntityDelta newState, 939d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa AccountType oldAccountType, AccountType newAccountType) { 940d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (newAccountType == oldAccountType) { 941d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Just copying all data in oldState isn't enough, but we can still rely on a lot of 942d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // shortcuts. 943d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (DataKind kind : newAccountType.getSortedDataKinds()) { 944d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final String mimeType = kind.mimeType; 945d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // The fields with short/long form capability must be treated properly. 946d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) { 947d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa migrateStructuredName(context, oldState, newState, kind); 948d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 949d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa List<ValuesDelta> entryList = oldState.getMimeEntries(mimeType); 950d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (entryList != null && !entryList.isEmpty()) { 951d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (ValuesDelta entry : entryList) { 952d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa ContentValues values = entry.getAfter(); 953d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (values != null) { 954d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa newState.addEntry(ValuesDelta.fromAfter(values)); 955d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 956d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 957d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 958d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 959d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 960d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 961d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Migrate data supported by the new account type. 962d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // All the other data inside oldState are silently dropped. 963d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (DataKind kind : newAccountType.getSortedDataKinds()) { 9642b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann if (!kind.editable) continue; 965d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final String mimeType = kind.mimeType; 966d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME.equals(mimeType) 967d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa || DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) { 9682b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann // Ignore pseudo data. 969d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa continue; 970d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) { 971d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa migrateStructuredName(context, oldState, newState, kind); 972d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) { 973d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa migratePostal(oldState, newState, kind); 974d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType)) { 975d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa migrateEvent(oldState, newState, kind, null /* default Year */); 976d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else if (sGenericMimeTypesWithoutTypeSupport.contains(mimeType)) { 977d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa migrateGenericWithoutTypeColumn(oldState, newState, kind); 9782b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann } else if (sGenericMimeTypesWithTypeSupport.contains(mimeType)) { 979d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa migrateGenericWithTypeColumn(oldState, newState, kind); 9802b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann } else { 9812b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann throw new IllegalStateException("Unexpected editable mime-type: " + mimeType); 982d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 983d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 984d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 985d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 986d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 987d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa /** 988d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa * Checks {@link DataKind#isList} and {@link DataKind#typeOverallMax}, and restricts 989d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa * the number of entries (ValuesDelta) inside newState. 990d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa */ 991d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa private static ArrayList<ValuesDelta> ensureEntryMaxSize(EntityDelta newState, DataKind kind, 992d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa ArrayList<ValuesDelta> mimeEntries) { 993d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (mimeEntries == null) { 994d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa return null; 995d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 996d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 99750a27b70581b0191995969c63edd1f6a3db3d1b7Makoto Onuki final int typeOverallMax = kind.typeOverallMax; 998d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (typeOverallMax >= 0 && (mimeEntries.size() > typeOverallMax)) { 999d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa ArrayList<ValuesDelta> newMimeEntries = new ArrayList<ValuesDelta>(typeOverallMax); 1000d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (int i = 0; i < typeOverallMax; i++) { 1001d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa newMimeEntries.add(mimeEntries.get(i)); 1002d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1003d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa mimeEntries = newMimeEntries; 1004d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1005d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa return mimeEntries; 1006d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1007d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1008d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa /** @hide Public only for testing. */ 1009d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa public static void migrateStructuredName( 1010d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Context context, EntityDelta oldState, EntityDelta newState, DataKind newDataKind) { 1011d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final ContentValues values = 1012d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa oldState.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE).getAfter(); 1013d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (values == null) { 1014d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa return; 1015d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1016d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1017d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa boolean supportDisplayName = false; 1018d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa boolean supportPhoneticFullName = false; 1019d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa boolean supportPhoneticFamilyName = false; 1020d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa boolean supportPhoneticMiddleName = false; 1021d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa boolean supportPhoneticGivenName = false; 1022d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (EditField editField : newDataKind.fieldList) { 1023d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (StructuredName.DISPLAY_NAME.equals(editField.column)) { 1024d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa supportDisplayName = true; 1025d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1026d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (DataKind.PSEUDO_COLUMN_PHONETIC_NAME.equals(editField.column)) { 1027d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa supportPhoneticFullName = true; 1028d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1029d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (StructuredName.PHONETIC_FAMILY_NAME.equals(editField.column)) { 1030d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa supportPhoneticFamilyName = true; 1031d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1032d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (StructuredName.PHONETIC_MIDDLE_NAME.equals(editField.column)) { 1033d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa supportPhoneticMiddleName = true; 1034d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1035d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (StructuredName.PHONETIC_GIVEN_NAME.equals(editField.column)) { 1036d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa supportPhoneticGivenName = true; 1037d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1038d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1039d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1040d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // DISPLAY_NAME <-> PREFIX, GIVEN_NAME, MIDDLE_NAME, FAMILY_NAME, SUFFIX 1041d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final String displayName = values.getAsString(StructuredName.DISPLAY_NAME); 1042d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!TextUtils.isEmpty(displayName)) { 1043d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!supportDisplayName) { 1044d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Old data has a display name, while the new account doesn't allow it. 1045da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro NameConverter.displayNameToStructuredName(context, displayName, values); 1046d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1047d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // We don't want to migrate unseen data which may confuse users after the creation. 1048d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredName.DISPLAY_NAME); 1049d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1050d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1051d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (supportDisplayName) { 1052d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Old data does not have display name, while the new account requires it. 1053d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(StructuredName.DISPLAY_NAME, 1054da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro NameConverter.structuredNameToDisplayName(context, values)); 1055da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro for (String field : NameConverter.STRUCTURED_NAME_FIELDS) { 1056da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro values.remove(field); 1057da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro } 1058d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1059d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1060d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1061d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Phonetic (full) name <-> PHONETIC_FAMILY_NAME, PHONETIC_MIDDLE_NAME, PHONETIC_GIVEN_NAME 1062d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final String phoneticFullName = values.getAsString(DataKind.PSEUDO_COLUMN_PHONETIC_NAME); 1063d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!TextUtils.isEmpty(phoneticFullName)) { 1064d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!supportPhoneticFullName) { 1065d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Old data has a phonetic (full) name, while the new account doesn't allow it. 1066d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final ContentValues tmpValues = 1067d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa PhoneticNameEditorView.parsePhoneticName(phoneticFullName, null); 1068d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(DataKind.PSEUDO_COLUMN_PHONETIC_NAME); 1069d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (supportPhoneticFamilyName) { 1070d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(StructuredName.PHONETIC_FAMILY_NAME, 1071d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa tmpValues.getAsString(StructuredName.PHONETIC_FAMILY_NAME)); 1072d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1073d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredName.PHONETIC_FAMILY_NAME); 1074d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1075d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (supportPhoneticMiddleName) { 1076d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(StructuredName.PHONETIC_MIDDLE_NAME, 1077d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa tmpValues.getAsString(StructuredName.PHONETIC_MIDDLE_NAME)); 1078d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1079d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredName.PHONETIC_MIDDLE_NAME); 1080d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1081d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (supportPhoneticGivenName) { 1082d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(StructuredName.PHONETIC_GIVEN_NAME, 1083d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa tmpValues.getAsString(StructuredName.PHONETIC_GIVEN_NAME)); 1084d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1085d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredName.PHONETIC_GIVEN_NAME); 1086d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1087d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1088d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1089d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (supportPhoneticFullName) { 1090d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Old data does not have a phonetic (full) name, while the new account requires it. 1091d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(DataKind.PSEUDO_COLUMN_PHONETIC_NAME, 1092d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa PhoneticNameEditorView.buildPhoneticName( 1093d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredName.PHONETIC_FAMILY_NAME), 1094d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredName.PHONETIC_MIDDLE_NAME), 1095d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredName.PHONETIC_GIVEN_NAME))); 1096d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1097d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!supportPhoneticFamilyName) { 1098d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredName.PHONETIC_FAMILY_NAME); 1099d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1100d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!supportPhoneticMiddleName) { 1101d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredName.PHONETIC_MIDDLE_NAME); 1102d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1103d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!supportPhoneticGivenName) { 1104d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredName.PHONETIC_GIVEN_NAME); 1105d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1106d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1107d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1108d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa newState.addEntry(ValuesDelta.fromAfter(values)); 1109d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1110d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1111d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa /** @hide Public only for testing. */ 1112d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa public static void migratePostal(EntityDelta oldState, EntityDelta newState, 1113d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa DataKind newDataKind) { 1114d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind, 1115d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa oldState.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE)); 1116d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (mimeEntries == null || mimeEntries.isEmpty()) { 1117d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa return; 1118d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1119d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1120d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa boolean supportFormattedAddress = false; 1121d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa boolean supportStreet = false; 1122d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final String firstColumn = newDataKind.fieldList.get(0).column; 1123d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (EditField editField : newDataKind.fieldList) { 1124d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (StructuredPostal.FORMATTED_ADDRESS.equals(editField.column)) { 1125d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa supportFormattedAddress = true; 1126d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1127d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (StructuredPostal.STREET.equals(editField.column)) { 1128d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa supportStreet = true; 1129d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1130d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1131d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1132d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final Set<Integer> supportedTypes = new HashSet<Integer>(); 1133d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (newDataKind.typeList != null && !newDataKind.typeList.isEmpty()) { 1134d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (EditType editType : newDataKind.typeList) { 1135d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa supportedTypes.add(editType.rawValue); 1136d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1137d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1138d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1139d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (ValuesDelta entry : mimeEntries) { 1140d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final ContentValues values = entry.getAfter(); 1141d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (values == null) { 1142d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa continue; 1143d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1144d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final Integer oldType = values.getAsInteger(StructuredPostal.TYPE); 1145d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!supportedTypes.contains(oldType)) { 1146d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa int defaultType; 1147d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (newDataKind.defaultValues != null) { 1148d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa defaultType = newDataKind.defaultValues.getAsInteger(StructuredPostal.TYPE); 1149d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1150d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa defaultType = newDataKind.typeList.get(0).rawValue; 1151d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1152d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(StructuredPostal.TYPE, defaultType); 1153d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (oldType != null && oldType == StructuredPostal.TYPE_CUSTOM) { 1154d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredPostal.LABEL); 1155d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1156d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1157d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1158d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final String formattedAddress = values.getAsString(StructuredPostal.FORMATTED_ADDRESS); 1159d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!TextUtils.isEmpty(formattedAddress)) { 1160d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!supportFormattedAddress) { 1161d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Old data has a formatted address, while the new account doesn't allow it. 1162d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredPostal.FORMATTED_ADDRESS); 1163d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1164d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Unlike StructuredName we don't have logic to split it, so first 1165d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // try to use street field and. If the new account doesn't have one, 1166d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // then select first one anyway. 1167d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (supportStreet) { 1168d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(StructuredPostal.STREET, formattedAddress); 1169d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1170d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(firstColumn, formattedAddress); 1171d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1172d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1173d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1174d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (supportFormattedAddress) { 1175d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Old data does not have formatted address, while the new account requires it. 1176d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Unlike StructuredName we don't have logic to join multiple address values. 1177d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Use poor join heuristics for now. 1178d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa String[] structuredData; 1179d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final boolean useJapaneseOrder = 1180d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Locale.JAPANESE.getLanguage().equals(Locale.getDefault().getLanguage()); 1181d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (useJapaneseOrder) { 1182d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa structuredData = new String[] { 1183d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.COUNTRY), 1184d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.POSTCODE), 1185d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.REGION), 1186d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.CITY), 1187d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.NEIGHBORHOOD), 1188d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.STREET), 1189d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.POBOX) }; 1190d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1191d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa structuredData = new String[] { 1192d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.POBOX), 1193d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.STREET), 1194d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.NEIGHBORHOOD), 1195d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.CITY), 1196d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.REGION), 1197d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.POSTCODE), 1198d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.COUNTRY) }; 1199d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1200d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final StringBuilder builder = new StringBuilder(); 1201d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (String elem : structuredData) { 1202d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!TextUtils.isEmpty(elem)) { 1203d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa builder.append(elem + "\n"); 1204d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1205d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1206d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(StructuredPostal.FORMATTED_ADDRESS, builder.toString()); 1207d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1208d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredPostal.POBOX); 1209d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredPostal.STREET); 1210d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredPostal.NEIGHBORHOOD); 1211d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredPostal.CITY); 1212d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredPostal.REGION); 1213d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredPostal.POSTCODE); 1214d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredPostal.COUNTRY); 1215d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1216d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1217d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1218d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa newState.addEntry(ValuesDelta.fromAfter(values)); 1219d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1220d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1221d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1222d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa /** @hide Public only for testing. */ 1223d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa public static void migrateEvent(EntityDelta oldState, EntityDelta newState, 1224d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa DataKind newDataKind, Integer defaultYear) { 1225d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind, 1226d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa oldState.getMimeEntries(Event.CONTENT_ITEM_TYPE)); 1227d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (mimeEntries == null || mimeEntries.isEmpty()) { 1228d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa return; 1229d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1230d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1231d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final Map<Integer, EventEditType> allowedTypes = new HashMap<Integer, EventEditType>(); 1232d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (EditType editType : newDataKind.typeList) { 1233d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa allowedTypes.put(editType.rawValue, (EventEditType) editType); 1234d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1235d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (ValuesDelta entry : mimeEntries) { 1236d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final ContentValues values = entry.getAfter(); 1237d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (values == null) { 1238d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa continue; 1239d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1240d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final String dateString = values.getAsString(Event.START_DATE); 1241d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final Integer type = values.getAsInteger(Event.TYPE); 1242d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (type != null && allowedTypes.containsKey(type) && !TextUtils.isEmpty(dateString)) { 1243d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa EventEditType suitableType = allowedTypes.get(type); 1244d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1245d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final ParsePosition position = new ParsePosition(0); 1246d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa boolean yearOptional = false; 1247d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Date date = DateUtils.DATE_AND_TIME_FORMAT.parse(dateString, position); 1248d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (date == null) { 1249d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa yearOptional = true; 1250d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa date = DateUtils.NO_YEAR_DATE_FORMAT.parse(dateString, position); 1251d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1252d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (date != null) { 1253d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (yearOptional && !suitableType.isYearOptional()) { 1254d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // The new EditType doesn't allow optional year. Supply default. 1255d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final Calendar calendar = Calendar.getInstance(DateUtils.UTC_TIMEZONE, 1256d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Locale.US); 1257d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (defaultYear == null) { 1258d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa defaultYear = calendar.get(Calendar.YEAR); 1259d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1260d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa calendar.setTime(date); 1261d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final int month = calendar.get(Calendar.MONTH); 1262d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final int day = calendar.get(Calendar.DAY_OF_MONTH); 1263d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Exchange requires 8:00 for birthdays 1264d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa calendar.set(defaultYear, month, day, 1265d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa EventFieldEditorView.getDefaultHourForBirthday(), 0, 0); 1266d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(Event.START_DATE, 1267d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa DateUtils.FULL_DATE_FORMAT.format(calendar.getTime())); 1268d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1269d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1270d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa newState.addEntry(ValuesDelta.fromAfter(values)); 1271d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1272d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Just drop it. 1273d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1274d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1275d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1276d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1277d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa /** @hide Public only for testing. */ 1278d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa public static void migrateGenericWithoutTypeColumn( 1279d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa EntityDelta oldState, EntityDelta newState, DataKind newDataKind) { 1280d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind, 1281d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa oldState.getMimeEntries(newDataKind.mimeType)); 1282d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (mimeEntries == null || mimeEntries.isEmpty()) { 1283d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa return; 1284d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1285d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1286d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (ValuesDelta entry : mimeEntries) { 1287d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa ContentValues values = entry.getAfter(); 1288d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (values != null) { 1289d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa newState.addEntry(ValuesDelta.fromAfter(values)); 1290d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1291d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1292d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1293d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1294d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa /** @hide Public only for testing. */ 1295d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa public static void migrateGenericWithTypeColumn( 1296d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa EntityDelta oldState, EntityDelta newState, DataKind newDataKind) { 1297d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final ArrayList<ValuesDelta> mimeEntries = oldState.getMimeEntries(newDataKind.mimeType); 1298d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (mimeEntries == null || mimeEntries.isEmpty()) { 1299d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa return; 1300d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1301d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1302d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Note that type specified with the old account may be invalid with the new account, while 1303d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // we want to preserve its data as much as possible. e.g. if a user typed a phone number 1304d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // with a type which is valid with an old account but not with a new account, the user 1305d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // probably wants to have the number with default type, rather than seeing complete data 1306d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // loss. 1307d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // 1308d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Specifically, this method works as follows: 1309d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // 1. detect defaultType 1310d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // 2. prepare constants & variables for iteration 1311d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // 3. iterate over mimeEntries: 1312d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // 3.1 stop iteration if total number of mimeEntries reached typeOverallMax specified in 1313d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // DataKind 1314d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // 3.2 replace unallowed types with defaultType 1315d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // 3.3 check if the number of entries is below specificMax specified in AccountType 1316d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1317d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Here, defaultType can be supplied in two ways 1318d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // - via kind.defaultValues 1319d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // - via kind.typeList.get(0).rawValue 1320d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Integer defaultType = null; 1321d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (newDataKind.defaultValues != null) { 1322d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa defaultType = newDataKind.defaultValues.getAsInteger(COLUMN_FOR_TYPE); 1323d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1324d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final Set<Integer> allowedTypes = new HashSet<Integer>(); 1325d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // key: type, value: the number of entries allowed for the type (specificMax) 1326d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final Map<Integer, Integer> typeSpecificMaxMap = new HashMap<Integer, Integer>(); 1327d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (defaultType != null) { 1328d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa allowedTypes.add(defaultType); 1329d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa typeSpecificMaxMap.put(defaultType, -1); 1330d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1331d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Note: typeList may be used in different purposes when defaultValues are specified. 1332d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Especially in IM, typeList contains available protocols (e.g. PROTOCOL_GOOGLE_TALK) 1333d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // instead of "types" which we want to treate here (e.g. TYPE_HOME). So we don't add 1334d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // anything other than defaultType into allowedTypes and typeSpecificMapMax. 1335d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!Im.CONTENT_ITEM_TYPE.equals(newDataKind.mimeType) && 1336d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa newDataKind.typeList != null && !newDataKind.typeList.isEmpty()) { 1337d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (EditType editType : newDataKind.typeList) { 1338d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa allowedTypes.add(editType.rawValue); 1339d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa typeSpecificMaxMap.put(editType.rawValue, editType.specificMax); 1340d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1341d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (defaultType == null) { 1342d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa defaultType = newDataKind.typeList.get(0).rawValue; 1343d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1344d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1345d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1346d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (defaultType == null) { 1347d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Log.w(TAG, "Default type isn't available for mimetype " + newDataKind.mimeType); 1348d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1349d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 135050a27b70581b0191995969c63edd1f6a3db3d1b7Makoto Onuki final int typeOverallMax = newDataKind.typeOverallMax; 1351d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1352d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // key: type, value: the number of current entries. 1353d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final Map<Integer, Integer> currentEntryCount = new HashMap<Integer, Integer>(); 1354d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa int totalCount = 0; 1355d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1356d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (ValuesDelta entry : mimeEntries) { 1357d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (typeOverallMax != -1 && totalCount >= typeOverallMax) { 1358d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa break; 1359d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1360d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1361d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final ContentValues values = entry.getAfter(); 1362d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (values == null) { 1363d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa continue; 1364d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1365d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1366d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final Integer oldType = entry.getAsInteger(COLUMN_FOR_TYPE); 1367d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final Integer typeForNewAccount; 1368d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!allowedTypes.contains(oldType)) { 1369d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // The new account doesn't support the type. 1370d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (defaultType != null) { 1371d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa typeForNewAccount = defaultType.intValue(); 1372d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(COLUMN_FOR_TYPE, defaultType.intValue()); 1373d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (oldType != null && oldType == TYPE_CUSTOM) { 1374d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(COLUMN_FOR_LABEL); 1375d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1376d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1377d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa typeForNewAccount = null; 1378d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(COLUMN_FOR_TYPE); 1379d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1380d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1381d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa typeForNewAccount = oldType; 1382d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1383d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (typeForNewAccount != null) { 1384d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final int specificMax = (typeSpecificMaxMap.containsKey(typeForNewAccount) ? 1385d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa typeSpecificMaxMap.get(typeForNewAccount) : 0); 1386d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (specificMax >= 0) { 1387d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final int currentCount = (currentEntryCount.get(typeForNewAccount) != null ? 1388d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa currentEntryCount.get(typeForNewAccount) : 0); 1389d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (currentCount >= specificMax) { 1390d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa continue; 1391d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1392d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa currentEntryCount.put(typeForNewAccount, currentCount + 1); 1393d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1394d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1395d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa newState.addEntry(ValuesDelta.fromAfter(values)); 1396d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa totalCount++; 1397d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1398d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 13992ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey} 1400