1173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann/* 2173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann * Copyright (C) 2010 The Android Open Source Project 3173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann * 4173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann * Licensed under the Apache License, Version 2.0 (the "License"); 5173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann * you may not use this file except in compliance with the License. 6173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann * You may obtain a copy of the License at 7173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann * 8173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann * http://www.apache.org/licenses/LICENSE-2.0 9173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann * 10173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann * Unless required by applicable law or agreed to in writing, software 11173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann * distributed under the License is distributed on an "AS IS" BASIS, 12173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann * See the License for the specific language governing permissions and 14173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann * limitations under the License. 15173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann */ 16173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann 1718ffaa2561cc7dd2e3ef81737e6537931c0a9a11Dmitri Plotnikovpackage com.android.contacts; 18173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann 19615ed9c5e89f06a56ed6ad30244a9f6cb495e85cJay Shraunerimport static android.Manifest.permission.WRITE_CONTACTS; 203a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikovimport android.app.Activity; 21173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmannimport android.app.IntentService; 22173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmannimport android.content.ContentProviderOperation; 232b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikovimport android.content.ContentProviderOperation.Builder; 24caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikovimport android.content.ContentProviderResult; 25caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikovimport android.content.ContentResolver; 26e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikovimport android.content.ContentUris; 27caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikovimport android.content.ContentValues; 289d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikovimport android.content.Context; 29173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmannimport android.content.Intent; 302b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikovimport android.content.OperationApplicationException; 312b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikovimport android.database.Cursor; 32caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikovimport android.net.Uri; 33c42ea4eca298419484444a57bfc2da2c83e7adb7Daniel Lehmannimport android.os.Bundle; 34886d3d6de4a6f7202b1871f5af9944e8650413dfDmitri Plotnikovimport android.os.Handler; 35886d3d6de4a6f7202b1871f5af9944e8650413dfDmitri Plotnikovimport android.os.Looper; 36a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikovimport android.os.Parcelable; 372b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikovimport android.os.RemoteException; 38173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmannimport android.provider.ContactsContract; 392b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikovimport android.provider.ContactsContract.AggregationExceptions; 401ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikovimport android.provider.ContactsContract.CommonDataKinds.GroupMembership; 41548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwellimport android.provider.ContactsContract.CommonDataKinds.StructuredName; 429d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikovimport android.provider.ContactsContract.Contacts; 43caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikovimport android.provider.ContactsContract.Data; 44e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikovimport android.provider.ContactsContract.Groups; 45ead19c5eafee0ffb43b02a4ae75ac5244ad3f853Isaac Katzenelsonimport android.provider.ContactsContract.Profile; 46caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikovimport android.provider.ContactsContract.RawContacts; 47c90f95e63684363d10ffe5ef8f08f2159fb5bfc0Dave Santoroimport android.provider.ContactsContract.RawContactsEntity; 48173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmannimport android.util.Log; 492b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikovimport android.widget.Toast; 50173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann 51dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wangimport com.android.contacts.activities.ContactEditorBaseActivity; 5267addcce8b54636c9e38f18db3bd262861aa3040Wenyi Wangimport com.android.contacts.common.compat.CompatUtils; 53d7ca03e23948c3b2d5f97ec5598d8b50e3fc0b25Chiao Chengimport com.android.contacts.common.database.ContactUpdateUtils; 540d5588da244d0992c3ff8f25d0875fdf95a8c644Chiao Chengimport com.android.contacts.common.model.AccountTypeManager; 5567addcce8b54636c9e38f18db3bd262861aa3040Wenyi Wangimport com.android.contacts.common.model.CPOWrapper; 56cd321f65f1e50409812976380ad1f0fdb3fa35cbYorke Leeimport com.android.contacts.common.model.RawContactDelta; 57cd321f65f1e50409812976380ad1f0fdb3fa35cbYorke Leeimport com.android.contacts.common.model.RawContactDeltaList; 58cd321f65f1e50409812976380ad1f0fdb3fa35cbYorke Leeimport com.android.contacts.common.model.RawContactModifier; 59428f008513d1591cc08fcfe2cf0c9237fb313241Chiao Chengimport com.android.contacts.common.model.account.AccountWithDataSet; 60615ed9c5e89f06a56ed6ad30244a9f6cb495e85cJay Shraunerimport com.android.contacts.common.util.PermissionsUtil; 61aac0e66fb100b329d6010637998849048efadca9Wenyi Wangimport com.android.contacts.compat.PinnedPositionsCompat; 620ae4a9330924726e3e06f9de96e81566e4fea635Walter Jangimport com.android.contacts.activities.ContactEditorBaseActivity.ContactEditor.SaveMode; 63637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Leeimport com.android.contacts.util.ContactPhotoUtils; 64637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee 65e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.google.common.collect.Lists; 66e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.google.common.collect.Sets; 67e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Cheng 68c42ea4eca298419484444a57bfc2da2c83e7adb7Daniel Lehmannimport java.util.ArrayList; 69c42ea4eca298419484444a57bfc2da2c83e7adb7Daniel Lehmannimport java.util.HashSet; 70c42ea4eca298419484444a57bfc2da2c83e7adb7Daniel Lehmannimport java.util.List; 71c42ea4eca298419484444a57bfc2da2c83e7adb7Daniel Lehmannimport java.util.concurrent.CopyOnWriteArrayList; 72173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann 7318ffaa2561cc7dd2e3ef81737e6537931c0a9a11Dmitri Plotnikov/** 7418ffaa2561cc7dd2e3ef81737e6537931c0a9a11Dmitri Plotnikov * A service responsible for saving changes to the content provider. 7518ffaa2561cc7dd2e3ef81737e6537931c0a9a11Dmitri Plotnikov */ 76173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmannpublic class ContactSaveService extends IntentService { 77173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann private static final String TAG = "ContactSaveService"; 78173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann 79a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan /** Set to true in order to view logs on content provider operations */ 80a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan private static final boolean DEBUG = false; 81a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan 82caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov public static final String ACTION_NEW_RAW_CONTACT = "newRawContact"; 83caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov 84caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov public static final String EXTRA_ACCOUNT_NAME = "accountName"; 85caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov public static final String EXTRA_ACCOUNT_TYPE = "accountType"; 862b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro public static final String EXTRA_DATA_SET = "dataSet"; 87caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov public static final String EXTRA_CONTENT_VALUES = "contentValues"; 88caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov public static final String EXTRA_CALLBACK_INTENT = "callbackIntent"; 89caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov 90a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov public static final String ACTION_SAVE_CONTACT = "saveContact"; 91a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov public static final String EXTRA_CONTACT_STATE = "state"; 92a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov public static final String EXTRA_SAVE_MODE = "saveMode"; 93ead19c5eafee0ffb43b02a4ae75ac5244ad3f853Isaac Katzenelson public static final String EXTRA_SAVE_IS_PROFILE = "saveIsProfile"; 9436d24d7ede42a252c82c4aa783b2231c5e2eea79Dave Santoro public static final String EXTRA_SAVE_SUCCEEDED = "saveSucceeded"; 95e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus public static final String EXTRA_UPDATED_PHOTOS = "updatedPhotos"; 96173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann 971ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov public static final String ACTION_CREATE_GROUP = "createGroup"; 98e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov public static final String ACTION_RENAME_GROUP = "renameGroup"; 99e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov public static final String ACTION_DELETE_GROUP = "deleteGroup"; 1002d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan public static final String ACTION_UPDATE_GROUP = "updateGroup"; 101e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov public static final String EXTRA_GROUP_ID = "groupId"; 102e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov public static final String EXTRA_GROUP_LABEL = "groupLabel"; 1032d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan public static final String EXTRA_RAW_CONTACTS_TO_ADD = "rawContactsToAdd"; 1042d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan public static final String EXTRA_RAW_CONTACTS_TO_REMOVE = "rawContactsToRemove"; 105e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov 1069d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov public static final String ACTION_SET_STARRED = "setStarred"; 1077d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov public static final String ACTION_DELETE_CONTACT = "delete"; 108d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell public static final String ACTION_DELETE_MULTIPLE_CONTACTS = "deleteMultipleContacts"; 1099d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov public static final String EXTRA_CONTACT_URI = "contactUri"; 110d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell public static final String EXTRA_CONTACT_IDS = "contactIds"; 1119d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov public static final String EXTRA_STARRED_FLAG = "starred"; 1129d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov 1130f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann public static final String ACTION_SET_SUPER_PRIMARY = "setSuperPrimary"; 1140f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann public static final String ACTION_CLEAR_PRIMARY = "clearPrimary"; 1150f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann public static final String EXTRA_DATA_ID = "dataId"; 1160f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann 1172b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov public static final String ACTION_JOIN_CONTACTS = "joinContacts"; 118d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell public static final String ACTION_JOIN_SEVERAL_CONTACTS = "joinSeveralContacts"; 1192b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov public static final String EXTRA_CONTACT_ID1 = "contactId1"; 1202b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov public static final String EXTRA_CONTACT_ID2 = "contactId2"; 1212b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov 122683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson public static final String ACTION_SET_SEND_TO_VOICEMAIL = "sendToVoicemail"; 123683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson public static final String EXTRA_SEND_TO_VOICEMAIL_FLAG = "sendToVoicemailFlag"; 124683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson 125683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson public static final String ACTION_SET_RINGTONE = "setRingtone"; 126683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson public static final String EXTRA_CUSTOM_RINGTONE = "customRingtone"; 127683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson 128caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov private static final HashSet<String> ALLOWED_DATA_COLUMNS = Sets.newHashSet( 129caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov Data.MIMETYPE, 130caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov Data.IS_PRIMARY, 131caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov Data.DATA1, 132caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov Data.DATA2, 133caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov Data.DATA3, 134caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov Data.DATA4, 135caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov Data.DATA5, 136caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov Data.DATA6, 137caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov Data.DATA7, 138caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov Data.DATA8, 139caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov Data.DATA9, 140caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov Data.DATA10, 141caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov Data.DATA11, 142caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov Data.DATA12, 143caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov Data.DATA13, 144caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov Data.DATA14, 145caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov Data.DATA15 146caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov ); 147caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov 148a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov private static final int PERSIST_TRIES = 3; 149a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 1500653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang private static final int MAX_CONTACTS_PROVIDER_BATCH_SIZE = 499; 1510653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang 1523a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov public interface Listener { 1533a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov public void onServiceCompleted(Intent callbackIntent); 1543a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov } 1553a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov 156a831c0b539cdc120655856074d4621e8e60a843bHugo Hudson private static final CopyOnWriteArrayList<Listener> sListeners = 157a831c0b539cdc120655856074d4621e8e60a843bHugo Hudson new CopyOnWriteArrayList<Listener>(); 1583a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov 1593a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov private Handler mMainHandler; 1603a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov 161173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann public ContactSaveService() { 162173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann super(TAG); 163173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann setIntentRedelivery(true); 1643a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov mMainHandler = new Handler(Looper.getMainLooper()); 1653a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov } 1663a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov 1673a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov public static void registerListener(Listener listener) { 1683a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov if (!(listener instanceof Activity)) { 1693a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov throw new ClassCastException("Only activities can be registered to" 1703a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov + " receive callback from " + ContactSaveService.class.getName()); 1713a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov } 172a831c0b539cdc120655856074d4621e8e60a843bHugo Hudson sListeners.add(0, listener); 1733a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov } 1743a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov 1753a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov public static void unregisterListener(Listener listener) { 176a831c0b539cdc120655856074d4621e8e60a843bHugo Hudson sListeners.remove(listener); 177173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann } 178173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann 179dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang /** 180dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang * Returns true if the ContactSaveService was started successfully and false if an exception 181dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang * was thrown and a Toast error message was displayed. 182dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang */ 183dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang public static boolean startService(Context context, Intent intent, int saveMode) { 184dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang try { 185dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang context.startService(intent); 186dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang } catch (Exception exception) { 187dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang final int resId; 188dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang switch (saveMode) { 189dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang case ContactEditorBaseActivity.ContactEditor.SaveMode.SPLIT: 190dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang resId = R.string.contactUnlinkErrorToast; 191dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang break; 192dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang case ContactEditorBaseActivity.ContactEditor.SaveMode.RELOAD: 193dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang resId = R.string.contactJoinErrorToast; 194dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang break; 195dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang case ContactEditorBaseActivity.ContactEditor.SaveMode.CLOSE: 196dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang resId = R.string.contactSavedErrorToast; 197dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang break; 198dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang default: 199dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang resId = R.string.contactGenericErrorToast; 200dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang } 201dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang Toast.makeText(context, resId, Toast.LENGTH_SHORT).show(); 202dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang return false; 203dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang } 204dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang return true; 205dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang } 206dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang 207dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang /** 208dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang * Utility method that starts service and handles exception. 209dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang */ 210dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang public static void startService(Context context, Intent intent) { 211dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang try { 212dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang context.startService(intent); 213dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang } catch (Exception exception) { 214dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang Toast.makeText(context, R.string.contactGenericErrorToast, Toast.LENGTH_SHORT).show(); 215dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang } 216dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang } 217dd7d456a080f48d93aa3a9f9b04da0f17a8833a7Wenyi Wang 218173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann @Override 219a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov public Object getSystemService(String name) { 220a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov Object service = super.getSystemService(name); 221a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov if (service != null) { 222a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov return service; 223a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 224a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 225a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov return getApplicationContext().getSystemService(name); 226a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 227a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 228a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov @Override 229173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann protected void onHandleIntent(Intent intent) { 2303a7cc76a5fdd41af0b8da0e6e27adbba51b73e52Jay Shrauner if (intent == null) { 2313a7cc76a5fdd41af0b8da0e6e27adbba51b73e52Jay Shrauner Log.d(TAG, "onHandleIntent: could not handle null intent"); 2323a7cc76a5fdd41af0b8da0e6e27adbba51b73e52Jay Shrauner return; 2333a7cc76a5fdd41af0b8da0e6e27adbba51b73e52Jay Shrauner } 234615ed9c5e89f06a56ed6ad30244a9f6cb495e85cJay Shrauner if (!PermissionsUtil.hasPermission(this, WRITE_CONTACTS)) { 235615ed9c5e89f06a56ed6ad30244a9f6cb495e85cJay Shrauner Log.w(TAG, "No WRITE_CONTACTS permission, unable to write to CP2"); 236615ed9c5e89f06a56ed6ad30244a9f6cb495e85cJay Shrauner // TODO: add more specific error string such as "Turn on Contacts 237615ed9c5e89f06a56ed6ad30244a9f6cb495e85cJay Shrauner // permission to update your contacts" 238615ed9c5e89f06a56ed6ad30244a9f6cb495e85cJay Shrauner showToast(R.string.contactSavedErrorToast); 239615ed9c5e89f06a56ed6ad30244a9f6cb495e85cJay Shrauner return; 240615ed9c5e89f06a56ed6ad30244a9f6cb495e85cJay Shrauner } 241615ed9c5e89f06a56ed6ad30244a9f6cb495e85cJay Shrauner 2422f21c44104f4c1376ef2147c472555f43990af26Daisuke Miyakawa // Call an appropriate method. If we're sure it affects how incoming phone calls are 2432f21c44104f4c1376ef2147c472555f43990af26Daisuke Miyakawa // handled, then notify the fact to in-call screen. 244caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov String action = intent.getAction(); 245caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov if (ACTION_NEW_RAW_CONTACT.equals(action)) { 246caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov createRawContact(intent); 247a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } else if (ACTION_SAVE_CONTACT.equals(action)) { 248a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov saveContact(intent); 2491ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov } else if (ACTION_CREATE_GROUP.equals(action)) { 2501ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov createGroup(intent); 251e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov } else if (ACTION_RENAME_GROUP.equals(action)) { 252e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov renameGroup(intent); 253e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov } else if (ACTION_DELETE_GROUP.equals(action)) { 254e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov deleteGroup(intent); 2552d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan } else if (ACTION_UPDATE_GROUP.equals(action)) { 2562d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan updateGroup(intent); 2579d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov } else if (ACTION_SET_STARRED.equals(action)) { 2589d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov setStarred(intent); 2590f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann } else if (ACTION_SET_SUPER_PRIMARY.equals(action)) { 2600f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann setSuperPrimary(intent); 2610f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann } else if (ACTION_CLEAR_PRIMARY.equals(action)) { 2620f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann clearPrimary(intent); 263d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell } else if (ACTION_DELETE_MULTIPLE_CONTACTS.equals(action)) { 264d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell deleteMultipleContacts(intent); 2657d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov } else if (ACTION_DELETE_CONTACT.equals(action)) { 2667d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov deleteContact(intent); 2672b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov } else if (ACTION_JOIN_CONTACTS.equals(action)) { 2682b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov joinContacts(intent); 269d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell } else if (ACTION_JOIN_SEVERAL_CONTACTS.equals(action)) { 270d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell joinSeveralContacts(intent); 271683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson } else if (ACTION_SET_SEND_TO_VOICEMAIL.equals(action)) { 272683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson setSendToVoicemail(intent); 273683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson } else if (ACTION_SET_RINGTONE.equals(action)) { 274683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson setRingtone(intent); 275caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov } 276caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov } 277caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov 2789d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov /** 2799d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov * Creates an intent that can be sent to this service to create a new raw contact 2809d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov * using data presented as a set of ContentValues. 2819d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov */ 2829d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov public static Intent createNewRawContactIntent(Context context, 2832b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro ArrayList<ContentValues> values, AccountWithDataSet account, 284e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus Class<? extends Activity> callbackActivity, String callbackAction) { 2859d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov Intent serviceIntent = new Intent( 2869d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov context, ContactSaveService.class); 2879d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov serviceIntent.setAction(ContactSaveService.ACTION_NEW_RAW_CONTACT); 2889d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov if (account != null) { 2899d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov serviceIntent.putExtra(ContactSaveService.EXTRA_ACCOUNT_NAME, account.name); 2909d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov serviceIntent.putExtra(ContactSaveService.EXTRA_ACCOUNT_TYPE, account.type); 2912b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro serviceIntent.putExtra(ContactSaveService.EXTRA_DATA_SET, account.dataSet); 2929d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov } 2939d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov serviceIntent.putParcelableArrayListExtra( 2949d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov ContactSaveService.EXTRA_CONTENT_VALUES, values); 2959d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov 2969d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov // Callback intent will be invoked by the service once the new contact is 2979d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov // created. The service will put the URI of the new contact as "data" on 2989d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov // the callback intent. 2999d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov Intent callbackIntent = new Intent(context, callbackActivity); 3009d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov callbackIntent.setAction(callbackAction); 3019d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent); 3029d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov return serviceIntent; 3039d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov } 3049d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov 305caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov private void createRawContact(Intent intent) { 306caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov String accountName = intent.getStringExtra(EXTRA_ACCOUNT_NAME); 307caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov String accountType = intent.getStringExtra(EXTRA_ACCOUNT_TYPE); 3082b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro String dataSet = intent.getStringExtra(EXTRA_DATA_SET); 309caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov List<ContentValues> valueList = intent.getParcelableArrayListExtra(EXTRA_CONTENT_VALUES); 310caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT); 311caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov 312caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(); 313caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov operations.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI) 314caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov .withValue(RawContacts.ACCOUNT_NAME, accountName) 315caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov .withValue(RawContacts.ACCOUNT_TYPE, accountType) 3162b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro .withValue(RawContacts.DATA_SET, dataSet) 317caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov .build()); 318caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov 319caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov int size = valueList.size(); 320caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov for (int i = 0; i < size; i++) { 321caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov ContentValues values = valueList.get(i); 322caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov values.keySet().retainAll(ALLOWED_DATA_COLUMNS); 323caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov operations.add(ContentProviderOperation.newInsert(Data.CONTENT_URI) 324caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov .withValueBackReference(Data.RAW_CONTACT_ID, 0) 325caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov .withValues(values) 326caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov .build()); 327caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov } 328caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov 329caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov ContentResolver resolver = getContentResolver(); 330caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov ContentProviderResult[] results; 331caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov try { 332caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov results = resolver.applyBatch(ContactsContract.AUTHORITY, operations); 333caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov } catch (Exception e) { 334caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov throw new RuntimeException("Failed to store new contact", e); 335caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov } 336caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov 337caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov Uri rawContactUri = results[0].uri; 338caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov callbackIntent.setData(RawContacts.getContactLookupUri(resolver, rawContactUri)); 339caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov 3403a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov deliverCallback(callbackIntent); 341caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov } 342caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov 343caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov /** 344a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov * Creates an intent that can be sent to this service to create a new raw contact 345a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov * using data presented as a set of ContentValues. 346e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus * This variant is more convenient to use when there is only one photo that can 347e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus * possibly be updated, as in the Contact Details screen. 348e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus * @param rawContactId identifies a writable raw-contact whose photo is to be updated. 349e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus * @param updatedPhotoPath denotes a temporary file containing the contact's new photo. 350a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov */ 351851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu public static Intent createSaveContactIntent(Context context, RawContactDeltaList state, 352e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus String saveModeExtraKey, int saveMode, boolean isProfile, 353e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus Class<? extends Activity> callbackActivity, String callbackAction, long rawContactId, 354637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee Uri updatedPhotoPath) { 355e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus Bundle bundle = new Bundle(); 356637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee bundle.putParcelable(String.valueOf(rawContactId), updatedPhotoPath); 357e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus return createSaveContactIntent(context, state, saveModeExtraKey, saveMode, isProfile, 358e3373dceb689209533e95a2cfbfbf1d9008ab6f6Walter Jang callbackActivity, callbackAction, bundle, 359e3373dceb689209533e95a2cfbfbf1d9008ab6f6Walter Jang /* joinContactIdExtraKey */ null, /* joinContactId */ null); 360e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus } 361e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus 362e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus /** 363e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus * Creates an intent that can be sent to this service to create a new raw contact 364e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus * using data presented as a set of ContentValues. 365e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus * This variant is used when multiple contacts' photos may be updated, as in the 366e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus * Contact Editor. 367e3373dceb689209533e95a2cfbfbf1d9008ab6f6Walter Jang * 368e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus * @param updatedPhotos maps each raw-contact's ID to the file-path of the new photo. 369e3373dceb689209533e95a2cfbfbf1d9008ab6f6Walter Jang * @param joinContactIdExtraKey the key used to pass the joinContactId in the callback intent. 370e3373dceb689209533e95a2cfbfbf1d9008ab6f6Walter Jang * @param joinContactId the raw contact ID to join to the contact after doing the save. 371e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus */ 372851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu public static Intent createSaveContactIntent(Context context, RawContactDeltaList state, 373e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus String saveModeExtraKey, int saveMode, boolean isProfile, 374e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus Class<? extends Activity> callbackActivity, String callbackAction, 375e3373dceb689209533e95a2cfbfbf1d9008ab6f6Walter Jang Bundle updatedPhotos, String joinContactIdExtraKey, Long joinContactId) { 376f8c8ac348cf217766a6626a5b7191a0de11f06d3Walter Jang Intent serviceIntent = new Intent( 377f8c8ac348cf217766a6626a5b7191a0de11f06d3Walter Jang context, ContactSaveService.class); 378a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov serviceIntent.setAction(ContactSaveService.ACTION_SAVE_CONTACT); 379a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov serviceIntent.putExtra(EXTRA_CONTACT_STATE, (Parcelable) state); 380ead19c5eafee0ffb43b02a4ae75ac5244ad3f853Isaac Katzenelson serviceIntent.putExtra(EXTRA_SAVE_IS_PROFILE, isProfile); 3813a4e7a282867bfe4803940eca40e35094507605bbenny.lin serviceIntent.putExtra(EXTRA_SAVE_MODE, saveMode); 3823a4e7a282867bfe4803940eca40e35094507605bbenny.lin 383e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus if (updatedPhotos != null) { 384e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus serviceIntent.putExtra(EXTRA_UPDATED_PHOTOS, (Parcelable) updatedPhotos); 385e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus } 386a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 387e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus if (callbackActivity != null) { 388e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus // Callback intent will be invoked by the service once the contact is 389e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus // saved. The service will put the URI of the new contact as "data" on 390e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus // the callback intent. 391e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus Intent callbackIntent = new Intent(context, callbackActivity); 392e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus callbackIntent.putExtra(saveModeExtraKey, saveMode); 393e3373dceb689209533e95a2cfbfbf1d9008ab6f6Walter Jang if (joinContactIdExtraKey != null && joinContactId != null) { 394e3373dceb689209533e95a2cfbfbf1d9008ab6f6Walter Jang callbackIntent.putExtra(joinContactIdExtraKey, joinContactId); 395e3373dceb689209533e95a2cfbfbf1d9008ab6f6Walter Jang } 396e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus callbackIntent.setAction(callbackAction); 397e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent); 398e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus } 399a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov return serviceIntent; 400a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 401a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 402a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov private void saveContact(Intent intent) { 403851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu RawContactDeltaList state = intent.getParcelableExtra(EXTRA_CONTACT_STATE); 404ead19c5eafee0ffb43b02a4ae75ac5244ad3f853Isaac Katzenelson boolean isProfile = intent.getBooleanExtra(EXTRA_SAVE_IS_PROFILE, false); 405e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus Bundle updatedPhotos = intent.getParcelableExtra(EXTRA_UPDATED_PHOTOS); 406a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 4070809978e5d3f87687e1fd986130897deca1f74cdJay Shrauner if (state == null) { 4080809978e5d3f87687e1fd986130897deca1f74cdJay Shrauner Log.e(TAG, "Invalid arguments for saveContact request"); 4090809978e5d3f87687e1fd986130897deca1f74cdJay Shrauner return; 4100809978e5d3f87687e1fd986130897deca1f74cdJay Shrauner } 4110809978e5d3f87687e1fd986130897deca1f74cdJay Shrauner 4123a4e7a282867bfe4803940eca40e35094507605bbenny.lin int saveMode = intent.getIntExtra(EXTRA_SAVE_MODE, -1); 413a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov // Trim any empty fields, and RawContacts, before persisting 414a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov final AccountTypeManager accountTypes = AccountTypeManager.getInstance(this); 415851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu RawContactModifier.trimEmpty(state, accountTypes); 416a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 417a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov Uri lookupUri = null; 418a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 419a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov final ContentResolver resolver = getContentResolver(); 420ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang 421e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus boolean succeeded = false; 422a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 423ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus // Keep track of the id of a newly raw-contact (if any... there can be at most one). 424ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus long insertedRawContactId = -1; 425ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus 426a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov // Attempt to persist changes 427a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov int tries = 0; 428a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov while (tries++ < PERSIST_TRIES) { 429a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov try { 430a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov // Build operations and try applying 43167addcce8b54636c9e38f18db3bd262861aa3040Wenyi Wang final ArrayList<CPOWrapper> diffWrapper = state.buildDiffWrapper(); 43267addcce8b54636c9e38f18db3bd262861aa3040Wenyi Wang 43367addcce8b54636c9e38f18db3bd262861aa3040Wenyi Wang final ArrayList<ContentProviderOperation> diff = Lists.newArrayList(); 43467addcce8b54636c9e38f18db3bd262861aa3040Wenyi Wang 43567addcce8b54636c9e38f18db3bd262861aa3040Wenyi Wang for (CPOWrapper cpoWrapper : diffWrapper) { 43667addcce8b54636c9e38f18db3bd262861aa3040Wenyi Wang diff.add(cpoWrapper.getOperation()); 43767addcce8b54636c9e38f18db3bd262861aa3040Wenyi Wang } 438ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang 439a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan if (DEBUG) { 440a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan Log.v(TAG, "Content Provider Operations:"); 441a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan for (ContentProviderOperation operation : diff) { 442a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan Log.v(TAG, operation.toString()); 443a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan } 444a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan } 445a007e442687d3836d6a9f0d0ddcea527fa1481acKatherine Kuan 446ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang int numberProcessed = 0; 447ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang boolean batchFailed = false; 448ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang final ContentProviderResult[] results = new ContentProviderResult[diff.size()]; 449ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang while (numberProcessed < diff.size()) { 450ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang final int subsetCount = applyDiffSubset(diff, numberProcessed, results, resolver); 451ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang if (subsetCount == -1) { 452511561dd8ef9e6cac3c62e138193ef48e6d79760Jay Shrauner Log.w(TAG, "Resolver.applyBatch failed in saveContacts"); 453ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang batchFailed = true; 454ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang break; 455ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang } else { 456ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang numberProcessed += subsetCount; 457511561dd8ef9e6cac3c62e138193ef48e6d79760Jay Shrauner } 458a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 459a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 460ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang if (batchFailed) { 461ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang // Retry save 462ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang continue; 463ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang } 464ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang 46567addcce8b54636c9e38f18db3bd262861aa3040Wenyi Wang final long rawContactId = getRawContactId(state, diffWrapper, results); 466a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov if (rawContactId == -1) { 467a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov throw new IllegalStateException("Could not determine RawContact ID after save"); 468a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 469ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus // We don't have to check to see if the value is still -1. If we reach here, 470ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus // the previous loop iteration didn't succeed, so any ID that we obtained is bogus. 47167addcce8b54636c9e38f18db3bd262861aa3040Wenyi Wang insertedRawContactId = getInsertedRawContactId(diffWrapper, results); 4727c34c0a46434b2669b0fdba2c9e0e4cce5675f94Dave Santoro if (isProfile) { 4737c34c0a46434b2669b0fdba2c9e0e4cce5675f94Dave Santoro // Since the profile supports local raw contacts, which may have been completely 4747c34c0a46434b2669b0fdba2c9e0e4cce5675f94Dave Santoro // removed if all information was removed, we need to do a special query to 4757c34c0a46434b2669b0fdba2c9e0e4cce5675f94Dave Santoro // get the lookup URI for the profile contact (if it still exists). 4767c34c0a46434b2669b0fdba2c9e0e4cce5675f94Dave Santoro Cursor c = resolver.query(Profile.CONTENT_URI, 4777c34c0a46434b2669b0fdba2c9e0e4cce5675f94Dave Santoro new String[] {Contacts._ID, Contacts.LOOKUP_KEY}, 4787c34c0a46434b2669b0fdba2c9e0e4cce5675f94Dave Santoro null, null, null); 479e320c0bcd539f9426db9b694147e845fe0e2d59dJay Shrauner if (c == null) { 480e320c0bcd539f9426db9b694147e845fe0e2d59dJay Shrauner continue; 481e320c0bcd539f9426db9b694147e845fe0e2d59dJay Shrauner } 4827c34c0a46434b2669b0fdba2c9e0e4cce5675f94Dave Santoro try { 483162b7e34fb581d0fa279957af5136d190c40759fErik if (c.moveToFirst()) { 484162b7e34fb581d0fa279957af5136d190c40759fErik final long contactId = c.getLong(0); 485162b7e34fb581d0fa279957af5136d190c40759fErik final String lookupKey = c.getString(1); 486162b7e34fb581d0fa279957af5136d190c40759fErik lookupUri = Contacts.getLookupUri(contactId, lookupKey); 487162b7e34fb581d0fa279957af5136d190c40759fErik } 4887c34c0a46434b2669b0fdba2c9e0e4cce5675f94Dave Santoro } finally { 4897c34c0a46434b2669b0fdba2c9e0e4cce5675f94Dave Santoro c.close(); 4907c34c0a46434b2669b0fdba2c9e0e4cce5675f94Dave Santoro } 4917c34c0a46434b2669b0fdba2c9e0e4cce5675f94Dave Santoro } else { 4927c34c0a46434b2669b0fdba2c9e0e4cce5675f94Dave Santoro final Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, 4937c34c0a46434b2669b0fdba2c9e0e4cce5675f94Dave Santoro rawContactId); 4947c34c0a46434b2669b0fdba2c9e0e4cce5675f94Dave Santoro lookupUri = RawContacts.getContactLookupUri(resolver, rawContactUri); 4957c34c0a46434b2669b0fdba2c9e0e4cce5675f94Dave Santoro } 496e320c0bcd539f9426db9b694147e845fe0e2d59dJay Shrauner if (lookupUri != null) { 497e320c0bcd539f9426db9b694147e845fe0e2d59dJay Shrauner Log.v(TAG, "Saved contact. New URI: " + lookupUri); 498e320c0bcd539f9426db9b694147e845fe0e2d59dJay Shrauner } 499e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus 500e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus // We can change this back to false later, if we fail to save the contact photo. 501e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus succeeded = true; 502a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov break; 503a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 504a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } catch (RemoteException e) { 505a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov // Something went wrong, bail without success 506a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov Log.e(TAG, "Problem persisting user edits", e); 507a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov break; 508a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 50957fca1851e5371f259d4dd6bdf322e20c606c975Jay Shrauner } catch (IllegalArgumentException e) { 51057fca1851e5371f259d4dd6bdf322e20c606c975Jay Shrauner // This is thrown by applyBatch on malformed requests 51157fca1851e5371f259d4dd6bdf322e20c606c975Jay Shrauner Log.e(TAG, "Problem persisting user edits", e); 51257fca1851e5371f259d4dd6bdf322e20c606c975Jay Shrauner showToast(R.string.contactSavedErrorToast); 51357fca1851e5371f259d4dd6bdf322e20c606c975Jay Shrauner break; 51457fca1851e5371f259d4dd6bdf322e20c606c975Jay Shrauner 515a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } catch (OperationApplicationException e) { 516a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov // Version consistency failed, re-parent change and try again 517a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov Log.w(TAG, "Version consistency failed, re-parenting: " + e.toString()); 518a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov final StringBuilder sb = new StringBuilder(RawContacts._ID + " IN("); 519a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov boolean first = true; 520a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov final int count = state.size(); 521a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov for (int i = 0; i < count; i++) { 522a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov Long rawContactId = state.getRawContactId(i); 523a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov if (rawContactId != null && rawContactId != -1) { 524a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov if (!first) { 525a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov sb.append(','); 526a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 527a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov sb.append(rawContactId); 528a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov first = false; 529a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 530a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 531a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov sb.append(")"); 532a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 533a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov if (first) { 5343b6c628fc9e717a41c2954b1101c3a04ad382c55Brian Attwell throw new IllegalStateException( 5353b6c628fc9e717a41c2954b1101c3a04ad382c55Brian Attwell "Version consistency failed for a new contact", e); 536a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 537a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 538851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu final RawContactDeltaList newState = RawContactDeltaList.fromQuery( 539c90f95e63684363d10ffe5ef8f08f2159fb5bfc0Dave Santoro isProfile 540c90f95e63684363d10ffe5ef8f08f2159fb5bfc0Dave Santoro ? RawContactsEntity.PROFILE_CONTENT_URI 541c90f95e63684363d10ffe5ef8f08f2159fb5bfc0Dave Santoro : RawContactsEntity.CONTENT_URI, 542c90f95e63684363d10ffe5ef8f08f2159fb5bfc0Dave Santoro resolver, sb.toString(), null, null); 543851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu state = RawContactDeltaList.mergeAfter(newState, state); 544c90f95e63684363d10ffe5ef8f08f2159fb5bfc0Dave Santoro 545c90f95e63684363d10ffe5ef8f08f2159fb5bfc0Dave Santoro // Update the new state to use profile URIs if appropriate. 546c90f95e63684363d10ffe5ef8f08f2159fb5bfc0Dave Santoro if (isProfile) { 547851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu for (RawContactDelta delta : state) { 548c90f95e63684363d10ffe5ef8f08f2159fb5bfc0Dave Santoro delta.setProfileQueryUri(); 549c90f95e63684363d10ffe5ef8f08f2159fb5bfc0Dave Santoro } 550c90f95e63684363d10ffe5ef8f08f2159fb5bfc0Dave Santoro } 551a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 552a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 553a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 554e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus // Now save any updated photos. We do this at the end to ensure that 555e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus // the ContactProvider already knows about newly-created contacts. 556e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus if (updatedPhotos != null) { 557e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus for (String key : updatedPhotos.keySet()) { 558637a38ec9de6b1f434d7a13105f2e747faae5107Yorke Lee Uri photoUri = updatedPhotos.getParcelable(key); 559e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus long rawContactId = Long.parseLong(key); 560ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus 561ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus // If the raw-contact ID is negative, we are saving a new raw-contact; 562ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus // replace the bogus ID with the new one that we actually saved the contact at. 563ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus if (rawContactId < 0) { 564ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus rawContactId = insertedRawContactId; 565ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus } 566ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus 567511561dd8ef9e6cac3c62e138193ef48e6d79760Jay Shrauner // If the save failed, insertedRawContactId will be -1 568c4698fbe3a5a94de639f91b01c2cb02b61d51331Jay Shrauner if (rawContactId < 0 || !saveUpdatedPhoto(rawContactId, photoUri, saveMode)) { 569511561dd8ef9e6cac3c62e138193ef48e6d79760Jay Shrauner succeeded = false; 570511561dd8ef9e6cac3c62e138193ef48e6d79760Jay Shrauner } 571e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus } 572e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus } 573e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus 574e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT); 575e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus if (callbackIntent != null) { 576e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus if (succeeded) { 577e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus // Mark the intent to indicate that the save was successful (even if the lookup URI 578e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus // is now null). For local contacts or the local profile, it's possible that the 579e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus // save triggered removal of the contact, so no lookup URI would exist.. 580e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus callbackIntent.putExtra(EXTRA_SAVE_SUCCEEDED, true); 581e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus } 582e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus callbackIntent.setData(lookupUri); 583e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus deliverCallback(callbackIntent); 584e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus } 585a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 586a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 587e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus /** 588ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang * Splits "diff" into subsets based on "MAX_CONTACTS_PROVIDER_BATCH_SIZE", applies each of the 589ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang * subsets, adds the returned array to "results". 590ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang * 591ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang * @return the size of the array, if not null; -1 when the array is null. 592ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang */ 593ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang private int applyDiffSubset(ArrayList<ContentProviderOperation> diff, int offset, 594ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang ContentProviderResult[] results, ContentResolver resolver) 595ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang throws RemoteException, OperationApplicationException { 596ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang final int subsetCount = Math.min(diff.size() - offset, MAX_CONTACTS_PROVIDER_BATCH_SIZE); 597ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang final ArrayList<ContentProviderOperation> subset = new ArrayList<>(); 598ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang subset.addAll(diff.subList(offset, offset + subsetCount)); 599ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang final ContentProviderResult[] subsetResult = resolver.applyBatch(ContactsContract 600ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang .AUTHORITY, subset); 601ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang if (subsetResult == null || (offset + subsetResult.length) > results.length) { 602ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang return -1; 603ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang } 604ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang for (ContentProviderResult c : subsetResult) { 605ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang results[offset++] = c; 606ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang } 607ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang return subsetResult.length; 608ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang } 609ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang 610ccdf69cb069764a971bf0173f3440cdbece58be3Wenyi Wang /** 611e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus * Save updated photo for the specified raw-contact. 612e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus * @return true for success, false for failure 613e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus */ 6143a4e7a282867bfe4803940eca40e35094507605bbenny.lin private boolean saveUpdatedPhoto(long rawContactId, Uri photoUri, int saveMode) { 615ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus final Uri outputUri = Uri.withAppendedPath( 616e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 617e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus RawContacts.DisplayPhoto.CONTENT_DIRECTORY); 618e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus 6193a4e7a282867bfe4803940eca40e35094507605bbenny.lin return ContactPhotoUtils.savePhotoFromUriToUri(this, photoUri, outputUri, (saveMode == 0)); 620e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus } 621e692e010ca02200087997280e7c239ebf94aa8f9Josh Gargus 622ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus /** 623ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus * Find the ID of an existing or newly-inserted raw-contact. If none exists, return -1. 624ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus */ 625851222a96b5d68602fb361ea3527101e893f67e3Maurice Chu private long getRawContactId(RawContactDeltaList state, 62667addcce8b54636c9e38f18db3bd262861aa3040Wenyi Wang final ArrayList<CPOWrapper> diffWrapper, 627a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov final ContentProviderResult[] results) { 628ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus long existingRawContactId = state.findRawContactId(); 629ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus if (existingRawContactId != -1) { 630ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus return existingRawContactId; 631a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 632a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 63367addcce8b54636c9e38f18db3bd262861aa3040Wenyi Wang return getInsertedRawContactId(diffWrapper, results); 634ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus } 635ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus 636ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus /** 637ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus * Find the ID of a newly-inserted raw-contact. If none exists, return -1. 638ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus */ 639ef15c8e4a1093ac9d6bb0e9aed10a130e8e79be5Josh Gargus private long getInsertedRawContactId( 64067addcce8b54636c9e38f18db3bd262861aa3040Wenyi Wang final ArrayList<CPOWrapper> diffWrapper, final ContentProviderResult[] results) { 641568f4e72711908455ccd20fbb04c1017b10d7e1cJay Shrauner if (results == null) { 642568f4e72711908455ccd20fbb04c1017b10d7e1cJay Shrauner return -1; 643568f4e72711908455ccd20fbb04c1017b10d7e1cJay Shrauner } 64467addcce8b54636c9e38f18db3bd262861aa3040Wenyi Wang final int diffSize = diffWrapper.size(); 6453d7edc3ef4f2521639bae91c93a8238e5a9509c1Jay Shrauner final int numResults = results.length; 6463d7edc3ef4f2521639bae91c93a8238e5a9509c1Jay Shrauner for (int i = 0; i < diffSize && i < numResults; i++) { 64767addcce8b54636c9e38f18db3bd262861aa3040Wenyi Wang final CPOWrapper cpoWrapper = diffWrapper.get(i); 64867addcce8b54636c9e38f18db3bd262861aa3040Wenyi Wang final boolean isInsert = CompatUtils.isInsertCompat(cpoWrapper); 64967addcce8b54636c9e38f18db3bd262861aa3040Wenyi Wang if (isInsert && cpoWrapper.getOperation().getUri().getEncodedPath().contains( 65067addcce8b54636c9e38f18db3bd262861aa3040Wenyi Wang RawContacts.CONTENT_URI.getEncodedPath())) { 651a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov return ContentUris.parseId(results[i].uri); 652a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 653a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 654a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov return -1; 655a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov } 656a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov 657a011414b12955a91c8f3efe528f374654d930098Dmitri Plotnikov /** 658717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan * Creates an intent that can be sent to this service to create a new group as 659717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan * well as add new members at the same time. 660717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan * 661717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan * @param context of the application 662717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan * @param account in which the group should be created 663717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan * @param label is the name of the group (cannot be null) 664717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan * @param rawContactsToAdd is an array of raw contact IDs for contacts that 665717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan * should be added to the group 666717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan * @param callbackActivity is the activity to send the callback intent to 667717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan * @param callbackAction is the intent action for the callback intent 668caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov */ 6692b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro public static Intent createNewGroupIntent(Context context, AccountWithDataSet account, 670e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus String label, long[] rawContactsToAdd, Class<? extends Activity> callbackActivity, 671717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan String callbackAction) { 6729d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov Intent serviceIntent = new Intent(context, ContactSaveService.class); 6739d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov serviceIntent.setAction(ContactSaveService.ACTION_CREATE_GROUP); 6749d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov serviceIntent.putExtra(ContactSaveService.EXTRA_ACCOUNT_TYPE, account.type); 6759d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov serviceIntent.putExtra(ContactSaveService.EXTRA_ACCOUNT_NAME, account.name); 6762b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro serviceIntent.putExtra(ContactSaveService.EXTRA_DATA_SET, account.dataSet); 6779d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_LABEL, label); 678717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan serviceIntent.putExtra(ContactSaveService.EXTRA_RAW_CONTACTS_TO_ADD, rawContactsToAdd); 679caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov 6809d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov // Callback intent will be invoked by the service once the new group is 681717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan // created. 6829d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov Intent callbackIntent = new Intent(context, callbackActivity); 6839d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov callbackIntent.setAction(callbackAction); 6841ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent); 6859d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov 6861ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov return serviceIntent; 6871ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov } 6881ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov 6891ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov private void createGroup(Intent intent) { 6902b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro String accountType = intent.getStringExtra(EXTRA_ACCOUNT_TYPE); 6912b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro String accountName = intent.getStringExtra(EXTRA_ACCOUNT_NAME); 6922b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro String dataSet = intent.getStringExtra(EXTRA_DATA_SET); 6932b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro String label = intent.getStringExtra(EXTRA_GROUP_LABEL); 694717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan final long[] rawContactsToAdd = intent.getLongArrayExtra(EXTRA_RAW_CONTACTS_TO_ADD); 6951ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov 6961ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov ContentValues values = new ContentValues(); 6971ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov values.put(Groups.ACCOUNT_TYPE, accountType); 6981ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov values.put(Groups.ACCOUNT_NAME, accountName); 6992b3f3c54d3beb017b2f59f19e9ce0ecc3e039dbcDave Santoro values.put(Groups.DATA_SET, dataSet); 7001ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov values.put(Groups.TITLE, label); 7011ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov 702717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan final ContentResolver resolver = getContentResolver(); 703717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan 704717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan // Create the new group 705717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan final Uri groupUri = resolver.insert(Groups.CONTENT_URI, values); 706717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan 707717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan // If there's no URI, then the insertion failed. Abort early because group members can't be 708717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan // added if the group doesn't exist 7091ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov if (groupUri == null) { 710717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan Log.e(TAG, "Couldn't create group with label " + label); 7111ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov return; 7121ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov } 7131ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov 714717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan // Add new group members 715717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan addMembersToGroup(resolver, rawContactsToAdd, ContentUris.parseId(groupUri)); 716717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan 717717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan // TODO: Move this into the contact editor where it belongs. This needs to be integrated 718717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan // with the way other intent extras that are passed to the {@link ContactEditorActivity}. 7191ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov values.clear(); 7201ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov values.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE); 7211ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov values.put(GroupMembership.GROUP_ROW_ID, ContentUris.parseId(groupUri)); 7221ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov 7231ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT); 724c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan callbackIntent.setData(groupUri); 725717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan // TODO: This can be taken out when the above TODO is addressed 7261ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov callbackIntent.putExtra(ContactsContract.Intents.Insert.DATA, Lists.newArrayList(values)); 7273a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov deliverCallback(callbackIntent); 7281ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov } 7291ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov 7301ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov /** 7319d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov * Creates an intent that can be sent to this service to rename a group. 7321ac58b6f2a925c5a4f759346e5244dfd174acd08Dmitri Plotnikov */ 733c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan public static Intent createGroupRenameIntent(Context context, long groupId, String newLabel, 734e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus Class<? extends Activity> callbackActivity, String callbackAction) { 7359d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov Intent serviceIntent = new Intent(context, ContactSaveService.class); 7369d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov serviceIntent.setAction(ContactSaveService.ACTION_RENAME_GROUP); 7379d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_ID, groupId); 7389d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_LABEL, newLabel); 739c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan 740c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan // Callback intent will be invoked by the service once the group is renamed. 741c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan Intent callbackIntent = new Intent(context, callbackActivity); 742c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan callbackIntent.setAction(callbackAction); 743c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent); 744c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan 745caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov return serviceIntent; 746caf0bc759c4ef96dde5bb0a5256c1dcb51b6ccc4Dmitri Plotnikov } 747e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov 748e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov private void renameGroup(Intent intent) { 749e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov long groupId = intent.getLongExtra(EXTRA_GROUP_ID, -1); 750e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov String label = intent.getStringExtra(EXTRA_GROUP_LABEL); 751e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov 752e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov if (groupId == -1) { 753e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov Log.e(TAG, "Invalid arguments for renameGroup request"); 754e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov return; 755e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov } 756e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov 757e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov ContentValues values = new ContentValues(); 758e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov values.put(Groups.TITLE, label); 759c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan final Uri groupUri = ContentUris.withAppendedId(Groups.CONTENT_URI, groupId); 760c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan getContentResolver().update(groupUri, values, null, null); 761c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan 762c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT); 763c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan callbackIntent.setData(groupUri); 764c6b8afe730255537978f2c938cca6986cae63c34Katherine Kuan deliverCallback(callbackIntent); 765e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov } 766e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov 767e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov /** 7689d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov * Creates an intent that can be sent to this service to delete a group. 769e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov */ 7709d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov public static Intent createGroupDeletionIntent(Context context, long groupId) { 7719d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov Intent serviceIntent = new Intent(context, ContactSaveService.class); 7729d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov serviceIntent.setAction(ContactSaveService.ACTION_DELETE_GROUP); 773e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_ID, groupId); 774e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov return serviceIntent; 775e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov } 776e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov 777e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov private void deleteGroup(Intent intent) { 778e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov long groupId = intent.getLongExtra(EXTRA_GROUP_ID, -1); 779e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov if (groupId == -1) { 780e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov Log.e(TAG, "Invalid arguments for deleteGroup request"); 781e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov return; 782e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov } 783e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov 784e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov getContentResolver().delete( 785e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov ContentUris.withAppendedId(Groups.CONTENT_URI, groupId), null, null); 786e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov } 787e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov 788e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov /** 7892d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan * Creates an intent that can be sent to this service to rename a group as 7902d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan * well as add and remove members from the group. 7912d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan * 7922d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan * @param context of the application 7932d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan * @param groupId of the group that should be modified 7942d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan * @param newLabel is the updated name of the group (can be null if the name 7952d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan * should not be updated) 7962d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan * @param rawContactsToAdd is an array of raw contact IDs for contacts that 7972d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan * should be added to the group 7982d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan * @param rawContactsToRemove is an array of raw contact IDs for contacts 7992d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan * that should be removed from the group 8002d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan * @param callbackActivity is the activity to send the callback intent to 8012d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan * @param callbackAction is the intent action for the callback intent 8022d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan */ 8032d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan public static Intent createGroupUpdateIntent(Context context, long groupId, String newLabel, 8042d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan long[] rawContactsToAdd, long[] rawContactsToRemove, 805e5d3f897689c8ba0f275c7679c72eacb190ae9b8Josh Gargus Class<? extends Activity> callbackActivity, String callbackAction) { 8062d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan Intent serviceIntent = new Intent(context, ContactSaveService.class); 8072d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan serviceIntent.setAction(ContactSaveService.ACTION_UPDATE_GROUP); 8082d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_ID, groupId); 8092d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_LABEL, newLabel); 8102d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan serviceIntent.putExtra(ContactSaveService.EXTRA_RAW_CONTACTS_TO_ADD, rawContactsToAdd); 8112d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan serviceIntent.putExtra(ContactSaveService.EXTRA_RAW_CONTACTS_TO_REMOVE, 8122d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan rawContactsToRemove); 8132d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan 8142d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan // Callback intent will be invoked by the service once the group is updated 8152d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan Intent callbackIntent = new Intent(context, callbackActivity); 8162d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan callbackIntent.setAction(callbackAction); 8172d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent); 8182d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan 8192d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan return serviceIntent; 8202d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan } 8212d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan 8222d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan private void updateGroup(Intent intent) { 8232d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan long groupId = intent.getLongExtra(EXTRA_GROUP_ID, -1); 8242d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan String label = intent.getStringExtra(EXTRA_GROUP_LABEL); 8252d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan long[] rawContactsToAdd = intent.getLongArrayExtra(EXTRA_RAW_CONTACTS_TO_ADD); 8262d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan long[] rawContactsToRemove = intent.getLongArrayExtra(EXTRA_RAW_CONTACTS_TO_REMOVE); 8272d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan 8282d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan if (groupId == -1) { 8292d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan Log.e(TAG, "Invalid arguments for updateGroup request"); 8302d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan return; 8312d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan } 8322d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan 8332d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan final ContentResolver resolver = getContentResolver(); 8342d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan final Uri groupUri = ContentUris.withAppendedId(Groups.CONTENT_URI, groupId); 8352d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan 8362d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan // Update group name if necessary 8372d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan if (label != null) { 8382d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan ContentValues values = new ContentValues(); 8392d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan values.put(Groups.TITLE, label); 840717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan resolver.update(groupUri, values, null, null); 8412d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan } 8422d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan 843717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan // Add and remove members if necessary 844717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan addMembersToGroup(resolver, rawContactsToAdd, groupId); 845717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan removeMembersFromGroup(resolver, rawContactsToRemove, groupId); 846717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan 847717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT); 848717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan callbackIntent.setData(groupUri); 849717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan deliverCallback(callbackIntent); 850717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan } 851717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan 85218958a29b3eddb6fc42cf651ec0eed27103f534dDaniel Lehmann private static void addMembersToGroup(ContentResolver resolver, long[] rawContactsToAdd, 853717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan long groupId) { 854717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan if (rawContactsToAdd == null) { 855717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan return; 856717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan } 8572d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan for (long rawContactId : rawContactsToAdd) { 8582d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan try { 8592d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan final ArrayList<ContentProviderOperation> rawContactOperations = 8602d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan new ArrayList<ContentProviderOperation>(); 8612d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan 8622d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan // Build an assert operation to ensure the contact is not already in the group 8632d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan final ContentProviderOperation.Builder assertBuilder = ContentProviderOperation 8642d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan .newAssertQuery(Data.CONTENT_URI); 8652d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan assertBuilder.withSelection(Data.RAW_CONTACT_ID + "=? AND " + 8662d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan Data.MIMETYPE + "=? AND " + GroupMembership.GROUP_ROW_ID + "=?", 8672d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan new String[] { String.valueOf(rawContactId), 8682d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan GroupMembership.CONTENT_ITEM_TYPE, String.valueOf(groupId)}); 8692d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan assertBuilder.withExpectedCount(0); 8702d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan rawContactOperations.add(assertBuilder.build()); 8712d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan 8722d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan // Build an insert operation to add the contact to the group 8732d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan final ContentProviderOperation.Builder insertBuilder = ContentProviderOperation 8742d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan .newInsert(Data.CONTENT_URI); 8752d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan insertBuilder.withValue(Data.RAW_CONTACT_ID, rawContactId); 8762d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan insertBuilder.withValue(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE); 8772d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan insertBuilder.withValue(GroupMembership.GROUP_ROW_ID, groupId); 8782d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan rawContactOperations.add(insertBuilder.build()); 8792d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan 8802d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan if (DEBUG) { 8812d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan for (ContentProviderOperation operation : rawContactOperations) { 8822d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan Log.v(TAG, operation.toString()); 8832d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan } 8842d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan } 8852d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan 8862d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan // Apply batch 8872d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan if (!rawContactOperations.isEmpty()) { 88818958a29b3eddb6fc42cf651ec0eed27103f534dDaniel Lehmann resolver.applyBatch(ContactsContract.AUTHORITY, rawContactOperations); 8892d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan } 8902d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan } catch (RemoteException e) { 8912d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan // Something went wrong, bail without success 8922d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan Log.e(TAG, "Problem persisting user edits for raw contact ID " + 8932d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan String.valueOf(rawContactId), e); 8942d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan } catch (OperationApplicationException e) { 8952d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan // The assert could have failed because the contact is already in the group, 8962d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan // just continue to the next contact 8972d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan Log.w(TAG, "Assert failed in adding raw contact ID " + 8982d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan String.valueOf(rawContactId) + ". Already exists in group " + 8992d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan String.valueOf(groupId), e); 9002d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan } 9012d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan } 902717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan } 9032d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan 90418958a29b3eddb6fc42cf651ec0eed27103f534dDaniel Lehmann private static void removeMembersFromGroup(ContentResolver resolver, long[] rawContactsToRemove, 905717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan long groupId) { 906717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan if (rawContactsToRemove == null) { 907717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan return; 908717e343811088da922cd84fb0d196de85fba7fe9Katherine Kuan } 9092d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan for (long rawContactId : rawContactsToRemove) { 9102d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan // Apply the delete operation on the data row for the given raw contact's 9112d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan // membership in the given group. If no contact matches the provided selection, then 9122d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan // nothing will be done. Just continue to the next contact. 91318958a29b3eddb6fc42cf651ec0eed27103f534dDaniel Lehmann resolver.delete(Data.CONTENT_URI, Data.RAW_CONTACT_ID + "=? AND " + 9142d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan Data.MIMETYPE + "=? AND " + GroupMembership.GROUP_ROW_ID + "=?", 9152d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan new String[] { String.valueOf(rawContactId), 9162d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan GroupMembership.CONTENT_ITEM_TYPE, String.valueOf(groupId)}); 9172d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan } 9182d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan } 9192d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan 9202d851cc895ffc7afd322298c7d4391ca5bea1a2dKatherine Kuan /** 9219d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov * Creates an intent that can be sent to this service to star or un-star a contact. 922e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov */ 9239d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov public static Intent createSetStarredIntent(Context context, Uri contactUri, boolean value) { 9249d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov Intent serviceIntent = new Intent(context, ContactSaveService.class); 9259d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov serviceIntent.setAction(ContactSaveService.ACTION_SET_STARRED); 9269d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_URI, contactUri); 9279d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov serviceIntent.putExtra(ContactSaveService.EXTRA_STARRED_FLAG, value); 9289d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov 929e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov return serviceIntent; 930e898a9fa52728b2ff6fcd3add693471e9e15553dDmitri Plotnikov } 9319d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov 9329d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov private void setStarred(Intent intent) { 9339d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov Uri contactUri = intent.getParcelableExtra(EXTRA_CONTACT_URI); 9349d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov boolean value = intent.getBooleanExtra(EXTRA_STARRED_FLAG, false); 9359d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov if (contactUri == null) { 9369d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov Log.e(TAG, "Invalid arguments for setStarred request"); 9379d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov return; 9389d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov } 9399d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov 9409d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov final ContentValues values = new ContentValues(1); 9419d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov values.put(Contacts.STARRED, value); 9429d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov getContentResolver().update(contactUri, values, null, null); 943e8e3fb83f3805d0636e5757a4d91db74796518e3Yorke Lee 944e8e3fb83f3805d0636e5757a4d91db74796518e3Yorke Lee // Undemote the contact if necessary 945e8e3fb83f3805d0636e5757a4d91db74796518e3Yorke Lee final Cursor c = getContentResolver().query(contactUri, new String[] {Contacts._ID}, 946e8e3fb83f3805d0636e5757a4d91db74796518e3Yorke Lee null, null, null); 947c12a280b496e6a997ab972641fb8e50e1eb8736cJay Shrauner if (c == null) { 948c12a280b496e6a997ab972641fb8e50e1eb8736cJay Shrauner return; 949c12a280b496e6a997ab972641fb8e50e1eb8736cJay Shrauner } 950e8e3fb83f3805d0636e5757a4d91db74796518e3Yorke Lee try { 951e8e3fb83f3805d0636e5757a4d91db74796518e3Yorke Lee if (c.moveToFirst()) { 952e8e3fb83f3805d0636e5757a4d91db74796518e3Yorke Lee final long id = c.getLong(0); 953bbb8c99a34061911c800bbd1981b74fb7f5b5a9dYorke Lee 954bbb8c99a34061911c800bbd1981b74fb7f5b5a9dYorke Lee // Don't bother undemoting if this contact is the user's profile. 955bbb8c99a34061911c800bbd1981b74fb7f5b5a9dYorke Lee if (id < Profile.MIN_ID) { 956aac0e66fb100b329d6010637998849048efadca9Wenyi Wang PinnedPositionsCompat.undemote(getContentResolver(), id); 957bbb8c99a34061911c800bbd1981b74fb7f5b5a9dYorke Lee } 958e8e3fb83f3805d0636e5757a4d91db74796518e3Yorke Lee } 959e8e3fb83f3805d0636e5757a4d91db74796518e3Yorke Lee } finally { 960e8e3fb83f3805d0636e5757a4d91db74796518e3Yorke Lee c.close(); 961e8e3fb83f3805d0636e5757a4d91db74796518e3Yorke Lee } 9629d730dd9d9efe125c9102b298f897577157ffecdDmitri Plotnikov } 9630f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann 9640f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann /** 965683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson * Creates an intent that can be sent to this service to set the redirect to voicemail. 966683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson */ 967683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson public static Intent createSetSendToVoicemail(Context context, Uri contactUri, 968683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson boolean value) { 969683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson Intent serviceIntent = new Intent(context, ContactSaveService.class); 970683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson serviceIntent.setAction(ContactSaveService.ACTION_SET_SEND_TO_VOICEMAIL); 971683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_URI, contactUri); 972683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson serviceIntent.putExtra(ContactSaveService.EXTRA_SEND_TO_VOICEMAIL_FLAG, value); 973683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson 974683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson return serviceIntent; 975683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson } 976683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson 977683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson private void setSendToVoicemail(Intent intent) { 978683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson Uri contactUri = intent.getParcelableExtra(EXTRA_CONTACT_URI); 979683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson boolean value = intent.getBooleanExtra(EXTRA_SEND_TO_VOICEMAIL_FLAG, false); 980683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson if (contactUri == null) { 981683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson Log.e(TAG, "Invalid arguments for setRedirectToVoicemail"); 982683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson return; 983683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson } 984683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson 985683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson final ContentValues values = new ContentValues(1); 986683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson values.put(Contacts.SEND_TO_VOICEMAIL, value); 987683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson getContentResolver().update(contactUri, values, null, null); 988683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson } 989683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson 990683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson /** 991683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson * Creates an intent that can be sent to this service to save the contact's ringtone. 992683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson */ 993683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson public static Intent createSetRingtone(Context context, Uri contactUri, 994683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson String value) { 995683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson Intent serviceIntent = new Intent(context, ContactSaveService.class); 996683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson serviceIntent.setAction(ContactSaveService.ACTION_SET_RINGTONE); 997683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_URI, contactUri); 998683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson serviceIntent.putExtra(ContactSaveService.EXTRA_CUSTOM_RINGTONE, value); 999683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson 1000683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson return serviceIntent; 1001683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson } 1002683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson 1003683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson private void setRingtone(Intent intent) { 1004683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson Uri contactUri = intent.getParcelableExtra(EXTRA_CONTACT_URI); 1005683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson String value = intent.getStringExtra(EXTRA_CUSTOM_RINGTONE); 1006683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson if (contactUri == null) { 1007683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson Log.e(TAG, "Invalid arguments for setRingtone"); 1008683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson return; 1009683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson } 1010683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson ContentValues values = new ContentValues(1); 1011683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson values.put(Contacts.CUSTOM_RINGTONE, value); 1012683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson getContentResolver().update(contactUri, values, null, null); 1013683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson } 1014683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson 1015683b57e1fbf27c81c9973de814fc8bb1852e6df8Isaac Katzenelson /** 10160f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann * Creates an intent that sets the selected data item as super primary (default) 10170f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann */ 10180f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann public static Intent createSetSuperPrimaryIntent(Context context, long dataId) { 10190f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann Intent serviceIntent = new Intent(context, ContactSaveService.class); 10200f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann serviceIntent.setAction(ContactSaveService.ACTION_SET_SUPER_PRIMARY); 10210f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann serviceIntent.putExtra(ContactSaveService.EXTRA_DATA_ID, dataId); 10220f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann return serviceIntent; 10230f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann } 10240f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann 10250f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann private void setSuperPrimary(Intent intent) { 10260f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann long dataId = intent.getLongExtra(EXTRA_DATA_ID, -1); 10270f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann if (dataId == -1) { 10280f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann Log.e(TAG, "Invalid arguments for setSuperPrimary request"); 10290f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann return; 10300f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann } 10310f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann 1032d7ca03e23948c3b2d5f97ec5598d8b50e3fc0b25Chiao Cheng ContactUpdateUtils.setSuperPrimary(this, dataId); 10330f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann } 10340f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann 10350f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann /** 10360f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann * Creates an intent that clears the primary flag of all data items that belong to the same 10370f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann * raw_contact as the given data item. Will only clear, if the data item was primary before 10380f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann * this call 10390f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann */ 10400f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann public static Intent createClearPrimaryIntent(Context context, long dataId) { 10410f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann Intent serviceIntent = new Intent(context, ContactSaveService.class); 10420f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann serviceIntent.setAction(ContactSaveService.ACTION_CLEAR_PRIMARY); 10430f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann serviceIntent.putExtra(ContactSaveService.EXTRA_DATA_ID, dataId); 10440f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann return serviceIntent; 10450f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann } 10460f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann 10470f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann private void clearPrimary(Intent intent) { 10480f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann long dataId = intent.getLongExtra(EXTRA_DATA_ID, -1); 10490f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann if (dataId == -1) { 10500f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann Log.e(TAG, "Invalid arguments for clearPrimary request"); 10510f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann return; 10520f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann } 10530f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann 10540f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann // Update the primary values in the data record. 10550f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann ContentValues values = new ContentValues(1); 10560f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann values.put(Data.IS_SUPER_PRIMARY, 0); 10570f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann values.put(Data.IS_PRIMARY, 0); 10580f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann 10590f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI, dataId), 10600f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann values, null, null); 10610f78e8b198c56963d6d6044839bb0679f4afd075Daniel Lehmann } 10627d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov 10637d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov /** 10647d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov * Creates an intent that can be sent to this service to delete a contact. 10657d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov */ 10667d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov public static Intent createDeleteContactIntent(Context context, Uri contactUri) { 10677d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov Intent serviceIntent = new Intent(context, ContactSaveService.class); 10687d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov serviceIntent.setAction(ContactSaveService.ACTION_DELETE_CONTACT); 10697d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_URI, contactUri); 10707d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov return serviceIntent; 10717d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov } 10727d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov 1073d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell /** 1074d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell * Creates an intent that can be sent to this service to delete multiple contacts. 1075d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell */ 1076d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell public static Intent createDeleteMultipleContactsIntent(Context context, 1077d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell long[] contactIds) { 1078d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell Intent serviceIntent = new Intent(context, ContactSaveService.class); 1079d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell serviceIntent.setAction(ContactSaveService.ACTION_DELETE_MULTIPLE_CONTACTS); 1080d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_IDS, contactIds); 1081d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell return serviceIntent; 1082d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell } 1083d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell 10847d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov private void deleteContact(Intent intent) { 10857d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov Uri contactUri = intent.getParcelableExtra(EXTRA_CONTACT_URI); 10867d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov if (contactUri == null) { 10877d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov Log.e(TAG, "Invalid arguments for deleteContact request"); 10887d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov return; 10897d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov } 10907d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov 10917d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov getContentResolver().delete(contactUri, null, null); 10927d8cabb65b02096583b928c64ae455d1f79e5633Dmitri Plotnikov } 10932b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov 1094d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell private void deleteMultipleContacts(Intent intent) { 1095d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell final long[] contactIds = intent.getLongArrayExtra(EXTRA_CONTACT_IDS); 1096d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell if (contactIds == null) { 1097d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell Log.e(TAG, "Invalid arguments for deleteMultipleContacts request"); 1098d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell return; 1099d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell } 1100d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell for (long contactId : contactIds) { 1101d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell final Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId); 1102d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell getContentResolver().delete(contactUri, null, null); 1103d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell } 1104687d218928bd188a13cc32a3c553590fe0d564b1Wenyi Wang final String deleteToastMessage = getResources().getQuantityString(R.plurals 1105687d218928bd188a13cc32a3c553590fe0d564b1Wenyi Wang .contacts_deleted_toast, contactIds.length); 1106687d218928bd188a13cc32a3c553590fe0d564b1Wenyi Wang mMainHandler.post(new Runnable() { 1107687d218928bd188a13cc32a3c553590fe0d564b1Wenyi Wang @Override 1108687d218928bd188a13cc32a3c553590fe0d564b1Wenyi Wang public void run() { 1109687d218928bd188a13cc32a3c553590fe0d564b1Wenyi Wang Toast.makeText(ContactSaveService.this, deleteToastMessage, Toast.LENGTH_LONG) 1110687d218928bd188a13cc32a3c553590fe0d564b1Wenyi Wang .show(); 1111687d218928bd188a13cc32a3c553590fe0d564b1Wenyi Wang } 1112687d218928bd188a13cc32a3c553590fe0d564b1Wenyi Wang }); 1113d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell } 1114d2962a3bb669a381d31a586df3b906033a8fa571Brian Attwell 11152b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov /** 11162b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov * Creates an intent that can be sent to this service to join two contacts. 1117d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell * The resulting contact uses the name from {@param contactId1} if possible. 11182b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov */ 11192b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov public static Intent createJoinContactsIntent(Context context, long contactId1, 1120d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell long contactId2, Class<? extends Activity> callbackActivity, String callbackAction) { 11212b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov Intent serviceIntent = new Intent(context, ContactSaveService.class); 11222b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov serviceIntent.setAction(ContactSaveService.ACTION_JOIN_CONTACTS); 11232b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_ID1, contactId1); 11242b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_ID2, contactId2); 11252b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov 11262b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov // Callback intent will be invoked by the service once the contacts are joined. 11272b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov Intent callbackIntent = new Intent(context, callbackActivity); 11282b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov callbackIntent.setAction(callbackAction); 11292b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent); 11302b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov 11312b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov return serviceIntent; 11322b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov } 11332b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov 1134d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell /** 1135d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell * Creates an intent to join all raw contacts inside {@param contactIds}'s contacts. 1136d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell * No special attention is paid to where the resulting contact's name is taken from. 1137d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell */ 1138d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell public static Intent createJoinSeveralContactsIntent(Context context, long[] contactIds) { 1139d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell Intent serviceIntent = new Intent(context, ContactSaveService.class); 1140d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell serviceIntent.setAction(ContactSaveService.ACTION_JOIN_SEVERAL_CONTACTS); 1141d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_IDS, contactIds); 1142d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell return serviceIntent; 1143d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell } 1144d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell 11452b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov 11462b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov private interface JoinContactQuery { 11472b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov String[] PROJECTION = { 11482b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov RawContacts._ID, 11492b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov RawContacts.CONTACT_ID, 11502b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov RawContacts.DISPLAY_NAME_SOURCE, 11512b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov }; 11522b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov 11532b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov int _ID = 0; 11542b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov int CONTACT_ID = 1; 1155548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell int DISPLAY_NAME_SOURCE = 2; 1156548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell } 1157548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell 1158548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell private interface ContactEntityQuery { 1159548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell String[] PROJECTION = { 1160548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell Contacts.Entity.DATA_ID, 1161548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell Contacts.Entity.CONTACT_ID, 1162548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell Contacts.Entity.IS_SUPER_PRIMARY, 1163548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell }; 1164548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell String SELECTION = Data.MIMETYPE + " = '" + StructuredName.CONTENT_ITEM_TYPE + "'" + 1165548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell " AND " + StructuredName.DISPLAY_NAME + "=" + Contacts.DISPLAY_NAME + 1166548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell " AND " + StructuredName.DISPLAY_NAME + " IS NOT NULL " + 1167548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell " AND " + StructuredName.DISPLAY_NAME + " != '' "; 1168548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell 1169548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell int DATA_ID = 0; 1170548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell int CONTACT_ID = 1; 1171548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell int IS_SUPER_PRIMARY = 2; 11722b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov } 11732b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov 1174d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell private void joinSeveralContacts(Intent intent) { 1175d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell final long[] contactIds = intent.getLongArrayExtra(EXTRA_CONTACT_IDS); 1176548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell 1177d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell // Load raw contact IDs for all contacts involved. 1178d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell long rawContactIds[] = getRawContactIdsForAggregation(contactIds); 1179d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell if (rawContactIds == null) { 1180d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell Log.e(TAG, "Invalid arguments for joinSeveralContacts request"); 11812b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov return; 11822b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov } 11832b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov 1184d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell // For each pair of raw contacts, insert an aggregation exception 11852b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov final ContentResolver resolver = getContentResolver(); 11860653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang // The maximum number of operations per batch (aka yield point) is 500. See b/22480225 11870653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang final int batchSize = MAX_CONTACTS_PROVIDER_BATCH_SIZE; 11880653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang final ArrayList<ContentProviderOperation> operations = new ArrayList<>(batchSize); 1189d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell for (int i = 0; i < rawContactIds.length; i++) { 1190d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell for (int j = 0; j < rawContactIds.length; j++) { 1191d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell if (i != j) { 1192d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell buildJoinContactDiff(operations, rawContactIds[i], rawContactIds[j]); 1193d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell } 11940653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang // Before we get to 500 we need to flush the operations list 11950653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang if (operations.size() > 0 && operations.size() % batchSize == 0) { 11960653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang if (!applyJoinOperations(resolver, operations)) { 11970653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang return; 11980653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang } 11990653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang operations.clear(); 12000653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang } 1201d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell } 1202d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell } 12030653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang if (operations.size() > 0 && !applyJoinOperations(resolver, operations)) { 12040653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang return; 12050653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang } 12060653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang showToast(R.string.contactsJoinedMessage); 12070653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang } 1208d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell 12090653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang /** Returns true if the batch was successfully applied and false otherwise. */ 12100653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang private boolean applyJoinOperations(ContentResolver resolver, 12110653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang ArrayList<ContentProviderOperation> operations) { 1212d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell try { 1213d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell resolver.applyBatch(ContactsContract.AUTHORITY, operations); 12140653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang return true; 1215d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell } catch (RemoteException | OperationApplicationException e) { 1216d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell Log.e(TAG, "Failed to apply aggregation exception batch", e); 1217d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell showToast(R.string.contactSavedErrorToast); 12180653de3dbac5b4f4a41332f09a2275a96dab4c8eWalter Jang return false; 1219d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell } 1220d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell } 1221d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell 1222d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell 1223d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell private void joinContacts(Intent intent) { 1224d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell long contactId1 = intent.getLongExtra(EXTRA_CONTACT_ID1, -1); 1225d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell long contactId2 = intent.getLongExtra(EXTRA_CONTACT_ID2, -1); 12262b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov 12272b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov // Load raw contact IDs for all raw contacts involved - currently edited and selected 1228548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell // in the join UIs. 1229548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell long rawContactIds[] = getRawContactIdsForAggregation(contactId1, contactId2); 1230548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell if (rawContactIds == null) { 1231d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell Log.e(TAG, "Invalid arguments for joinContacts request"); 1232c12a280b496e6a997ab972641fb8e50e1eb8736cJay Shrauner return; 1233c12a280b496e6a997ab972641fb8e50e1eb8736cJay Shrauner } 12342b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov 1235548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>(); 12362b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov 12372b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov // For each pair of raw contacts, insert an aggregation exception 12382b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov for (int i = 0; i < rawContactIds.length; i++) { 12392b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov for (int j = 0; j < rawContactIds.length; j++) { 12402b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov if (i != j) { 12412b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov buildJoinContactDiff(operations, rawContactIds[i], rawContactIds[j]); 12422b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov } 12432b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov } 12442b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov } 12452b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov 1246d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell final ContentResolver resolver = getContentResolver(); 1247d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell 1248548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell // Use the name for contactId1 as the name for the newly aggregated contact. 1249548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell final Uri contactId1Uri = ContentUris.withAppendedId( 1250548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell Contacts.CONTENT_URI, contactId1); 1251548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell final Uri entityUri = Uri.withAppendedPath( 1252548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell contactId1Uri, Contacts.Entity.CONTENT_DIRECTORY); 1253548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell Cursor c = resolver.query(entityUri, 1254548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell ContactEntityQuery.PROJECTION, ContactEntityQuery.SELECTION, null, null); 1255548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell if (c == null) { 1256548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell Log.e(TAG, "Unable to open Contacts DB cursor"); 1257548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell showToast(R.string.contactSavedErrorToast); 1258548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell return; 1259548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell } 1260548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell long dataIdToAddSuperPrimary = -1; 1261548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell try { 1262548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell if (c.moveToFirst()) { 1263548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell dataIdToAddSuperPrimary = c.getLong(ContactEntityQuery.DATA_ID); 1264548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell } 1265548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell } finally { 1266548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell c.close(); 1267548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell } 1268548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell 1269548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell // Mark the name from contactId1 IS_SUPER_PRIMARY to make sure that the contact 1270548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell // display name does not change as a result of the join. 1271548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell if (dataIdToAddSuperPrimary != -1) { 12722b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov Builder builder = ContentProviderOperation.newUpdate( 1273548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell ContentUris.withAppendedId(Data.CONTENT_URI, dataIdToAddSuperPrimary)); 1274548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell builder.withValue(Data.IS_SUPER_PRIMARY, 1); 1275548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell builder.withValue(Data.IS_PRIMARY, 1); 12762b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov operations.add(builder.build()); 12772b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov } 12782b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov 12792b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov boolean success = false; 12802b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov // Apply all aggregation exceptions as one batch 12812b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov try { 12822b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov resolver.applyBatch(ContactsContract.AUTHORITY, operations); 1283886d3d6de4a6f7202b1871f5af9944e8650413dfDmitri Plotnikov showToast(R.string.contactsJoinedMessage); 12842b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov success = true; 1285d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell } catch (RemoteException | OperationApplicationException e) { 12862b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov Log.e(TAG, "Failed to apply aggregation exception batch", e); 1287886d3d6de4a6f7202b1871f5af9944e8650413dfDmitri Plotnikov showToast(R.string.contactSavedErrorToast); 12882b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov } 12892b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov 12902b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT); 12912b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov if (success) { 12922b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov Uri uri = RawContacts.getContactLookupUri(resolver, 12932b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactIds[0])); 12942b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov callbackIntent.setData(uri); 12952b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov } 12963a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov deliverCallback(callbackIntent); 12972b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov } 12982b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov 1299d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell private long[] getRawContactIdsForAggregation(long[] contactIds) { 1300d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell if (contactIds == null) { 1301d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell return null; 1302d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell } 1303d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell 1304548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell final ContentResolver resolver = getContentResolver(); 1305548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell long rawContactIds[]; 1306d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell 1307d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell final StringBuilder queryBuilder = new StringBuilder(); 1308d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell final String stringContactIds[] = new String[contactIds.length]; 1309d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell for (int i = 0; i < contactIds.length; i++) { 1310d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell queryBuilder.append(RawContacts.CONTACT_ID + "=?"); 1311d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell stringContactIds[i] = String.valueOf(contactIds[i]); 1312d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell if (contactIds[i] == -1) { 1313d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell return null; 1314d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell } 1315d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell if (i == contactIds.length -1) { 1316d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell break; 1317d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell } 1318d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell queryBuilder.append(" OR "); 1319d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell } 1320d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell 1321548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell final Cursor c = resolver.query(RawContacts.CONTENT_URI, 1322548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell JoinContactQuery.PROJECTION, 1323d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell queryBuilder.toString(), 1324d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell stringContactIds, null); 1325548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell if (c == null) { 1326548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell Log.e(TAG, "Unable to open Contacts DB cursor"); 1327548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell showToast(R.string.contactSavedErrorToast); 1328548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell return null; 1329548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell } 1330548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell try { 1331548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell if (c.getCount() < 2) { 1332d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell Log.e(TAG, "Not enough raw contacts to aggregate together."); 1333548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell return null; 1334548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell } 1335548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell rawContactIds = new long[c.getCount()]; 1336548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell for (int i = 0; i < rawContactIds.length; i++) { 1337548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell c.moveToPosition(i); 1338548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell long rawContactId = c.getLong(JoinContactQuery._ID); 1339548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell rawContactIds[i] = rawContactId; 1340548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell } 1341548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell } finally { 1342548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell c.close(); 1343548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell } 1344548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell return rawContactIds; 1345548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell } 1346548f5c658547041feb50cdfbe6f48e900558c00cBrian Attwell 1347d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell private long[] getRawContactIdsForAggregation(long contactId1, long contactId2) { 1348d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell return getRawContactIdsForAggregation(new long[] {contactId1, contactId2}); 1349d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell } 1350d3946cae17273ed1c2fceb507990882e3f828ba9Brian Attwell 13512b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov /** 13522b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov * Construct a {@link AggregationExceptions#TYPE_KEEP_TOGETHER} ContentProviderOperation. 13532b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov */ 13542b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov private void buildJoinContactDiff(ArrayList<ContentProviderOperation> operations, 13552b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov long rawContactId1, long rawContactId2) { 13562b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov Builder builder = 13572b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov ContentProviderOperation.newUpdate(AggregationExceptions.CONTENT_URI); 13582b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov builder.withValue(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_TOGETHER); 13592b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1); 13602b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov builder.withValue(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2); 13612b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov operations.add(builder.build()); 13622b46f0301c51973d6e3b02b78b398af097244af9Dmitri Plotnikov } 1363886d3d6de4a6f7202b1871f5af9944e8650413dfDmitri Plotnikov 1364886d3d6de4a6f7202b1871f5af9944e8650413dfDmitri Plotnikov /** 1365886d3d6de4a6f7202b1871f5af9944e8650413dfDmitri Plotnikov * Shows a toast on the UI thread. 1366886d3d6de4a6f7202b1871f5af9944e8650413dfDmitri Plotnikov */ 1367886d3d6de4a6f7202b1871f5af9944e8650413dfDmitri Plotnikov private void showToast(final int message) { 13683a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov mMainHandler.post(new Runnable() { 1369886d3d6de4a6f7202b1871f5af9944e8650413dfDmitri Plotnikov 1370886d3d6de4a6f7202b1871f5af9944e8650413dfDmitri Plotnikov @Override 1371886d3d6de4a6f7202b1871f5af9944e8650413dfDmitri Plotnikov public void run() { 1372886d3d6de4a6f7202b1871f5af9944e8650413dfDmitri Plotnikov Toast.makeText(ContactSaveService.this, message, Toast.LENGTH_LONG).show(); 1373886d3d6de4a6f7202b1871f5af9944e8650413dfDmitri Plotnikov } 1374886d3d6de4a6f7202b1871f5af9944e8650413dfDmitri Plotnikov }); 1375886d3d6de4a6f7202b1871f5af9944e8650413dfDmitri Plotnikov } 13763a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov 13773a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov private void deliverCallback(final Intent callbackIntent) { 13783a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov mMainHandler.post(new Runnable() { 13793a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov 13803a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov @Override 13813a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov public void run() { 13823a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov deliverCallbackOnUiThread(callbackIntent); 13833a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov } 13843a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov }); 13853a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov } 13863a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov 13873a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov void deliverCallbackOnUiThread(final Intent callbackIntent) { 13883a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov // TODO: this assumes that if there are multiple instances of the same 13893a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov // activity registered, the last one registered is the one waiting for 13903a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov // the callback. Validity of this assumption needs to be verified. 1391a831c0b539cdc120655856074d4621e8e60a843bHugo Hudson for (Listener listener : sListeners) { 1392a831c0b539cdc120655856074d4621e8e60a843bHugo Hudson if (callbackIntent.getComponent().equals( 1393a831c0b539cdc120655856074d4621e8e60a843bHugo Hudson ((Activity) listener).getIntent().getComponent())) { 1394a831c0b539cdc120655856074d4621e8e60a843bHugo Hudson listener.onServiceCompleted(callbackIntent); 1395a831c0b539cdc120655856074d4621e8e60a843bHugo Hudson return; 13963a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov } 13973a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov } 13983a6a905a39e6cbb3b2dc99835cdf28c84437691aDmitri Plotnikov } 1399173ffe1300afa6a88a7f0a924adade121e564274Daniel Lehmann} 1400