15ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee/* 25ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Copyright (C) 2009 The Android Open Source Project 35ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * 45ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Licensed under the Apache License, Version 2.0 (the "License"); 55ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * you may not use this file except in compliance with the License. 65ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * You may obtain a copy of the License at 75ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * 85ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * http://www.apache.org/licenses/LICENSE-2.0 95ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * 105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Unless required by applicable law or agreed to in writing, software 115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * distributed under the License is distributed on an "AS IS" BASIS, 125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * See the License for the specific language governing permissions and 145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * limitations under the License. 155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leepackage com.android.contacts.common.model; 185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.content.ContentProviderOperation; 205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.content.ContentProviderOperation.Builder; 215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.content.ContentResolver; 225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.content.Context; 235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.content.Entity; 245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.content.EntityIterator; 255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.net.Uri; 265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.os.Parcel; 275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.os.Parcelable; 285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.AggregationExceptions; 295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.Contacts; 305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.provider.ContactsContract.RawContacts; 315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport android.util.Log; 325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.android.contacts.common.model.ValuesDelta; 345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport com.google.common.collect.Lists; 355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport java.util.ArrayList; 375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport java.util.Arrays; 385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leeimport java.util.Iterator; 395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee/** 415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Container for multiple {@link RawContactDelta} objects, usually when editing 425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * together as an entire aggregate. Provides convenience methods for parceling 435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * and applying another {@link RawContactDeltaList} over it. 445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Leepublic class RawContactDeltaList extends ArrayList<RawContactDelta> implements Parcelable { 465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee private static final String TAG = RawContactDeltaList.class.getSimpleName(); 475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE); 485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee private boolean mSplitRawContacts; 505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee private long[] mJoinWithRawContactIds; 515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public RawContactDeltaList() { 535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** 565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Create an {@link RawContactDeltaList} based on {@link Contacts} specified by the 575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * given query parameters. This closes the {@link EntityIterator} when 585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * finished, so it doesn't subscribe to updates. 595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public static RawContactDeltaList fromQuery(Uri entityUri, ContentResolver resolver, 615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee String selection, String[] selectionArgs, String sortOrder) { 625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final EntityIterator iterator = RawContacts.newEntityIterator( 635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee resolver.query(entityUri, null, selection, selectionArgs, sortOrder)); 645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee try { 655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return fromIterator(iterator); 665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } finally { 675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee iterator.close(); 685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** 725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Create an {@link RawContactDeltaList} that contains the entities of the Iterator as before 735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * values. This function can be passed an iterator of Entity objects or an iterator of 745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * RawContact objects. 755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public static RawContactDeltaList fromIterator(Iterator<?> iterator) { 775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final RawContactDeltaList state = new RawContactDeltaList(); 785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee state.addAll(iterator); 795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return state; 805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public void addAll(Iterator<?> iterator) { 835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee // Perform background query to pull contact details 845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee while (iterator.hasNext()) { 855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee // Read all contacts into local deltas to prepare for edits 865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee Object nextObject = iterator.next(); 875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final RawContact before = nextObject instanceof Entity 885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee ? RawContact.createFrom((Entity) nextObject) 895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee : (RawContact) nextObject; 905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final RawContactDelta rawContactDelta = RawContactDelta.fromBefore(before); 915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee add(rawContactDelta); 925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** 965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Merge the "after" values from the given {@link RawContactDeltaList}, discarding any 975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * previous "after" states. This is typically used when re-parenting user 985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * edits onto an updated {@link RawContactDeltaList}. 995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 1005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public static RawContactDeltaList mergeAfter(RawContactDeltaList local, 1015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee RawContactDeltaList remote) { 1025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (local == null) local = new RawContactDeltaList(); 1035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee // For each entity in the remote set, try matching over existing 1055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee for (RawContactDelta remoteEntity : remote) { 1065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final Long rawContactId = remoteEntity.getValues().getId(); 1075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee // Find or create local match and merge 1095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final RawContactDelta localEntity = local.getByRawContactId(rawContactId); 1105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final RawContactDelta merged = RawContactDelta.mergeAfter(localEntity, remoteEntity); 1115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (localEntity == null && merged != null) { 1135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee // No local entry before, so insert 1145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee local.add(merged); 1155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 1165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 1175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return local; 1195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 1205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** 1225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Build a list of {@link ContentProviderOperation} that will transform all 1235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * the "before" {@link Entity} states into the modified state which all 1245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * {@link RawContactDelta} objects represent. This method specifically creates 1255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * any {@link AggregationExceptions} rules needed to groups edits together. 1265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 1275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public ArrayList<ContentProviderOperation> buildDiff() { 1285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (VERBOSE_LOGGING) { 1295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee Log.v(TAG, "buildDiff: list=" + toString()); 1305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 1315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); 1325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final long rawContactId = this.findRawContactId(); 1345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee int firstInsertRow = -1; 1355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee // First pass enforces versions remain consistent 1375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee for (RawContactDelta delta : this) { 1385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee delta.buildAssert(diff); 1395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 1405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final int assertMark = diff.size(); 1425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee int backRefs[] = new int[size()]; 1435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee int rawContactIndex = 0; 1455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee // Second pass builds actual operations 1475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee for (RawContactDelta delta : this) { 1485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final int firstBatch = diff.size(); 1495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final boolean isInsert = delta.isContactInsert(); 1505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee backRefs[rawContactIndex++] = isInsert ? firstBatch : -1; 1515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee delta.buildDiff(diff); 1535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee // If the user chose to join with some other existing raw contact(s) at save time, 1555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee // add aggregation exceptions for all those raw contacts. 1565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (mJoinWithRawContactIds != null) { 1575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee for (Long joinedRawContactId : mJoinWithRawContactIds) { 1585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final Builder builder = beginKeepTogether(); 1595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, joinedRawContactId); 1605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (rawContactId != -1) { 1615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee builder.withValue(AggregationExceptions.RAW_CONTACT_ID2, rawContactId); 1625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } else { 1635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee builder.withValueBackReference( 1645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee AggregationExceptions.RAW_CONTACT_ID2, firstBatch); 1655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 1665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee diff.add(builder.build()); 1675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 1685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 1695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee // Only create rules for inserts 1715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (!isInsert) continue; 1725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee // If we are going to split all contacts, there is no point in first combining them 1745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (mSplitRawContacts) continue; 1755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (rawContactId != -1) { 1775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee // Has existing contact, so bind to it strongly 1785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final Builder builder = beginKeepTogether(); 1795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, rawContactId); 1805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID2, firstBatch); 1815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee diff.add(builder.build()); 1825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } else if (firstInsertRow == -1) { 1845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee // First insert case, so record row 1855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee firstInsertRow = firstBatch; 1865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } else { 1885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee // Additional insert case, so point at first insert 1895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final Builder builder = beginKeepTogether(); 1905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID1, 1915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee firstInsertRow); 1925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID2, firstBatch); 1935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee diff.add(builder.build()); 1945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 1955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 1965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 1975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (mSplitRawContacts) { 1985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee buildSplitContactDiff(diff, backRefs); 1995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 2005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 2015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee // No real changes if only left with asserts 2025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (diff.size() == assertMark) { 2035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee diff.clear(); 2045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 2055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (VERBOSE_LOGGING) { 2065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee Log.v(TAG, "buildDiff: ops=" + diffToString(diff)); 2075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 2085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return diff; 2095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 2105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 2115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee private static String diffToString(ArrayList<ContentProviderOperation> ops) { 2125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee StringBuilder sb = new StringBuilder(); 2135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee sb.append("[\n"); 2145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee for (ContentProviderOperation op : ops) { 2155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee sb.append(op.toString()); 2165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee sb.append(",\n"); 2175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 2185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee sb.append("]\n"); 2195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return sb.toString(); 2205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 2215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 2225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** 2235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Start building a {@link ContentProviderOperation} that will keep two 2245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * {@link RawContacts} together. 2255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 2265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee protected Builder beginKeepTogether() { 2275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final Builder builder = ContentProviderOperation 2285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee .newUpdate(AggregationExceptions.CONTENT_URI); 2295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee builder.withValue(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_TOGETHER); 2305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return builder; 2315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 2325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 2335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** 2345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Builds {@link AggregationExceptions} to split all constituent raw contacts into 2355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * separate contacts. 2365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 2375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee private void buildSplitContactDiff(final ArrayList<ContentProviderOperation> diff, 2385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee int[] backRefs) { 2395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee int count = size(); 2405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee for (int i = 0; i < count; i++) { 2415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee for (int j = 0; j < count; j++) { 2425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (i != j) { 2435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee buildSplitContactDiff(diff, i, j, backRefs); 2445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 2455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 2465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 2475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 2485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 2495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** 2505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Construct a {@link AggregationExceptions#TYPE_KEEP_SEPARATE}. 2515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 2525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee private void buildSplitContactDiff(ArrayList<ContentProviderOperation> diff, int index1, 2535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee int index2, int[] backRefs) { 2545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee Builder builder = 2555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee ContentProviderOperation.newUpdate(AggregationExceptions.CONTENT_URI); 2565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee builder.withValue(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_SEPARATE); 2575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 2585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee Long rawContactId1 = get(index1).getValues().getAsLong(RawContacts._ID); 2595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee int backRef1 = backRefs[index1]; 2605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (rawContactId1 != null && rawContactId1 >= 0) { 2615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1); 2625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } else if (backRef1 >= 0) { 2635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID1, backRef1); 2645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } else { 2655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return; 2665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 2675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 2685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee Long rawContactId2 = get(index2).getValues().getAsLong(RawContacts._ID); 2695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee int backRef2 = backRefs[index2]; 2705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (rawContactId2 != null && rawContactId2 >= 0) { 2715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee builder.withValue(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2); 2725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } else if (backRef2 >= 0) { 2735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee builder.withValueBackReference(AggregationExceptions.RAW_CONTACT_ID2, backRef2); 2745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } else { 2755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return; 2765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 2775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 2785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee diff.add(builder.build()); 2795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 2805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 2815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** 2825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Search all contained {@link RawContactDelta} for the first one with an 2835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * existing {@link RawContacts#_ID} value. Usually used when creating 2845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * {@link AggregationExceptions} during an update. 2855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 2865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public long findRawContactId() { 2875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee for (RawContactDelta delta : this) { 2885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final Long rawContactId = delta.getValues().getAsLong(RawContacts._ID); 2895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (rawContactId != null && rawContactId >= 0) { 2905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return rawContactId; 2915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 2925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 2935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return -1; 2945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 2955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 2965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** 2975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Find {@link RawContacts#_ID} of the requested {@link RawContactDelta}. 2985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 2995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public Long getRawContactId(int index) { 3005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (index >= 0 && index < this.size()) { 3015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final RawContactDelta delta = this.get(index); 3025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final ValuesDelta values = delta.getValues(); 3035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (values.isVisible()) { 3045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return values.getAsLong(RawContacts._ID); 3055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 3065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 3075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return null; 3085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 3095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 3105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** 3115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Find the raw-contact (an {@link RawContactDelta}) with the specified ID. 3125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 3135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public RawContactDelta getByRawContactId(Long rawContactId) { 3145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final int index = this.indexOfRawContactId(rawContactId); 3155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return (index == -1) ? null : this.get(index); 3165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 3175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 3185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** 3195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Find index of given {@link RawContacts#_ID} when present. 3205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 3215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public int indexOfRawContactId(Long rawContactId) { 3225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (rawContactId == null) return -1; 3235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final int size = this.size(); 3245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee for (int i = 0; i < size; i++) { 3255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final Long currentId = getRawContactId(i); 3265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (rawContactId.equals(currentId)) { 3275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return i; 3285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 3295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 3305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return -1; 3315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 3325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 3335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** 3345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Return the index of the first RawContactDelta corresponding to a writable raw-contact, or -1. 3355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * */ 3365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public int indexOfFirstWritableRawContact(Context context) { 3375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee // Find the first writable entity. 3385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee int entityIndex = 0; 3395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee for (RawContactDelta delta : this) { 3405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (delta.getRawContactAccountType(context).areContactsWritable()) return entityIndex; 3415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee entityIndex++; 3425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 3435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return -1; 3445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 3455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 3465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** Return the first RawContactDelta corresponding to a writable raw-contact, or null. */ 3475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public RawContactDelta getFirstWritableRawContact(Context context) { 3485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final int index = indexOfFirstWritableRawContact(context); 3495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return (index == -1) ? null : get(index); 3505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 3515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 3525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public ValuesDelta getSuperPrimaryEntry(final String mimeType) { 3535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee ValuesDelta primary = null; 3545ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee ValuesDelta randomEntry = null; 3555ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee for (RawContactDelta delta : this) { 3565ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final ArrayList<ValuesDelta> mimeEntries = delta.getMimeEntries(mimeType); 3575ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (mimeEntries == null) return null; 3585ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 3595ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee for (ValuesDelta entry : mimeEntries) { 3605ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (entry.isSuperPrimary()) { 3615ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return entry; 3625ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } else if (primary == null && entry.isPrimary()) { 3635ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee primary = entry; 3645ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } else if (randomEntry == null) { 3655ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee randomEntry = entry; 3665ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 3675ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 3685ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 3695ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee // When no direct super primary, return something 3705ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee if (primary != null) { 3715ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return primary; 3725ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 3735ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return randomEntry; 3745ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 3755ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 3765ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** 3775ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee * Sets a flag that will split ("explode") the raw_contacts into seperate contacts 3785ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee */ 3795ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public void markRawContactsForSplitting() { 3805ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee mSplitRawContacts = true; 3815ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 3825ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 3835ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public boolean isMarkedForSplitting() { 3845ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return mSplitRawContacts; 3855ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 3865ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 3875ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public void setJoinWithRawContacts(long[] rawContactIds) { 3885ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee mJoinWithRawContactIds = rawContactIds; 3895ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 3905ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 3915ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public boolean isMarkedForJoining() { 3925ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return mJoinWithRawContactIds != null && mJoinWithRawContactIds.length > 0; 3935ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 3945ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 3955ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** {@inheritDoc} */ 3965ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee @Override 3975ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public int describeContents() { 3985ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee // Nothing special about this parcel 3995ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return 0; 4005ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 4015ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 4025ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee /** {@inheritDoc} */ 4035ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee @Override 4045ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public void writeToParcel(Parcel dest, int flags) { 4055ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final int size = this.size(); 4065ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee dest.writeInt(size); 4075ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee for (RawContactDelta delta : this) { 4085ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee dest.writeParcelable(delta, flags); 4095ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 4105ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee dest.writeLongArray(mJoinWithRawContactIds); 4115ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee dest.writeInt(mSplitRawContacts ? 1 : 0); 4125ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 4135ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 4145ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee @SuppressWarnings("unchecked") 4155ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public void readFromParcel(Parcel source) { 4165ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final ClassLoader loader = getClass().getClassLoader(); 4175ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final int size = source.readInt(); 4185ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee for (int i = 0; i < size; i++) { 4195ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee this.add(source.<RawContactDelta> readParcelable(loader)); 4205ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 4215ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee mJoinWithRawContactIds = source.createLongArray(); 4225ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee mSplitRawContacts = source.readInt() != 0; 4235ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 4245ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 4255ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public static final Parcelable.Creator<RawContactDeltaList> CREATOR = 4265ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee new Parcelable.Creator<RawContactDeltaList>() { 4275ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee @Override 4285ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public RawContactDeltaList createFromParcel(Parcel in) { 4295ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee final RawContactDeltaList state = new RawContactDeltaList(); 4305ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee state.readFromParcel(in); 4315ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return state; 4325ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 4335ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 4345ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee @Override 4355ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public RawContactDeltaList[] newArray(int size) { 4365ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return new RawContactDeltaList[size]; 4375ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 4385ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee }; 4395ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee 4405ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee @Override 4415ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee public String toString() { 4425ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee StringBuilder sb = new StringBuilder(); 4435ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee sb.append("("); 4445ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee sb.append("Split="); 4455ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee sb.append(mSplitRawContacts); 4465ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee sb.append(", Join=["); 4475ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee sb.append(Arrays.toString(mJoinWithRawContactIds)); 4485ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee sb.append("], Values="); 4495ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee sb.append(super.toString()); 4505ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee sb.append(")"); 4515ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee return sb.toString(); 4525ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee } 4535ade0bb1757b216ace2f50d2357409bf9876a07aYorke Lee} 454