12ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey/* 22ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Copyright (C) 2009 The Android Open Source Project 32ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * 42ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License"); 52ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * you may not use this file except in compliance with the License. 62ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * You may obtain a copy of the License at 72ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * 82ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * http://www.apache.org/licenses/LICENSE-2.0 92ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * 102ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Unless required by applicable law or agreed to in writing, software 112ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS, 122ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * See the License for the specific language governing permissions and 142ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * limitations under the License. 152ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 162ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 172ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeypackage com.android.contacts.model; 182ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 192ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeyimport android.content.ContentValues; 20802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkeyimport android.content.Context; 21802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkeyimport android.database.Cursor; 22c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.net.Uri; 23d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.os.Bundle; 24c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract; 257f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.BaseTypes; 26d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Email; 27d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Event; 28c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership; 29d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Im; 30d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Nickname; 3116c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Note; 3216c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Organization; 33d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.Phone; 34a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintanaimport android.provider.ContactsContract.CommonDataKinds.Photo; 35d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Relation; 36d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.SipAddress; 37d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredName; 38d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.CommonDataKinds.StructuredPostal; 39d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport android.provider.ContactsContract.CommonDataKinds.Website; 40c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract.Data; 41c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract.Intents; 42d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.provider.ContactsContract.Intents.Insert; 43c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikovimport android.provider.ContactsContract.RawContacts; 44d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkeyimport android.text.TextUtils; 456f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkeyimport android.util.Log; 467b1255eab0729fd43a8aae9933bd79afa50a53a9Daniel Lehmannimport android.util.SparseArray; 4707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport android.util.SparseIntArray; 482ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 49e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.ContactsUtils; 50e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.editor.EventFieldEditorView; 51e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.editor.PhoneticNameEditorView; 52851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.RawContactDelta.ValuesDelta; 53851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.account.AccountType; 54851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.account.AccountType.EditField; 55851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.account.AccountType.EditType; 56851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.account.AccountType.EventEditType; 57851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.account.GoogleAccountType; 58851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.dataitem.DataKind; 59851222a96b5d68602fb361ea3527101e893f67e3Maurice Chuimport com.android.contacts.model.dataitem.StructuredNameDataItem; 60e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.util.DateUtils; 61e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.util.NameConverter; 62e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Cheng 63d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.text.ParsePosition; 6407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport java.util.ArrayList; 65d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Arrays; 66d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Calendar; 67d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Date; 68d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.HashSet; 6907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkeyimport java.util.Iterator; 702ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkeyimport java.util.List; 71d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Locale; 72d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawaimport java.util.Set; 732ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 742ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey/** 75851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu * Helper methods for modifying an {@link RawContactDelta}, such as inserting 764597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov * new rows, or enforcing {@link AccountType}. 772ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 78851222a96b5d68602fb361ea3527101e893f67e3Maurice Chupublic class RawContactModifier { 79851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu private static final String TAG = RawContactModifier.class.getSimpleName(); 806f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 81a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan /** Set to true in order to view logs on entity operations */ 82a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan private static final boolean DEBUG = false; 83a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan 842ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 85851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu * For the given {@link RawContactDelta}, determine if the given 862ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * {@link DataKind} could be inserted under specific 874597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov * {@link AccountType}. 882ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 89851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu public static boolean canInsert(RawContactDelta state, DataKind kind) { 9007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Insert possible when have valid types and under overall maximum 91ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey final int visibleCount = state.getMimeEntriesCount(kind.mimeType, true); 9207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final boolean validTypes = hasValidTypes(state, kind); 9307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final boolean validOverall = (kind.typeOverallMax == -1) 94ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey || (visibleCount < kind.typeOverallMax); 9507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return (validTypes && validOverall); 9607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 9707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 98851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu public static boolean hasValidTypes(RawContactDelta state, DataKind kind) { 99851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu if (RawContactModifier.hasEditTypes(kind)) { 100d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return (getValidTypes(state, kind).size() > 0); 101d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } else { 102d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return true; 103d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 1042ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 1052ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 1062ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 107aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey * Ensure that at least one of the given {@link DataKind} exists in the 108851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu * given {@link RawContactDelta} state, and try creating one if none exist. 1096fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * @return The child (either newly created or the first existing one), or null if the 1106fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro * account doesn't support this {@link DataKind}. 111aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey */ 1126fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro public static ValuesDelta ensureKindExists( 113851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu RawContactDelta state, AccountType accountType, String mimeType) { 11469f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov final DataKind kind = accountType.getKindForMimetype(mimeType); 115ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey final boolean hasChild = state.getMimeEntriesCount(mimeType, true) > 0; 116aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey 1176fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (kind != null) { 1186fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (hasChild) { 1196fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Return the first entry. 1206fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return state.getMimeEntries(mimeType).get(0); 1216fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } else { 1226fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro // Create child when none exists and valid kind 1236fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro final ValuesDelta child = insertChild(state, kind); 1246fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro if (kind.mimeType.equals(Photo.CONTENT_ITEM_TYPE)) { 1256fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro child.setFromTemplate(true); 1266fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro } 1276fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return child; 128e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi } 129aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey } 1306fa7384a1a5576ace18159de61af91c8b7c34dc6Dave Santoro return null; 131aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey } 132aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey 133aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey /** 134851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu * For the given {@link RawContactDelta} and {@link DataKind}, return the 1352ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * list possible {@link EditType} options available based on 1364597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov * {@link AccountType}. 1372ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 138851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu public static ArrayList<EditType> getValidTypes(RawContactDelta state, DataKind kind) { 13907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return getValidTypes(state, kind, null, true, null); 14007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 14107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 14207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 143851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu * For the given {@link RawContactDelta} and {@link DataKind}, return the 14407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * list possible {@link EditType} options available based on 1454597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov * {@link AccountType}. 14607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * 14707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * @param forceInclude Always include this {@link EditType} in the returned 14807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * list, even when an otherwise-invalid choice. This is useful 14907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * when showing a dialog that includes the current type. 15007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 151851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu public static ArrayList<EditType> getValidTypes(RawContactDelta state, DataKind kind, 1522ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey EditType forceInclude) { 15307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return getValidTypes(state, kind, forceInclude, true, null); 15407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 15507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 15607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 157851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu * For the given {@link RawContactDelta} and {@link DataKind}, return the 15807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * list possible {@link EditType} options available based on 1594597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov * {@link AccountType}. 16007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * 16107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * @param forceInclude Always include this {@link EditType} in the returned 16207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * list, even when an otherwise-invalid choice. This is useful 16307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * when showing a dialog that includes the current type. 16407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * @param includeSecondary If true, include any valid types marked as 16507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * {@link EditType#secondary}. 16607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * @param typeCount When provided, will be used for the frequency count of 16707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * each {@link EditType}, otherwise built using 168851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu * {@link #getTypeFrequencies(RawContactDelta, DataKind)}. 16907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 170851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu private static ArrayList<EditType> getValidTypes(RawContactDelta state, DataKind kind, 17107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey EditType forceInclude, boolean includeSecondary, SparseIntArray typeCount) { 172da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro final ArrayList<EditType> validTypes = new ArrayList<EditType>(); 17307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 17407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Bail early if no types provided 17507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (!hasEditTypes(kind)) return validTypes; 17607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 17707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (typeCount == null) { 17807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Build frequency counts if not provided 17907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey typeCount = getTypeFrequencies(state, kind); 18007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 18107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 18207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Build list of valid types 18307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final int overallCount = typeCount.get(FREQUENCY_TOTAL); 18407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey for (EditType type : kind.typeList) { 185d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final boolean validOverall = (kind.typeOverallMax == -1 ? true 186d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey : overallCount < kind.typeOverallMax); 187d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final boolean validSpecific = (type.specificMax == -1 ? true : typeCount 188d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey .get(type.rawValue) < type.specificMax); 18907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final boolean validSecondary = (includeSecondary ? true : !type.secondary); 19007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final boolean forcedInclude = type.equals(forceInclude); 191d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey if (forcedInclude || (validOverall && validSpecific && validSecondary)) { 19207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Type is valid when no limit, under limit, or forced include 19307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey validTypes.add(type); 19407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 19507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 19607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 19707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return validTypes; 19807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 19907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 20007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey private static final int FREQUENCY_TOTAL = Integer.MIN_VALUE; 20107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 20207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 20307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * Count up the frequency that each {@link EditType} appears in the given 204851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu * {@link RawContactDelta}. The returned {@link SparseIntArray} maps from 20507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * {@link EditType#rawValue} to counts, with the total overall count stored 20607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * as {@link #FREQUENCY_TOTAL}. 20707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 208851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu private static SparseIntArray getTypeFrequencies(RawContactDelta state, DataKind kind) { 20907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final SparseIntArray typeCount = new SparseIntArray(); 21007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 21107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Find all entries for this kind, bailing early if none found 2128d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey final List<ValuesDelta> mimeEntries = state.getMimeEntries(kind.mimeType); 21307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (mimeEntries == null) return typeCount; 21407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 21507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey int totalCount = 0; 2168d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey for (ValuesDelta entry : mimeEntries) { 21707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Only count visible entries 21807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (!entry.isVisible()) continue; 21907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey totalCount++; 22007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 22107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final EditType type = getCurrentType(entry, kind); 22207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (type != null) { 22307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final int count = typeCount.get(type.rawValue); 22407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey typeCount.put(type.rawValue, count + 1); 22507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 22607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 22707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey typeCount.put(FREQUENCY_TOTAL, totalCount); 22807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return typeCount; 2292ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 2302ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 2312ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 2322ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Check if the given {@link DataKind} has multiple types that should be 2332ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * displayed for users to pick. 2342ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 2352ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey public static boolean hasEditTypes(DataKind kind) { 2362ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey return kind.typeList != null && kind.typeList.size() > 0; 2372ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 2382ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 2392ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 2402ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Find the {@link EditType} that describes the given 2418d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey * {@link ValuesDelta} row, assuming the given {@link DataKind} dictates 2422ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * the possible types. 2432ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 2448d9767d79f9fe2a09ee6e981b1fede7e9863d62aJeff Sharkey public static EditType getCurrentType(ValuesDelta entry, DataKind kind) { 24507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final Long rawValue = entry.getAsLong(kind.typeColumn); 24607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (rawValue == null) return null; 24707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return getType(kind, rawValue.intValue()); 24807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 24907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 25007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 25111d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar * Find the {@link EditType} that describes the given {@link ContentValues} row, 25211d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar * assuming the given {@link DataKind} dictates the possible types. 25311d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar */ 25411d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar public static EditType getCurrentType(ContentValues entry, DataKind kind) { 25511d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar if (kind.typeColumn == null) return null; 25649d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey final Integer rawValue = entry.getAsInteger(kind.typeColumn); 25749d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey if (rawValue == null) return null; 25811d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar return getType(kind, rawValue); 25911d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar } 26011d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar 26111d628c52c16a18c0caf40df0ce43396e7592ffcEvan Millar /** 262802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey * Find the {@link EditType} that describes the given {@link Cursor} row, 263802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey * assuming the given {@link DataKind} dictates the possible types. 264802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey */ 265802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey public static EditType getCurrentType(Cursor cursor, DataKind kind) { 266ad40a919d318c7aa26f252d3f0fe541e2c44a211Jeff Sharkey if (kind.typeColumn == null) return null; 267802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey final int index = cursor.getColumnIndex(kind.typeColumn); 26849d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey if (index == -1) return null; 269802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey final int rawValue = cursor.getInt(index); 270802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey return getType(kind, rawValue); 271802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey } 272802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey 273802b205ac677ffbde9aaf4fa3cfa9b94e8c98a44Jeff Sharkey /** 27407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * Find the {@link EditType} with the given {@link EditType#rawValue}. 27507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 27607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey public static EditType getType(DataKind kind, int rawValue) { 2772ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey for (EditType type : kind.typeList) { 2782ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey if (type.rawValue == rawValue) { 2792ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey return type; 2802ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 2812ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 2822ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey return null; 2832ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 2842ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 2852ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey /** 28649d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey * Return the precedence for the the given {@link EditType#rawValue}, where 28749d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey * lower numbers are higher precedence. 28849d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey */ 28949d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey public static int getTypePrecedence(DataKind kind, int rawValue) { 29049d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey for (int i = 0; i < kind.typeList.size(); i++) { 29149d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey final EditType type = kind.typeList.get(i); 29249d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey if (type.rawValue == rawValue) { 29349d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey return i; 29449d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey } 29549d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey } 29649d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey return Integer.MAX_VALUE; 29749d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey } 29849d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey 29949d17b3e7692ae9442c342db236fa93d4a837c28Jeff Sharkey /** 30007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * Find the best {@link EditType} for a potential insert. The "best" is the 30107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * first primary type that doesn't already exist. When all valid types 30207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * exist, we pick the last valid option. 30307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 304851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu public static EditType getBestValidType(RawContactDelta state, DataKind kind, 305d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey boolean includeSecondary, int exactValue) { 30607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Shortcut when no types 30707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (kind.typeColumn == null) return null; 30807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 30907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Find type counts and valid primary types, bail if none 31007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final SparseIntArray typeCount = getTypeFrequencies(state, kind); 31107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final ArrayList<EditType> validTypes = getValidTypes(state, kind, null, includeSecondary, 31207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey typeCount); 31307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (validTypes.size() == 0) return null; 31407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 31507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Keep track of the last valid type 31607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final EditType lastType = validTypes.get(validTypes.size() - 1); 31707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 31807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Remove any types that already exist 31907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey Iterator<EditType> iterator = validTypes.iterator(); 32007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey while (iterator.hasNext()) { 32107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final EditType type = iterator.next(); 32207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey final int count = typeCount.get(type.rawValue); 32307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 324e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey if (exactValue == type.rawValue) { 325d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Found exact value match 326d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return type; 327d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 328d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 32907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (count > 0) { 33007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Type already appears, so don't consider 33107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey iterator.remove(); 33207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 33307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 33407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 33507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Use the best remaining, otherwise the last valid 33607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (validTypes.size() > 0) { 33707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return validTypes.get(0); 33807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } else { 33907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey return lastType; 34007c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 34107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 34207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 34307c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 3442ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey * Insert a new child of kind {@link DataKind} into the given 345851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu * {@link RawContactDelta}. Tries using the best {@link EditType} found using 346851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu * {@link #getBestValidType(RawContactDelta, DataKind, boolean, int)}. 3472ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey */ 348851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu public static ValuesDelta insertChild(RawContactDelta state, DataKind kind) { 34907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // First try finding a valid primary 350d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey EditType bestType = getBestValidType(state, kind, false, Integer.MIN_VALUE); 35107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (bestType == null) { 35207c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // No valid primary found, so expand search to secondary 353d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey bestType = getBestValidType(state, kind, true, Integer.MIN_VALUE); 35407c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 355d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return insertChild(state, kind, bestType); 35607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey } 35707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey 35807c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey /** 35907c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey * Insert a new child of kind {@link DataKind} into the given 360851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu * {@link RawContactDelta}, marked with the given {@link EditType}. 36107c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey */ 362851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu public static ValuesDelta insertChild(RawContactDelta state, DataKind kind, EditType type) { 363d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Bail early if invalid kind 364d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey if (kind == null) return null; 3652ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey final ContentValues after = new ContentValues(); 3662ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 367e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey // Our parent CONTACT_ID is provided later 3682ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey after.put(Data.MIMETYPE, kind.mimeType); 369e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey 370e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey // Fill-in with any requested default values 371e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey if (kind.defaultValues != null) { 372e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey after.putAll(kind.defaultValues); 373e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey } 374e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey 37507c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey if (kind.typeColumn != null && type != null) { 37607c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey // Set type, if provided 37707c2e41b5ce7abeb9dd14e5d700a8fb928723330Jeff Sharkey after.put(kind.typeColumn, type.rawValue); 378e731d426eda3692402f3cecdc29421fcf7f1fb54Jeff Sharkey } 3792ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 380d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey final ValuesDelta child = ValuesDelta.fromAfter(after); 381a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov state.addEntry(child); 382d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey return child; 3832ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey } 3842ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey 385d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey /** 386851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu * Processing to trim any empty {@link ValuesDelta} and {@link RawContactDelta} 387851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu * from the given {@link RawContactDeltaList}, assuming the given {@link AccountTypeManager} 3887f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey * dictates the structure for various fields. This method ignores rows not 3894597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov * described by the {@link AccountType}. 3907f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey */ 391851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu public static void trimEmpty(RawContactDeltaList set, AccountTypeManager accountTypes) { 392851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu for (RawContactDelta state : set) { 3932b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro ValuesDelta values = state.getValues(); 3942b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE); 3952b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro final String dataSet = values.getAsString(RawContacts.DATA_SET); 3962b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro final AccountType type = accountTypes.getAccountType(accountType, dataSet); 397a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov trimEmpty(state, type); 3987f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey } 3997f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey } 4007f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey 401851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu public static boolean hasChanges(RawContactDeltaList set, AccountTypeManager accountTypes) { 402a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov if (set.isMarkedForSplitting() || set.isMarkedForJoining()) { 403a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov return true; 404a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 405a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 406851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu for (RawContactDelta state : set) { 4072b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro ValuesDelta values = state.getValues(); 4082b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro final String accountType = values.getAsString(RawContacts.ACCOUNT_TYPE); 4092b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro final String dataSet = values.getAsString(RawContacts.DATA_SET); 4102b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro final AccountType type = accountTypes.getAccountType(accountType, dataSet); 411a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov if (hasChanges(state, type)) { 412a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov return true; 413a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 414a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 415a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov return false; 416a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 417a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 4187f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey /** 4196f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey * Processing to trim any empty {@link ValuesDelta} rows from the given 420851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu * {@link RawContactDelta}, assuming the given {@link AccountType} dictates 4216f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey * the structure for various fields. This method ignores rows not described 4224597c92d655d45447780b32c7572acef110b6ed1Dmitri Plotnikov * by the {@link AccountType}. 4236f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey */ 424851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu public static void trimEmpty(RawContactDelta state, AccountType accountType) { 4257f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey boolean hasValues = false; 4267f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey 4276f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey // Walk through entries for each well-known kind 42869f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov for (DataKind kind : accountType.getSortedDataKinds()) { 4296f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey final String mimeType = kind.mimeType; 4306f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey final ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType); 4316f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey if (entries == null) continue; 4326f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 4336f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey for (ValuesDelta entry : entries) { 4347f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey // Skip any values that haven't been touched 4356f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey final boolean touched = entry.isInsert() || entry.isUpdate(); 4367f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey if (!touched) { 4377f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey hasValues = true; 4387f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey continue; 4397f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey } 4407f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey 441a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana // Test and remove this row if empty and it isn't a photo from google 44269f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov final boolean isGoogleAccount = TextUtils.equals(GoogleAccountType.ACCOUNT_TYPE, 443a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana state.getValues().getAsString(RawContacts.ACCOUNT_TYPE)); 444a6ff67e83082c7953d3b70181549ff4cabce2ebaFred Quintana final boolean isPhoto = TextUtils.equals(Photo.CONTENT_ITEM_TYPE, kind.mimeType); 44569f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov final boolean isGooglePhoto = isPhoto && isGoogleAccount; 446e86b928be36bb3e7136796ad519e736325d1d2b1Megha Joshi 447851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu if (RawContactModifier.isEmpty(entry, kind) && !isGooglePhoto) { 448a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan if (DEBUG) { 449a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan Log.v(TAG, "Trimming: " + entry.toString()); 450a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan } 4516f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey entry.markDeleted(); 452d2fdb9095e0009408b89e24048154ea7a8a14413Megha Joshi } else if (!entry.isFromTemplate()) { 4537f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey hasValues = true; 4546f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4556f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4566f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4577f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey if (!hasValues) { 4587f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey // Trim overall entity if no children exist 4597f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey state.markDeleted(); 4607f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey } 4616f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4626f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 463851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu private static boolean hasChanges(RawContactDelta state, AccountType accountType) { 464a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov for (DataKind kind : accountType.getSortedDataKinds()) { 465a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov final String mimeType = kind.mimeType; 466a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov final ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType); 467a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov if (entries == null) continue; 468a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 469a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov for (ValuesDelta entry : entries) { 470de0898f0337d3c6ea818237b0a94acf1f15b6299Daniel Lehmann // An empty Insert must be ignored, because it won't save anything (an example 471de0898f0337d3c6ea818237b0a94acf1f15b6299Daniel Lehmann // is an empty name that stays empty) 472de0898f0337d3c6ea818237b0a94acf1f15b6299Daniel Lehmann final boolean isRealInsert = entry.isInsert() && !isEmpty(entry, kind); 473de0898f0337d3c6ea818237b0a94acf1f15b6299Daniel Lehmann if (isRealInsert || entry.isUpdate() || entry.isDelete()) { 474a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov return true; 475a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 476a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 477a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 478a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov return false; 479a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 480a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 4816f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey /** 4826f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey * Test if the given {@link ValuesDelta} would be considered "empty" in 4836f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey * terms of {@link DataKind#fieldList}. 4846f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey */ 4856f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey public static boolean isEmpty(ValuesDelta values, DataKind kind) { 486f3cdeca5e0b3a7f9411b6babad5de2925623372aDmitri Plotnikov if (Photo.CONTENT_ITEM_TYPE.equals(kind.mimeType)) { 487c863b0171f2a54a0ee71b34b33034aeea2464ccaDmitri Plotnikov return values.isInsert() && values.getAsByteArray(Photo.PHOTO) == null; 488f3cdeca5e0b3a7f9411b6babad5de2925623372aDmitri Plotnikov } 489f3cdeca5e0b3a7f9411b6babad5de2925623372aDmitri Plotnikov 490dbeb965d19a3c03e78cd54d9fbe7337b4fbb06f2Jeff Sharkey // No defined fields mean this row is always empty 491dbeb965d19a3c03e78cd54d9fbe7337b4fbb06f2Jeff Sharkey if (kind.fieldList == null) return true; 492dbeb965d19a3c03e78cd54d9fbe7337b4fbb06f2Jeff Sharkey 4936f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey for (EditField field : kind.fieldList) { 4946f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey // If any field has values, we're not empty 4956f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey final String value = values.getAsString(field.column); 496e31dac84479a4c7b356edfc062a447cdfb5efc69Jeff Sharkey if (ContactsUtils.isGraphic(value)) { 497c863b0171f2a54a0ee71b34b33034aeea2464ccaDmitri Plotnikov return false; 4986f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 4996f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 5006f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 501c863b0171f2a54a0ee71b34b33034aeea2464ccaDmitri Plotnikov return true; 5026f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey } 5036f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey 5046f8d46b5fb96c2bead1317aae93a73fc89b093abJeff Sharkey /** 505916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov * Compares corresponding fields in values1 and values2. Only the fields 506916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov * declared by the DataKind are taken into consideration. 507916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov */ 508916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov protected static boolean areEqual(ValuesDelta values1, ContentValues values2, DataKind kind) { 509916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (kind.fieldList == null) return false; 510916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 511916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov for (EditField field : kind.fieldList) { 512916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov final String value1 = values1.getAsString(field.column); 513916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov final String value2 = values2.getAsString(field.column); 514916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (!TextUtils.equals(value1, value2)) { 515916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return false; 516916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 517916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 518916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 519916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return true; 520916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 521916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 522916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov /** 523851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu * Parse the given {@link Bundle} into the given {@link RawContactDelta} state, 524d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * assuming the extras defined through {@link Intents}. 525d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey */ 526851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu public static void parseExtras(Context context, AccountType accountType, RawContactDelta state, 527aad8848282f51d73ad308e9ad3ebcef592fa153fJeff Sharkey Bundle extras) { 528d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey if (extras == null || extras.size() == 0) { 529d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey // Bail early if no useful data 530d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey return; 531d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey } 532d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey 533c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov parseStructuredNameExtra(context, accountType, state, extras); 534c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov parseStructuredPostalExtra(accountType, state, extras); 535d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 536d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey { 537d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Phone 53869f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov final DataKind kind = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE); 539d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.PHONE_TYPE, Insert.PHONE, Phone.NUMBER); 540d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.SECONDARY_PHONE_TYPE, Insert.SECONDARY_PHONE, 541d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey Phone.NUMBER); 542d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.TERTIARY_PHONE_TYPE, Insert.TERTIARY_PHONE, 543d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey Phone.NUMBER); 544d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 545d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 546d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey { 547d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Email 54869f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov final DataKind kind = accountType.getKindForMimetype(Email.CONTENT_ITEM_TYPE); 549d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey parseExtras(state, kind, extras, Insert.EMAIL_TYPE, Insert.EMAIL, Email.DATA); 550d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.SECONDARY_EMAIL_TYPE, Insert.SECONDARY_EMAIL, 551d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey Email.DATA); 552d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.TERTIARY_EMAIL_TYPE, Insert.TERTIARY_EMAIL, 553d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey Email.DATA); 554d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 555d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 556d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey { 557d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Im 55869f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov final DataKind kind = accountType.getKindForMimetype(Im.CONTENT_ITEM_TYPE); 559e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey fixupLegacyImType(extras); 560d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey parseExtras(state, kind, extras, Insert.IM_PROTOCOL, Insert.IM_HANDLE, Im.DATA); 561d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 56216c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey 56316c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey // Organization 56416c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final boolean hasOrg = extras.containsKey(Insert.COMPANY) 56516c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey || extras.containsKey(Insert.JOB_TITLE); 56669f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov final DataKind kindOrg = accountType.getKindForMimetype(Organization.CONTENT_ITEM_TYPE); 567851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu if (hasOrg && RawContactModifier.canInsert(state, kindOrg)) { 568851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu final ValuesDelta child = RawContactModifier.insertChild(state, kindOrg); 56916c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey 57016c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final String company = extras.getString(Insert.COMPANY); 57116c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey if (ContactsUtils.isGraphic(company)) { 57216c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey child.put(Organization.COMPANY, company); 57316c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey } 57416c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey 57516c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final String title = extras.getString(Insert.JOB_TITLE); 57616c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey if (ContactsUtils.isGraphic(title)) { 57716c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey child.put(Organization.TITLE, title); 57816c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey } 57916c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey } 58016c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey 58116c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey // Notes 58216c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final boolean hasNotes = extras.containsKey(Insert.NOTES); 58369f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov final DataKind kindNotes = accountType.getKindForMimetype(Note.CONTENT_ITEM_TYPE); 584851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu if (hasNotes && RawContactModifier.canInsert(state, kindNotes)) { 585851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu final ValuesDelta child = RawContactModifier.insertChild(state, kindNotes); 58616c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey 58716c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey final String notes = extras.getString(Insert.NOTES); 58816c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey if (ContactsUtils.isGraphic(notes)) { 58916c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey child.put(Note.NOTE, notes); 59016c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey } 59116c364a557d619a0396ed35249478dcf2c1ffba5Jeff Sharkey } 5926a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov 5936a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov // Arbitrary additional data 594916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov ArrayList<ContentValues> values = extras.getParcelableArrayList(Insert.DATA); 595916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (values != null) { 59669f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov parseValues(state, accountType, values); 597916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 598916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 599916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 600c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov private static void parseStructuredNameExtra( 601851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu Context context, AccountType accountType, RawContactDelta state, Bundle extras) { 602c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov // StructuredName 603851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu RawContactModifier.ensureKindExists(state, accountType, StructuredName.CONTENT_ITEM_TYPE); 604c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov final ValuesDelta child = state.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE); 605c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov 606c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov final String name = extras.getString(Insert.NAME); 607c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (ContactsUtils.isGraphic(name)) { 608c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov final DataKind kind = accountType.getKindForMimetype(StructuredName.CONTENT_ITEM_TYPE); 609c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov boolean supportsDisplayName = false; 610c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (kind.fieldList != null) { 611c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov for (EditField field : kind.fieldList) { 612c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (StructuredName.DISPLAY_NAME.equals(field.column)) { 613c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov supportsDisplayName = true; 614c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov break; 615c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 616c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 617c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 618c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov 619c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (supportsDisplayName) { 620c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov child.put(StructuredName.DISPLAY_NAME, name); 621c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } else { 622c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov Uri uri = ContactsContract.AUTHORITY_URI.buildUpon() 623c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov .appendPath("complete_name") 624c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov .appendQueryParameter(StructuredName.DISPLAY_NAME, name) 625c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov .build(); 626c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov Cursor cursor = context.getContentResolver().query(uri, 627c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov new String[]{ 628c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov StructuredName.PREFIX, 629c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov StructuredName.GIVEN_NAME, 630c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov StructuredName.MIDDLE_NAME, 631c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov StructuredName.FAMILY_NAME, 632c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov StructuredName.SUFFIX, 633c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov }, null, null, null); 634c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov 635c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov try { 636c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (cursor.moveToFirst()) { 637c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov child.put(StructuredName.PREFIX, cursor.getString(0)); 638c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov child.put(StructuredName.GIVEN_NAME, cursor.getString(1)); 639c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov child.put(StructuredName.MIDDLE_NAME, cursor.getString(2)); 640c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov child.put(StructuredName.FAMILY_NAME, cursor.getString(3)); 641c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov child.put(StructuredName.SUFFIX, cursor.getString(4)); 642c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 643c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } finally { 644c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov cursor.close(); 645c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 646c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 647c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 648c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov 649c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov final String phoneticName = extras.getString(Insert.PHONETIC_NAME); 650c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (ContactsUtils.isGraphic(phoneticName)) { 651c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov child.put(StructuredName.PHONETIC_GIVEN_NAME, phoneticName); 652c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 653c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 654c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov 655c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov private static void parseStructuredPostalExtra( 656851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu AccountType accountType, RawContactDelta state, Bundle extras) { 657c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov // StructuredPostal 658c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov final DataKind kind = accountType.getKindForMimetype(StructuredPostal.CONTENT_ITEM_TYPE); 659c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov final ValuesDelta child = parseExtras(state, kind, extras, Insert.POSTAL_TYPE, 660c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov Insert.POSTAL, StructuredPostal.FORMATTED_ADDRESS); 661c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov String address = child == null ? null 662c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov : child.getAsString(StructuredPostal.FORMATTED_ADDRESS); 663c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (!TextUtils.isEmpty(address)) { 664c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov boolean supportsFormatted = false; 665c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (kind.fieldList != null) { 666c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov for (EditField field : kind.fieldList) { 667c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (StructuredPostal.FORMATTED_ADDRESS.equals(field.column)) { 668c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov supportsFormatted = true; 669c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov break; 670c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 671c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 672c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 673c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov 674c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (!supportsFormatted) { 675c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov child.put(StructuredPostal.STREET, address); 676c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov child.putNull(StructuredPostal.FORMATTED_ADDRESS); 677c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 678c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 679c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov } 680c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov 681916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov private static void parseValues( 682851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu RawContactDelta state, AccountType accountType, 683851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu ArrayList<ContentValues> dataValueList) { 684916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov for (ContentValues values : dataValueList) { 685916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov String mimeType = values.getAsString(Data.MIMETYPE); 686916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (TextUtils.isEmpty(mimeType)) { 687916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov Log.e(TAG, "Mimetype is required. Ignoring: " + values); 688916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov continue; 689916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 690916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 691916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov // Won't override the contact name 692916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) { 693916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov continue; 694916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 695916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 69669f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov DataKind kind = accountType.getKindForMimetype(mimeType); 697916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (kind == null) { 6982b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro Log.e(TAG, "Mimetype not supported for account type " 6992b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro + accountType.getAccountTypeAndDataSet() + ". Ignoring: " + values); 700916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov continue; 701916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 702916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 703916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov ValuesDelta entry = ValuesDelta.fromAfter(values); 704916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (isEmpty(entry, kind)) { 705916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov continue; 706916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 707916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 708916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov ArrayList<ValuesDelta> entries = state.getMimeEntries(mimeType); 709916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 71050a27b70581b0191995969c63edd1f6a3db3d1b7Makoto Onuki if ((kind.typeOverallMax != 1) || GroupMembership.CONTENT_ITEM_TYPE.equals(mimeType)) { 711916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov // Check for duplicates 712916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov boolean addEntry = true; 713916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov int count = 0; 714916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (entries != null && entries.size() > 0) { 715916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov for (ValuesDelta delta : entries) { 716916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (!delta.isDelete()) { 717916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (areEqual(delta, values, kind)) { 718916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov addEntry = false; 719916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov break; 720916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 721916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov count++; 722916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 723916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 724916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 725916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 726916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (kind.typeOverallMax != -1 && count >= kind.typeOverallMax) { 727916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov Log.e(TAG, "Mimetype allows at most " + kind.typeOverallMax 728916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov + " entries. Ignoring: " + values); 729916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov addEntry = false; 730916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 731916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 732916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (addEntry) { 733916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov addEntry = adjustType(entry, entries, kind); 734916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 735916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 736916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (addEntry) { 737916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov state.addEntry(entry); 738916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 739916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } else { 740916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov // Non-list entries should not be overridden 741916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov boolean addEntry = true; 742916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (entries != null && entries.size() > 0) { 743916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov for (ValuesDelta delta : entries) { 744916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (!delta.isDelete() && !isEmpty(delta, kind)) { 745916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov addEntry = false; 746916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov break; 747916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 748916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 749916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (addEntry) { 750916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov for (ValuesDelta delta : entries) { 751916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov delta.markDeleted(); 752916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 753916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 754916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 755916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 756916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (addEntry) { 757916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov addEntry = adjustType(entry, entries, kind); 758916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 759916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 760916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (addEntry) { 761916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov state.addEntry(entry); 762916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } else if (Note.CONTENT_ITEM_TYPE.equals(mimeType)){ 763916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov // Note is most likely to contain large amounts of text 764916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov // that we don't want to drop on the ground. 765916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov for (ValuesDelta delta : entries) { 766916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (!isEmpty(delta, kind)) { 767916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov delta.put(Note.NOTE, delta.getAsString(Note.NOTE) + "\n" 768916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov + values.getAsString(Note.NOTE)); 769916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov break; 770916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 771916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 772916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } else { 773916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov Log.e(TAG, "Will not override mimetype " + mimeType + ". Ignoring: " 774916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov + values); 775916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 776916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 777916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 778916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 779916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 780916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov /** 781916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov * Checks if the data kind allows addition of another entry (e.g. Exchange only 782916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov * supports two "work" phone numbers). If not, tries to switch to one of the 783916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov * unused types. If successful, returns true. 784916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov */ 785916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov private static boolean adjustType( 786916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov ValuesDelta entry, ArrayList<ValuesDelta> entries, DataKind kind) { 787916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (kind.typeColumn == null || kind.typeList == null || kind.typeList.size() == 0) { 788916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return true; 789916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 790916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 791916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov Integer typeInteger = entry.getAsInteger(kind.typeColumn); 792916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov int type = typeInteger != null ? typeInteger : kind.typeList.get(0).rawValue; 793916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 794916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (isTypeAllowed(type, entries, kind)) { 795916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov entry.put(kind.typeColumn, type); 796916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return true; 797916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 798916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 799916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov // Specified type is not allowed - choose the first available type that is allowed 800916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov int size = kind.typeList.size(); 801916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov for (int i = 0; i < size; i++) { 802916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov EditType editType = kind.typeList.get(i); 803916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (isTypeAllowed(editType.rawValue, entries, kind)) { 804916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov entry.put(kind.typeColumn, editType.rawValue); 805916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return true; 806916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 8076a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov } 808916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 809916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return false; 810916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 811916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 812916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov /** 813916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov * Checks if a new entry of the specified type can be added to the raw 814916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov * contact. For example, Exchange only supports two "work" phone numbers, so 815916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov * addition of a third would not be allowed. 816916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov */ 817916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov private static boolean isTypeAllowed(int type, ArrayList<ValuesDelta> entries, DataKind kind) { 818916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov int max = 0; 819916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov int size = kind.typeList.size(); 820916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov for (int i = 0; i < size; i++) { 821916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov EditType editType = kind.typeList.get(i); 822916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (editType.rawValue == type) { 823916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov max = editType.specificMax; 824916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov break; 825916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 826916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 827916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 828916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (max == 0) { 829916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov // This type is not allowed at all 830916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return false; 831916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 832916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 833916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov if (max == -1) { 834916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov // Unlimited instances of this type are allowed 835916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return true; 836916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 837916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov 838916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return getEntryCountByType(entries, kind.typeColumn, type) < max; 8396a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov } 8406a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov 841916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov /** 842916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov * Counts occurrences of the specified type in the supplied entry list. 8439629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng * 8449629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng * @return The count of occurrences of the type in the entry list. 0 if entries is 8459629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng * {@literal null} 846916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov */ 8479629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng private static int getEntryCountByType(ArrayList<ValuesDelta> entries, String typeColumn, 8489629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng int type) { 849916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov int count = 0; 8509629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng if (entries != null) { 8519629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng for (ValuesDelta entry : entries) { 8529629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng Integer typeInteger = entry.getAsInteger(typeColumn); 8539629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng if (typeInteger != null && typeInteger == type) { 8549629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng count++; 8559629ad2fa0de7c9cc4f6e8fc0131db09809073a2Chiao Cheng } 856916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov } 8576a91292251913b3c0c8add733326cc70eae4cf51Dmitri Plotnikov } 858916cf2661ada41a83556884d8f5b322d32ef452bDmitri Plotnikov return count; 859d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 860d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 861d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey /** 862e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey * Attempt to parse legacy {@link Insert#IM_PROTOCOL} values, replacing them 863e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey * with updated values. 864e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey */ 865c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov @SuppressWarnings("deprecation") 866e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey private static void fixupLegacyImType(Bundle bundle) { 867e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey final String encodedString = bundle.getString(Insert.IM_PROTOCOL); 868e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey if (encodedString == null) return; 869e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey 870e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey try { 871e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey final Object protocol = android.provider.Contacts.ContactMethods 872e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey .decodeImProtocol(encodedString); 873e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey if (protocol instanceof Integer) { 874e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey bundle.putInt(Insert.IM_PROTOCOL, (Integer)protocol); 875e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } else { 876e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey bundle.putString(Insert.IM_PROTOCOL, (String)protocol); 877e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } 878e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } catch (IllegalArgumentException e) { 879e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey // Ignore exception when legacy parser fails 880e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } 881e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey } 882e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey 883e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey /** 884d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * Parse a specific entry from the given {@link Bundle} and insert into the 885851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu * given {@link RawContactDelta}. Silently skips the insert when missing value 886d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * or no valid {@link EditType} found. 887d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * 888d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * @param typeExtra {@link Bundle} key that holds the incoming 889d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * {@link EditType#rawValue} value. 890d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * @param valueExtra {@link Bundle} key that holds the incoming value. 891d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey * @param valueColumn Column to write value into {@link ValuesDelta}. 892d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey */ 893851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu public static ValuesDelta parseExtras(RawContactDelta state, DataKind kind, Bundle extras, 894d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey String typeExtra, String valueExtra, String valueColumn) { 8957f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey final CharSequence value = extras.getCharSequence(valueExtra); 8967f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey 89769f9e6f0cd9b5401da55f251e9bd98e69643d7dfDmitri Plotnikov // Bail early if account type doesn't handle this MIME type 898c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (kind == null) return null; 899d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 900d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Bail when can't insert type, or value missing 901851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu final boolean canInsert = RawContactModifier.canInsert(state, kind); 902d9798aefc844dd9ce29da085cb8ab7e769f63e75Jeff Sharkey final boolean validValue = (value != null && TextUtils.isGraphic(value)); 903c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov if (!validValue || !canInsert) return null; 904d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 9056164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey // Find exact type when requested, otherwise best available type 9066164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey final boolean hasType = extras.containsKey(typeExtra); 9076164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey final int typeValue = extras.getInt(typeExtra, hasType ? BaseTypes.TYPE_CUSTOM 9086164461a80cf46ecc4b9d4de21a8c2662d5ac220Jeff Sharkey : Integer.MIN_VALUE); 909851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu final EditType editType = RawContactModifier.getBestValidType(state, kind, true, typeValue); 910d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 911d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Create data row and fill with value 912851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu final ValuesDelta child = RawContactModifier.insertChild(state, kind, editType); 9137f86847eddbec5dff4d87ac9243d839593582e42Jeff Sharkey child.put(valueColumn, value.toString()); 914d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey 915d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey if (editType != null && editType.customColumn != null) { 916d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey // Write down label when custom type picked 917e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey final String customType = extras.getString(typeExtra); 918e50d64d1e9eb321735c94a15b1a20a59a19cc421Jeff Sharkey child.put(editType.customColumn, customType); 919d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 920c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov 921c77f48947093d5f84b7df661a29b8b45d573cc10Dmitri Plotnikov return child; 922d046a039e41deab0635c3327cd40c3896e39acadJeff Sharkey } 923d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 924d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa /** 925d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa * Generic mime types with type support (e.g. TYPE_HOME). 926d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa * Here, "type support" means if the data kind has CommonColumns#TYPE or not. Data kinds which 927d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa * have their own migrate methods aren't listed here. 928d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa */ 929d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa private static final Set<String> sGenericMimeTypesWithTypeSupport = new HashSet<String>( 930d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Arrays.asList(Phone.CONTENT_ITEM_TYPE, 931d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Email.CONTENT_ITEM_TYPE, 932d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Im.CONTENT_ITEM_TYPE, 933d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Nickname.CONTENT_ITEM_TYPE, 934d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Website.CONTENT_ITEM_TYPE, 935d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Relation.CONTENT_ITEM_TYPE, 936d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa SipAddress.CONTENT_ITEM_TYPE)); 937d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa private static final Set<String> sGenericMimeTypesWithoutTypeSupport = new HashSet<String>( 938d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Arrays.asList(Organization.CONTENT_ITEM_TYPE, 939d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Note.CONTENT_ITEM_TYPE, 940d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Photo.CONTENT_ITEM_TYPE, 941d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa GroupMembership.CONTENT_ITEM_TYPE)); 942d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // CommonColumns.TYPE cannot be accessed as it is protected interface, so use 943d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Phone.TYPE instead. 944d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa private static final String COLUMN_FOR_TYPE = Phone.TYPE; 945d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa private static final String COLUMN_FOR_LABEL = Phone.LABEL; 946d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa private static final int TYPE_CUSTOM = Phone.TYPE_CUSTOM; 947d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 948d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa /** 949851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu * Migrates old RawContactDelta to newly created one with a new restriction supplied from 950d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa * newAccountType. 951d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa * 952d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa * This is only for account switch during account creation (which must be insert operation). 953d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa */ 954d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa public static void migrateStateForNewContact(Context context, 955851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu RawContactDelta oldState, RawContactDelta newState, 956d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa AccountType oldAccountType, AccountType newAccountType) { 957d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (newAccountType == oldAccountType) { 958d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Just copying all data in oldState isn't enough, but we can still rely on a lot of 959d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // shortcuts. 960d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (DataKind kind : newAccountType.getSortedDataKinds()) { 961d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final String mimeType = kind.mimeType; 962d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // The fields with short/long form capability must be treated properly. 963d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) { 964d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa migrateStructuredName(context, oldState, newState, kind); 965d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 966d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa List<ValuesDelta> entryList = oldState.getMimeEntries(mimeType); 967d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (entryList != null && !entryList.isEmpty()) { 968d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (ValuesDelta entry : entryList) { 969d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa ContentValues values = entry.getAfter(); 970d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (values != null) { 971d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa newState.addEntry(ValuesDelta.fromAfter(values)); 972d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 973d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 974d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 975d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 976d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 977d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 978d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Migrate data supported by the new account type. 979d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // All the other data inside oldState are silently dropped. 980d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (DataKind kind : newAccountType.getSortedDataKinds()) { 9812b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann if (!kind.editable) continue; 982d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final String mimeType = kind.mimeType; 983d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (DataKind.PSEUDO_MIME_TYPE_DISPLAY_NAME.equals(mimeType) 984d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa || DataKind.PSEUDO_MIME_TYPE_PHONETIC_NAME.equals(mimeType)) { 9852b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann // Ignore pseudo data. 986d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa continue; 987d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else if (StructuredName.CONTENT_ITEM_TYPE.equals(mimeType)) { 988d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa migrateStructuredName(context, oldState, newState, kind); 989d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else if (StructuredPostal.CONTENT_ITEM_TYPE.equals(mimeType)) { 990d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa migratePostal(oldState, newState, kind); 991d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else if (Event.CONTENT_ITEM_TYPE.equals(mimeType)) { 992d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa migrateEvent(oldState, newState, kind, null /* default Year */); 993d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else if (sGenericMimeTypesWithoutTypeSupport.contains(mimeType)) { 994d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa migrateGenericWithoutTypeColumn(oldState, newState, kind); 9952b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann } else if (sGenericMimeTypesWithTypeSupport.contains(mimeType)) { 996d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa migrateGenericWithTypeColumn(oldState, newState, kind); 9972b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann } else { 9982b3ee0f34801444c7d775ce747125459f69712bbDaniel Lehmann throw new IllegalStateException("Unexpected editable mime-type: " + mimeType); 999d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1000d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1001d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1002d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1003d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1004d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa /** 1005d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa * Checks {@link DataKind#isList} and {@link DataKind#typeOverallMax}, and restricts 1006d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa * the number of entries (ValuesDelta) inside newState. 1007d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa */ 1008851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu private static ArrayList<ValuesDelta> ensureEntryMaxSize(RawContactDelta newState, 1009851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu DataKind kind, ArrayList<ValuesDelta> mimeEntries) { 1010d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (mimeEntries == null) { 1011d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa return null; 1012d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1013d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 101450a27b70581b0191995969c63edd1f6a3db3d1b7Makoto Onuki final int typeOverallMax = kind.typeOverallMax; 1015d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (typeOverallMax >= 0 && (mimeEntries.size() > typeOverallMax)) { 1016d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa ArrayList<ValuesDelta> newMimeEntries = new ArrayList<ValuesDelta>(typeOverallMax); 1017d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (int i = 0; i < typeOverallMax; i++) { 1018d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa newMimeEntries.add(mimeEntries.get(i)); 1019d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1020d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa mimeEntries = newMimeEntries; 1021d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1022d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa return mimeEntries; 1023d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1024d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1025d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa /** @hide Public only for testing. */ 1026d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa public static void migrateStructuredName( 1027851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu Context context, RawContactDelta oldState, RawContactDelta newState, 1028851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu DataKind newDataKind) { 1029d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final ContentValues values = 1030d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa oldState.getPrimaryEntry(StructuredName.CONTENT_ITEM_TYPE).getAfter(); 1031d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (values == null) { 1032d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa return; 1033d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1034d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1035d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa boolean supportDisplayName = false; 1036d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa boolean supportPhoneticFullName = false; 1037d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa boolean supportPhoneticFamilyName = false; 1038d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa boolean supportPhoneticMiddleName = false; 1039d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa boolean supportPhoneticGivenName = false; 1040d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (EditField editField : newDataKind.fieldList) { 1041d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (StructuredName.DISPLAY_NAME.equals(editField.column)) { 1042d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa supportDisplayName = true; 1043d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1044d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (DataKind.PSEUDO_COLUMN_PHONETIC_NAME.equals(editField.column)) { 1045d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa supportPhoneticFullName = true; 1046d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1047d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (StructuredName.PHONETIC_FAMILY_NAME.equals(editField.column)) { 1048d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa supportPhoneticFamilyName = true; 1049d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1050d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (StructuredName.PHONETIC_MIDDLE_NAME.equals(editField.column)) { 1051d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa supportPhoneticMiddleName = true; 1052d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1053d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (StructuredName.PHONETIC_GIVEN_NAME.equals(editField.column)) { 1054d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa supportPhoneticGivenName = true; 1055d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1056d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1057d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1058d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // DISPLAY_NAME <-> PREFIX, GIVEN_NAME, MIDDLE_NAME, FAMILY_NAME, SUFFIX 1059d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final String displayName = values.getAsString(StructuredName.DISPLAY_NAME); 1060d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!TextUtils.isEmpty(displayName)) { 1061d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!supportDisplayName) { 1062d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Old data has a display name, while the new account doesn't allow it. 1063da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro NameConverter.displayNameToStructuredName(context, displayName, values); 1064d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1065d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // We don't want to migrate unseen data which may confuse users after the creation. 1066d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredName.DISPLAY_NAME); 1067d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1068d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1069d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (supportDisplayName) { 1070d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Old data does not have display name, while the new account requires it. 1071d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(StructuredName.DISPLAY_NAME, 1072da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro NameConverter.structuredNameToDisplayName(context, values)); 1073da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro for (String field : NameConverter.STRUCTURED_NAME_FIELDS) { 1074da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro values.remove(field); 1075da5bf1cf60beef3de5e651a569fa544293683926Dave Santoro } 1076d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1077d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1078d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1079d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Phonetic (full) name <-> PHONETIC_FAMILY_NAME, PHONETIC_MIDDLE_NAME, PHONETIC_GIVEN_NAME 1080d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final String phoneticFullName = values.getAsString(DataKind.PSEUDO_COLUMN_PHONETIC_NAME); 1081d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!TextUtils.isEmpty(phoneticFullName)) { 1082d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!supportPhoneticFullName) { 1083d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Old data has a phonetic (full) name, while the new account doesn't allow it. 1084851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu final StructuredNameDataItem tmpItem = 1085d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa PhoneticNameEditorView.parsePhoneticName(phoneticFullName, null); 1086d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(DataKind.PSEUDO_COLUMN_PHONETIC_NAME); 1087d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (supportPhoneticFamilyName) { 1088d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(StructuredName.PHONETIC_FAMILY_NAME, 1089851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu tmpItem.getPhoneticFamilyName()); 1090d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1091d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredName.PHONETIC_FAMILY_NAME); 1092d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1093d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (supportPhoneticMiddleName) { 1094d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(StructuredName.PHONETIC_MIDDLE_NAME, 1095851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu tmpItem.getPhoneticMiddleName()); 1096d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1097d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredName.PHONETIC_MIDDLE_NAME); 1098d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1099d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (supportPhoneticGivenName) { 1100d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(StructuredName.PHONETIC_GIVEN_NAME, 1101851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu tmpItem.getPhoneticGivenName()); 1102d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1103d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredName.PHONETIC_GIVEN_NAME); 1104d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1105d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1106d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1107d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (supportPhoneticFullName) { 1108d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Old data does not have a phonetic (full) name, while the new account requires it. 1109d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(DataKind.PSEUDO_COLUMN_PHONETIC_NAME, 1110d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa PhoneticNameEditorView.buildPhoneticName( 1111d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredName.PHONETIC_FAMILY_NAME), 1112d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredName.PHONETIC_MIDDLE_NAME), 1113d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredName.PHONETIC_GIVEN_NAME))); 1114d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1115d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!supportPhoneticFamilyName) { 1116d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredName.PHONETIC_FAMILY_NAME); 1117d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1118d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!supportPhoneticMiddleName) { 1119d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredName.PHONETIC_MIDDLE_NAME); 1120d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1121d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!supportPhoneticGivenName) { 1122d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredName.PHONETIC_GIVEN_NAME); 1123d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1124d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1125d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1126d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa newState.addEntry(ValuesDelta.fromAfter(values)); 1127d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1128d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1129d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa /** @hide Public only for testing. */ 1130851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu public static void migratePostal(RawContactDelta oldState, RawContactDelta newState, 1131d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa DataKind newDataKind) { 1132d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind, 1133d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa oldState.getMimeEntries(StructuredPostal.CONTENT_ITEM_TYPE)); 1134d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (mimeEntries == null || mimeEntries.isEmpty()) { 1135d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa return; 1136d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1137d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1138d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa boolean supportFormattedAddress = false; 1139d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa boolean supportStreet = false; 1140d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final String firstColumn = newDataKind.fieldList.get(0).column; 1141d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (EditField editField : newDataKind.fieldList) { 1142d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (StructuredPostal.FORMATTED_ADDRESS.equals(editField.column)) { 1143d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa supportFormattedAddress = true; 1144d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1145d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (StructuredPostal.STREET.equals(editField.column)) { 1146d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa supportStreet = true; 1147d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1148d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1149d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1150d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final Set<Integer> supportedTypes = new HashSet<Integer>(); 1151d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (newDataKind.typeList != null && !newDataKind.typeList.isEmpty()) { 1152d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (EditType editType : newDataKind.typeList) { 1153d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa supportedTypes.add(editType.rawValue); 1154d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1155d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1156d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1157d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (ValuesDelta entry : mimeEntries) { 1158d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final ContentValues values = entry.getAfter(); 1159d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (values == null) { 1160d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa continue; 1161d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1162d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final Integer oldType = values.getAsInteger(StructuredPostal.TYPE); 1163d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!supportedTypes.contains(oldType)) { 1164d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa int defaultType; 1165d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (newDataKind.defaultValues != null) { 1166d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa defaultType = newDataKind.defaultValues.getAsInteger(StructuredPostal.TYPE); 1167d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1168d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa defaultType = newDataKind.typeList.get(0).rawValue; 1169d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1170d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(StructuredPostal.TYPE, defaultType); 1171d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (oldType != null && oldType == StructuredPostal.TYPE_CUSTOM) { 1172d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredPostal.LABEL); 1173d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1174d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1175d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1176d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final String formattedAddress = values.getAsString(StructuredPostal.FORMATTED_ADDRESS); 1177d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!TextUtils.isEmpty(formattedAddress)) { 1178d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!supportFormattedAddress) { 1179d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Old data has a formatted address, while the new account doesn't allow it. 1180d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredPostal.FORMATTED_ADDRESS); 1181d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1182d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Unlike StructuredName we don't have logic to split it, so first 1183d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // try to use street field and. If the new account doesn't have one, 1184d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // then select first one anyway. 1185d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (supportStreet) { 1186d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(StructuredPostal.STREET, formattedAddress); 1187d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1188d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(firstColumn, formattedAddress); 1189d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1190d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1191d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1192d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (supportFormattedAddress) { 1193d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Old data does not have formatted address, while the new account requires it. 1194d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Unlike StructuredName we don't have logic to join multiple address values. 1195d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Use poor join heuristics for now. 1196d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa String[] structuredData; 1197d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final boolean useJapaneseOrder = 1198d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Locale.JAPANESE.getLanguage().equals(Locale.getDefault().getLanguage()); 1199d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (useJapaneseOrder) { 1200d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa structuredData = new String[] { 1201d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.COUNTRY), 1202d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.POSTCODE), 1203d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.REGION), 1204d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.CITY), 1205d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.NEIGHBORHOOD), 1206d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.STREET), 1207d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.POBOX) }; 1208d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1209d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa structuredData = new String[] { 1210d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.POBOX), 1211d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.STREET), 1212d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.NEIGHBORHOOD), 1213d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.CITY), 1214d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.REGION), 1215d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.POSTCODE), 1216d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.getAsString(StructuredPostal.COUNTRY) }; 1217d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1218d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final StringBuilder builder = new StringBuilder(); 1219d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (String elem : structuredData) { 1220d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!TextUtils.isEmpty(elem)) { 1221d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa builder.append(elem + "\n"); 1222d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1223d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1224d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(StructuredPostal.FORMATTED_ADDRESS, builder.toString()); 1225d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1226d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredPostal.POBOX); 1227d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredPostal.STREET); 1228d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredPostal.NEIGHBORHOOD); 1229d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredPostal.CITY); 1230d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredPostal.REGION); 1231d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredPostal.POSTCODE); 1232d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(StructuredPostal.COUNTRY); 1233d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1234d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1235d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1236d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa newState.addEntry(ValuesDelta.fromAfter(values)); 1237d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1238d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1239d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1240d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa /** @hide Public only for testing. */ 1241851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu public static void migrateEvent(RawContactDelta oldState, RawContactDelta newState, 1242d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa DataKind newDataKind, Integer defaultYear) { 1243d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind, 1244d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa oldState.getMimeEntries(Event.CONTENT_ITEM_TYPE)); 1245d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (mimeEntries == null || mimeEntries.isEmpty()) { 1246d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa return; 1247d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1248d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 12497b1255eab0729fd43a8aae9933bd79afa50a53a9Daniel Lehmann final SparseArray<EventEditType> allowedTypes = new SparseArray<EventEditType>(); 1250d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (EditType editType : newDataKind.typeList) { 1251d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa allowedTypes.put(editType.rawValue, (EventEditType) editType); 1252d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1253d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (ValuesDelta entry : mimeEntries) { 1254d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final ContentValues values = entry.getAfter(); 1255d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (values == null) { 1256d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa continue; 1257d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1258d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final String dateString = values.getAsString(Event.START_DATE); 1259d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final Integer type = values.getAsInteger(Event.TYPE); 12607b1255eab0729fd43a8aae9933bd79afa50a53a9Daniel Lehmann if (type != null && (allowedTypes.indexOfKey(type) >= 0) 12617b1255eab0729fd43a8aae9933bd79afa50a53a9Daniel Lehmann && !TextUtils.isEmpty(dateString)) { 1262d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa EventEditType suitableType = allowedTypes.get(type); 1263d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1264d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final ParsePosition position = new ParsePosition(0); 1265d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa boolean yearOptional = false; 1266d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Date date = DateUtils.DATE_AND_TIME_FORMAT.parse(dateString, position); 1267d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (date == null) { 1268d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa yearOptional = true; 1269d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa date = DateUtils.NO_YEAR_DATE_FORMAT.parse(dateString, position); 1270d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1271d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (date != null) { 1272d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (yearOptional && !suitableType.isYearOptional()) { 1273d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // The new EditType doesn't allow optional year. Supply default. 1274d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final Calendar calendar = Calendar.getInstance(DateUtils.UTC_TIMEZONE, 1275d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Locale.US); 1276d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (defaultYear == null) { 1277d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa defaultYear = calendar.get(Calendar.YEAR); 1278d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1279d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa calendar.setTime(date); 1280d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final int month = calendar.get(Calendar.MONTH); 1281d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final int day = calendar.get(Calendar.DAY_OF_MONTH); 1282d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Exchange requires 8:00 for birthdays 1283d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa calendar.set(defaultYear, month, day, 1284d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa EventFieldEditorView.getDefaultHourForBirthday(), 0, 0); 1285d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(Event.START_DATE, 1286d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa DateUtils.FULL_DATE_FORMAT.format(calendar.getTime())); 1287d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1288d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1289d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa newState.addEntry(ValuesDelta.fromAfter(values)); 1290d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1291d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Just drop it. 1292d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1293d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1294d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1295d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1296d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa /** @hide Public only for testing. */ 1297d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa public static void migrateGenericWithoutTypeColumn( 1298851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu RawContactDelta oldState, RawContactDelta newState, DataKind newDataKind) { 1299d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final ArrayList<ValuesDelta> mimeEntries = ensureEntryMaxSize(newState, newDataKind, 1300d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa oldState.getMimeEntries(newDataKind.mimeType)); 1301d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (mimeEntries == null || mimeEntries.isEmpty()) { 1302d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa return; 1303d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1304d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1305d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (ValuesDelta entry : mimeEntries) { 1306d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa ContentValues values = entry.getAfter(); 1307d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (values != null) { 1308d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa newState.addEntry(ValuesDelta.fromAfter(values)); 1309d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1310d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1311d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1312d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1313d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa /** @hide Public only for testing. */ 1314d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa public static void migrateGenericWithTypeColumn( 1315851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu RawContactDelta oldState, RawContactDelta newState, DataKind newDataKind) { 1316d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final ArrayList<ValuesDelta> mimeEntries = oldState.getMimeEntries(newDataKind.mimeType); 1317d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (mimeEntries == null || mimeEntries.isEmpty()) { 1318d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa return; 1319d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1320d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1321d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Note that type specified with the old account may be invalid with the new account, while 1322d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // we want to preserve its data as much as possible. e.g. if a user typed a phone number 1323d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // with a type which is valid with an old account but not with a new account, the user 1324d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // probably wants to have the number with default type, rather than seeing complete data 1325d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // loss. 1326d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // 1327d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Specifically, this method works as follows: 1328d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // 1. detect defaultType 1329d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // 2. prepare constants & variables for iteration 1330d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // 3. iterate over mimeEntries: 1331d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // 3.1 stop iteration if total number of mimeEntries reached typeOverallMax specified in 1332d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // DataKind 1333d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // 3.2 replace unallowed types with defaultType 1334d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // 3.3 check if the number of entries is below specificMax specified in AccountType 1335d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1336d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Here, defaultType can be supplied in two ways 1337d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // - via kind.defaultValues 1338d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // - via kind.typeList.get(0).rawValue 1339d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Integer defaultType = null; 1340d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (newDataKind.defaultValues != null) { 1341d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa defaultType = newDataKind.defaultValues.getAsInteger(COLUMN_FOR_TYPE); 1342d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1343d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final Set<Integer> allowedTypes = new HashSet<Integer>(); 1344d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // key: type, value: the number of entries allowed for the type (specificMax) 13457b1255eab0729fd43a8aae9933bd79afa50a53a9Daniel Lehmann final SparseIntArray typeSpecificMaxMap = new SparseIntArray(); 1346d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (defaultType != null) { 1347d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa allowedTypes.add(defaultType); 1348d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa typeSpecificMaxMap.put(defaultType, -1); 1349d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1350d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Note: typeList may be used in different purposes when defaultValues are specified. 1351d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // Especially in IM, typeList contains available protocols (e.g. PROTOCOL_GOOGLE_TALK) 1352d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // instead of "types" which we want to treate here (e.g. TYPE_HOME). So we don't add 1353d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // anything other than defaultType into allowedTypes and typeSpecificMapMax. 1354d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!Im.CONTENT_ITEM_TYPE.equals(newDataKind.mimeType) && 1355d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa newDataKind.typeList != null && !newDataKind.typeList.isEmpty()) { 1356d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (EditType editType : newDataKind.typeList) { 1357d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa allowedTypes.add(editType.rawValue); 1358d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa typeSpecificMaxMap.put(editType.rawValue, editType.specificMax); 1359d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1360d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (defaultType == null) { 1361d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa defaultType = newDataKind.typeList.get(0).rawValue; 1362d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1363d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1364d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1365d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (defaultType == null) { 1366d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa Log.w(TAG, "Default type isn't available for mimetype " + newDataKind.mimeType); 1367d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1368d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 136950a27b70581b0191995969c63edd1f6a3db3d1b7Makoto Onuki final int typeOverallMax = newDataKind.typeOverallMax; 1370d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1371d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // key: type, value: the number of current entries. 13727b1255eab0729fd43a8aae9933bd79afa50a53a9Daniel Lehmann final SparseIntArray currentEntryCount = new SparseIntArray(); 1373d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa int totalCount = 0; 1374d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1375d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa for (ValuesDelta entry : mimeEntries) { 1376d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (typeOverallMax != -1 && totalCount >= typeOverallMax) { 1377d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa break; 1378d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1379d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1380d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final ContentValues values = entry.getAfter(); 1381d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (values == null) { 1382d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa continue; 1383d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1384d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa 1385d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final Integer oldType = entry.getAsInteger(COLUMN_FOR_TYPE); 1386d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa final Integer typeForNewAccount; 1387d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (!allowedTypes.contains(oldType)) { 1388d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa // The new account doesn't support the type. 1389d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (defaultType != null) { 1390d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa typeForNewAccount = defaultType.intValue(); 1391d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.put(COLUMN_FOR_TYPE, defaultType.intValue()); 1392d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (oldType != null && oldType == TYPE_CUSTOM) { 1393d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(COLUMN_FOR_LABEL); 1394d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1395d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1396d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa typeForNewAccount = null; 1397d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa values.remove(COLUMN_FOR_TYPE); 1398d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1399d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } else { 1400d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa typeForNewAccount = oldType; 1401d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1402d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (typeForNewAccount != null) { 14037b1255eab0729fd43a8aae9933bd79afa50a53a9Daniel Lehmann final int specificMax = typeSpecificMaxMap.get(typeForNewAccount, 0); 1404d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (specificMax >= 0) { 14057b1255eab0729fd43a8aae9933bd79afa50a53a9Daniel Lehmann final int currentCount = currentEntryCount.get(typeForNewAccount, 0); 1406d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa if (currentCount >= specificMax) { 1407d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa continue; 1408d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1409d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa currentEntryCount.put(typeForNewAccount, currentCount + 1); 1410d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1411d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1412d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa newState.addEntry(ValuesDelta.fromAfter(values)); 1413d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa totalCount++; 1414d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 1415d37a891cb44cd17083c06d7be693c469403d16dfDaisuke Miyakawa } 14162ae666ec99ae9318936a9326e5243987e4e1c586Jeff Sharkey} 1417