blob: 775009e04e7d3a9dcac1903b46860bd554e77a66 [file] [log] [blame]
Daniel Lehmann173ffe12010-06-14 18:19:10 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
Dmitri Plotnikov18ffaa22010-12-03 14:28:00 -080017package com.android.contacts;
Daniel Lehmann173ffe12010-06-14 18:19:10 -070018
Dmitri Plotnikova0114142011-02-15 13:53:21 -080019import com.android.contacts.model.AccountTypeManager;
20import com.android.contacts.model.EntityDeltaList;
21import com.android.contacts.model.EntityModifier;
Dmitri Plotnikov1ac58b62010-11-19 16:12:09 -080022import com.google.android.collect.Lists;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070023import com.google.android.collect.Sets;
24
25import android.accounts.Account;
Dmitri Plotnikov3a6a9052011-03-02 10:14:43 -080026import android.app.Activity;
Daniel Lehmann173ffe12010-06-14 18:19:10 -070027import android.app.IntentService;
28import android.content.ContentProviderOperation;
Dmitri Plotnikov2b46f032010-11-29 16:41:43 -080029import android.content.ContentProviderOperation.Builder;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070030import android.content.ContentProviderResult;
31import android.content.ContentResolver;
Dmitri Plotnikove898a9f2010-11-18 16:58:25 -080032import android.content.ContentUris;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070033import android.content.ContentValues;
Dmitri Plotnikov9d730dd2010-11-24 13:22:23 -080034import android.content.Context;
Daniel Lehmann173ffe12010-06-14 18:19:10 -070035import android.content.Intent;
Dmitri Plotnikov2b46f032010-11-29 16:41:43 -080036import android.content.OperationApplicationException;
37import android.database.Cursor;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070038import android.net.Uri;
Dmitri Plotnikov886d3d62011-01-03 10:08:47 -080039import android.os.Handler;
40import android.os.Looper;
Dmitri Plotnikova0114142011-02-15 13:53:21 -080041import android.os.Parcelable;
Dmitri Plotnikov2b46f032010-11-29 16:41:43 -080042import android.os.RemoteException;
Daniel Lehmann173ffe12010-06-14 18:19:10 -070043import android.provider.ContactsContract;
Dmitri Plotnikov2b46f032010-11-29 16:41:43 -080044import android.provider.ContactsContract.AggregationExceptions;
Dmitri Plotnikov1ac58b62010-11-19 16:12:09 -080045import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
Dmitri Plotnikov9d730dd2010-11-24 13:22:23 -080046import android.provider.ContactsContract.Contacts;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070047import android.provider.ContactsContract.Data;
Dmitri Plotnikove898a9f2010-11-18 16:58:25 -080048import android.provider.ContactsContract.Groups;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070049import android.provider.ContactsContract.RawContacts;
Daniel Lehmann173ffe12010-06-14 18:19:10 -070050import android.util.Log;
Dmitri Plotnikov2b46f032010-11-29 16:41:43 -080051import android.widget.Toast;
Daniel Lehmann173ffe12010-06-14 18:19:10 -070052
53import java.util.ArrayList;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070054import java.util.HashSet;
Dmitri Plotnikov3a6a9052011-03-02 10:14:43 -080055import java.util.LinkedList;
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070056import java.util.List;
Daniel Lehmann173ffe12010-06-14 18:19:10 -070057
Dmitri Plotnikov18ffaa22010-12-03 14:28:00 -080058/**
59 * A service responsible for saving changes to the content provider.
60 */
Daniel Lehmann173ffe12010-06-14 18:19:10 -070061public class ContactSaveService extends IntentService {
62 private static final String TAG = "ContactSaveService";
63
Katherine Kuana007e442011-07-07 09:25:34 -070064 /** Set to true in order to view logs on content provider operations */
65 private static final boolean DEBUG = false;
66
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -070067 public static final String ACTION_NEW_RAW_CONTACT = "newRawContact";
68
69 public static final String EXTRA_ACCOUNT_NAME = "accountName";
70 public static final String EXTRA_ACCOUNT_TYPE = "accountType";
71 public static final String EXTRA_CONTENT_VALUES = "contentValues";
72 public static final String EXTRA_CALLBACK_INTENT = "callbackIntent";
73
Dmitri Plotnikova0114142011-02-15 13:53:21 -080074 public static final String ACTION_SAVE_CONTACT = "saveContact";
75 public static final String EXTRA_CONTACT_STATE = "state";
76 public static final String EXTRA_SAVE_MODE = "saveMode";
Daniel Lehmann173ffe12010-06-14 18:19:10 -070077
Dmitri Plotnikov1ac58b62010-11-19 16:12:09 -080078 public static final String ACTION_CREATE_GROUP = "createGroup";
Dmitri Plotnikove898a9f2010-11-18 16:58:25 -080079 public static final String ACTION_RENAME_GROUP = "renameGroup";
80 public static final String ACTION_DELETE_GROUP = "deleteGroup";
Katherine Kuan2d851cc2011-07-05 16:23:27 -070081 public static final String ACTION_UPDATE_GROUP = "updateGroup";
Dmitri Plotnikove898a9f2010-11-18 16:58:25 -080082 public static final String EXTRA_GROUP_ID = "groupId";
83 public static final String EXTRA_GROUP_LABEL = "groupLabel";
Katherine Kuan2d851cc2011-07-05 16:23:27 -070084 public static final String EXTRA_RAW_CONTACTS_TO_ADD = "rawContactsToAdd";
85 public static final String EXTRA_RAW_CONTACTS_TO_REMOVE = "rawContactsToRemove";
Dmitri Plotnikove898a9f2010-11-18 16:58:25 -080086
Dmitri Plotnikov9d730dd2010-11-24 13:22:23 -080087 public static final String ACTION_SET_STARRED = "setStarred";
Dmitri Plotnikov7d8cabb2010-11-24 17:40:01 -080088 public static final String ACTION_DELETE_CONTACT = "delete";
Dmitri Plotnikov9d730dd2010-11-24 13:22:23 -080089 public static final String EXTRA_CONTACT_URI = "contactUri";
90 public static final String EXTRA_STARRED_FLAG = "starred";
91
Daniel Lehmann0f78e8b2010-11-24 17:32:03 -080092 public static final String ACTION_SET_SUPER_PRIMARY = "setSuperPrimary";
93 public static final String ACTION_CLEAR_PRIMARY = "clearPrimary";
94 public static final String EXTRA_DATA_ID = "dataId";
95
Dmitri Plotnikov2b46f032010-11-29 16:41:43 -080096 public static final String ACTION_JOIN_CONTACTS = "joinContacts";
97 public static final String EXTRA_CONTACT_ID1 = "contactId1";
98 public static final String EXTRA_CONTACT_ID2 = "contactId2";
99 public static final String EXTRA_CONTACT_WRITABLE = "contactWritable";
100
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700101 private static final HashSet<String> ALLOWED_DATA_COLUMNS = Sets.newHashSet(
102 Data.MIMETYPE,
103 Data.IS_PRIMARY,
104 Data.DATA1,
105 Data.DATA2,
106 Data.DATA3,
107 Data.DATA4,
108 Data.DATA5,
109 Data.DATA6,
110 Data.DATA7,
111 Data.DATA8,
112 Data.DATA9,
113 Data.DATA10,
114 Data.DATA11,
115 Data.DATA12,
116 Data.DATA13,
117 Data.DATA14,
118 Data.DATA15
119 );
120
Dmitri Plotnikova0114142011-02-15 13:53:21 -0800121 private static final int PERSIST_TRIES = 3;
122
Dmitri Plotnikov3a6a9052011-03-02 10:14:43 -0800123 public interface Listener {
124 public void onServiceCompleted(Intent callbackIntent);
125 }
126
127 private static final LinkedList<Listener> sListeners = new LinkedList<Listener>();
128
129 private Handler mMainHandler;
130
Daniel Lehmann173ffe12010-06-14 18:19:10 -0700131 public ContactSaveService() {
132 super(TAG);
133 setIntentRedelivery(true);
Dmitri Plotnikov3a6a9052011-03-02 10:14:43 -0800134 mMainHandler = new Handler(Looper.getMainLooper());
135 }
136
137 public static void registerListener(Listener listener) {
138 if (!(listener instanceof Activity)) {
139 throw new ClassCastException("Only activities can be registered to"
140 + " receive callback from " + ContactSaveService.class.getName());
141 }
142 synchronized (sListeners) {
143 sListeners.addFirst(listener);
144 }
145 }
146
147 public static void unregisterListener(Listener listener) {
148 synchronized (sListeners) {
149 sListeners.remove(listener);
150 }
Daniel Lehmann173ffe12010-06-14 18:19:10 -0700151 }
152
153 @Override
Dmitri Plotnikova0114142011-02-15 13:53:21 -0800154 public Object getSystemService(String name) {
155 Object service = super.getSystemService(name);
156 if (service != null) {
157 return service;
158 }
159
160 return getApplicationContext().getSystemService(name);
161 }
162
163 @Override
Daniel Lehmann173ffe12010-06-14 18:19:10 -0700164 protected void onHandleIntent(Intent intent) {
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700165 String action = intent.getAction();
166 if (ACTION_NEW_RAW_CONTACT.equals(action)) {
167 createRawContact(intent);
Dmitri Plotnikova0114142011-02-15 13:53:21 -0800168 } else if (ACTION_SAVE_CONTACT.equals(action)) {
169 saveContact(intent);
Dmitri Plotnikov1ac58b62010-11-19 16:12:09 -0800170 } else if (ACTION_CREATE_GROUP.equals(action)) {
171 createGroup(intent);
Dmitri Plotnikove898a9f2010-11-18 16:58:25 -0800172 } else if (ACTION_RENAME_GROUP.equals(action)) {
173 renameGroup(intent);
174 } else if (ACTION_DELETE_GROUP.equals(action)) {
175 deleteGroup(intent);
Katherine Kuan2d851cc2011-07-05 16:23:27 -0700176 } else if (ACTION_UPDATE_GROUP.equals(action)) {
177 updateGroup(intent);
Dmitri Plotnikov9d730dd2010-11-24 13:22:23 -0800178 } else if (ACTION_SET_STARRED.equals(action)) {
179 setStarred(intent);
Daniel Lehmann0f78e8b2010-11-24 17:32:03 -0800180 } else if (ACTION_SET_SUPER_PRIMARY.equals(action)) {
181 setSuperPrimary(intent);
182 } else if (ACTION_CLEAR_PRIMARY.equals(action)) {
183 clearPrimary(intent);
Dmitri Plotnikov7d8cabb2010-11-24 17:40:01 -0800184 } else if (ACTION_DELETE_CONTACT.equals(action)) {
185 deleteContact(intent);
Dmitri Plotnikov2b46f032010-11-29 16:41:43 -0800186 } else if (ACTION_JOIN_CONTACTS.equals(action)) {
187 joinContacts(intent);
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700188 }
189 }
190
Dmitri Plotnikov9d730dd2010-11-24 13:22:23 -0800191 /**
192 * Creates an intent that can be sent to this service to create a new raw contact
193 * using data presented as a set of ContentValues.
194 */
195 public static Intent createNewRawContactIntent(Context context,
196 ArrayList<ContentValues> values, Account account, Class<?> callbackActivity,
197 String callbackAction) {
198 Intent serviceIntent = new Intent(
199 context, ContactSaveService.class);
200 serviceIntent.setAction(ContactSaveService.ACTION_NEW_RAW_CONTACT);
201 if (account != null) {
202 serviceIntent.putExtra(ContactSaveService.EXTRA_ACCOUNT_NAME, account.name);
203 serviceIntent.putExtra(ContactSaveService.EXTRA_ACCOUNT_TYPE, account.type);
204 }
205 serviceIntent.putParcelableArrayListExtra(
206 ContactSaveService.EXTRA_CONTENT_VALUES, values);
207
208 // Callback intent will be invoked by the service once the new contact is
209 // created. The service will put the URI of the new contact as "data" on
210 // the callback intent.
211 Intent callbackIntent = new Intent(context, callbackActivity);
212 callbackIntent.setAction(callbackAction);
Dmitri Plotnikov9d730dd2010-11-24 13:22:23 -0800213 serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent);
214 return serviceIntent;
215 }
216
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700217 private void createRawContact(Intent intent) {
218 String accountName = intent.getStringExtra(EXTRA_ACCOUNT_NAME);
219 String accountType = intent.getStringExtra(EXTRA_ACCOUNT_TYPE);
220 List<ContentValues> valueList = intent.getParcelableArrayListExtra(EXTRA_CONTENT_VALUES);
221 Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
222
223 ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
224 operations.add(ContentProviderOperation.newInsert(RawContacts.CONTENT_URI)
225 .withValue(RawContacts.ACCOUNT_NAME, accountName)
226 .withValue(RawContacts.ACCOUNT_TYPE, accountType)
227 .build());
228
229 int size = valueList.size();
230 for (int i = 0; i < size; i++) {
231 ContentValues values = valueList.get(i);
232 values.keySet().retainAll(ALLOWED_DATA_COLUMNS);
233 operations.add(ContentProviderOperation.newInsert(Data.CONTENT_URI)
234 .withValueBackReference(Data.RAW_CONTACT_ID, 0)
235 .withValues(values)
236 .build());
237 }
238
239 ContentResolver resolver = getContentResolver();
240 ContentProviderResult[] results;
241 try {
242 results = resolver.applyBatch(ContactsContract.AUTHORITY, operations);
243 } catch (Exception e) {
244 throw new RuntimeException("Failed to store new contact", e);
245 }
246
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700247 Uri rawContactUri = results[0].uri;
248 callbackIntent.setData(RawContacts.getContactLookupUri(resolver, rawContactUri));
249
Dmitri Plotnikov3a6a9052011-03-02 10:14:43 -0800250 deliverCallback(callbackIntent);
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700251 }
252
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700253 /**
Dmitri Plotnikova0114142011-02-15 13:53:21 -0800254 * Creates an intent that can be sent to this service to create a new raw contact
255 * using data presented as a set of ContentValues.
256 */
257 public static Intent createSaveContactIntent(Context context, EntityDeltaList state,
258 String saveModeExtraKey, int saveMode, Class<?> callbackActivity,
259 String callbackAction) {
260 Intent serviceIntent = new Intent(
261 context, ContactSaveService.class);
262 serviceIntent.setAction(ContactSaveService.ACTION_SAVE_CONTACT);
263 serviceIntent.putExtra(EXTRA_CONTACT_STATE, (Parcelable) state);
264
265 // Callback intent will be invoked by the service once the contact is
266 // saved. The service will put the URI of the new contact as "data" on
267 // the callback intent.
268 Intent callbackIntent = new Intent(context, callbackActivity);
269 callbackIntent.putExtra(saveModeExtraKey, saveMode);
270 callbackIntent.setAction(callbackAction);
Dmitri Plotnikova0114142011-02-15 13:53:21 -0800271 serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent);
272 return serviceIntent;
273 }
274
275 private void saveContact(Intent intent) {
276 EntityDeltaList state = intent.getParcelableExtra(EXTRA_CONTACT_STATE);
277 Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
278
279 // Trim any empty fields, and RawContacts, before persisting
280 final AccountTypeManager accountTypes = AccountTypeManager.getInstance(this);
281 EntityModifier.trimEmpty(state, accountTypes);
282
283 Uri lookupUri = null;
284
285 final ContentResolver resolver = getContentResolver();
286
287 // Attempt to persist changes
288 int tries = 0;
289 while (tries++ < PERSIST_TRIES) {
290 try {
291 // Build operations and try applying
292 final ArrayList<ContentProviderOperation> diff = state.buildDiff();
Katherine Kuana007e442011-07-07 09:25:34 -0700293 if (DEBUG) {
294 Log.v(TAG, "Content Provider Operations:");
295 for (ContentProviderOperation operation : diff) {
296 Log.v(TAG, operation.toString());
297 }
298 }
299
Dmitri Plotnikova0114142011-02-15 13:53:21 -0800300 ContentProviderResult[] results = null;
301 if (!diff.isEmpty()) {
302 results = resolver.applyBatch(ContactsContract.AUTHORITY, diff);
303 }
304
305 final long rawContactId = getRawContactId(state, diff, results);
306 if (rawContactId == -1) {
307 throw new IllegalStateException("Could not determine RawContact ID after save");
308 }
309 final Uri rawContactUri = ContentUris.withAppendedId(
310 RawContacts.CONTENT_URI, rawContactId);
311 lookupUri = RawContacts.getContactLookupUri(resolver, rawContactUri);
312 Log.v(TAG, "Saved contact. New URI: " + lookupUri);
313 break;
314
315 } catch (RemoteException e) {
316 // Something went wrong, bail without success
317 Log.e(TAG, "Problem persisting user edits", e);
318 break;
319
320 } catch (OperationApplicationException e) {
321 // Version consistency failed, re-parent change and try again
322 Log.w(TAG, "Version consistency failed, re-parenting: " + e.toString());
323 final StringBuilder sb = new StringBuilder(RawContacts._ID + " IN(");
324 boolean first = true;
325 final int count = state.size();
326 for (int i = 0; i < count; i++) {
327 Long rawContactId = state.getRawContactId(i);
328 if (rawContactId != null && rawContactId != -1) {
329 if (!first) {
330 sb.append(',');
331 }
332 sb.append(rawContactId);
333 first = false;
334 }
335 }
336 sb.append(")");
337
338 if (first) {
339 throw new IllegalStateException("Version consistency failed for a new contact");
340 }
341
342 final EntityDeltaList newState = EntityDeltaList.fromQuery(resolver,
343 sb.toString(), null, null);
344 state = EntityDeltaList.mergeAfter(newState, state);
345 }
346 }
347
348 callbackIntent.setData(lookupUri);
349
Dmitri Plotnikov3a6a9052011-03-02 10:14:43 -0800350 deliverCallback(callbackIntent);
Dmitri Plotnikova0114142011-02-15 13:53:21 -0800351 }
352
353 private long getRawContactId(EntityDeltaList state,
354 final ArrayList<ContentProviderOperation> diff,
355 final ContentProviderResult[] results) {
356 long rawContactId = state.findRawContactId();
357 if (rawContactId != -1) {
358 return rawContactId;
359 }
360
361 final int diffSize = diff.size();
362 for (int i = 0; i < diffSize; i++) {
363 ContentProviderOperation operation = diff.get(i);
364 if (operation.getType() == ContentProviderOperation.TYPE_INSERT
365 && operation.getUri().getEncodedPath().contains(
366 RawContacts.CONTENT_URI.getEncodedPath())) {
367 return ContentUris.parseId(results[i].uri);
368 }
369 }
370 return -1;
371 }
372
373 /**
Katherine Kuan717e3432011-07-13 17:03:24 -0700374 * Creates an intent that can be sent to this service to create a new group as
375 * well as add new members at the same time.
376 *
377 * @param context of the application
378 * @param account in which the group should be created
379 * @param label is the name of the group (cannot be null)
380 * @param rawContactsToAdd is an array of raw contact IDs for contacts that
381 * should be added to the group
382 * @param callbackActivity is the activity to send the callback intent to
383 * @param callbackAction is the intent action for the callback intent
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700384 */
Katherine Kuan717e3432011-07-13 17:03:24 -0700385 public static Intent createNewGroupIntent(Context context, Account account,
386 String label, long[] rawContactsToAdd, Class<?> callbackActivity,
387 String callbackAction) {
Dmitri Plotnikov9d730dd2010-11-24 13:22:23 -0800388 Intent serviceIntent = new Intent(context, ContactSaveService.class);
389 serviceIntent.setAction(ContactSaveService.ACTION_CREATE_GROUP);
390 serviceIntent.putExtra(ContactSaveService.EXTRA_ACCOUNT_TYPE, account.type);
391 serviceIntent.putExtra(ContactSaveService.EXTRA_ACCOUNT_NAME, account.name);
392 serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_LABEL, label);
Katherine Kuan717e3432011-07-13 17:03:24 -0700393 serviceIntent.putExtra(ContactSaveService.EXTRA_RAW_CONTACTS_TO_ADD, rawContactsToAdd);
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700394
Dmitri Plotnikov9d730dd2010-11-24 13:22:23 -0800395 // Callback intent will be invoked by the service once the new group is
Katherine Kuan717e3432011-07-13 17:03:24 -0700396 // created.
Dmitri Plotnikov9d730dd2010-11-24 13:22:23 -0800397 Intent callbackIntent = new Intent(context, callbackActivity);
398 callbackIntent.setAction(callbackAction);
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700399 serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent);
Dmitri Plotnikov9d730dd2010-11-24 13:22:23 -0800400
Dmitri Plotnikovcaf0bc72010-09-03 15:16:21 -0700401 return serviceIntent;
402 }
Dmitri Plotnikove898a9f2010-11-18 16:58:25 -0800403
Dmitri Plotnikov1ac58b62010-11-19 16:12:09 -0800404 private void createGroup(Intent intent) {
Katherine Kuan717e3432011-07-13 17:03:24 -0700405 final String accountType = intent.getStringExtra(EXTRA_ACCOUNT_TYPE);
406 final String accountName = intent.getStringExtra(EXTRA_ACCOUNT_NAME);
407 final String label = intent.getStringExtra(EXTRA_GROUP_LABEL);
408 final long[] rawContactsToAdd = intent.getLongArrayExtra(EXTRA_RAW_CONTACTS_TO_ADD);
Dmitri Plotnikov1ac58b62010-11-19 16:12:09 -0800409
410 ContentValues values = new ContentValues();
411 values.put(Groups.ACCOUNT_TYPE, accountType);
412 values.put(Groups.ACCOUNT_NAME, accountName);
413 values.put(Groups.TITLE, label);
414
Katherine Kuan717e3432011-07-13 17:03:24 -0700415 final ContentResolver resolver = getContentResolver();
416
417 // Create the new group
418 final Uri groupUri = resolver.insert(Groups.CONTENT_URI, values);
419
420 // If there's no URI, then the insertion failed. Abort early because group members can't be
421 // added if the group doesn't exist
Dmitri Plotnikov1ac58b62010-11-19 16:12:09 -0800422 if (groupUri == null) {
Katherine Kuan717e3432011-07-13 17:03:24 -0700423 Log.e(TAG, "Couldn't create group with label " + label);
Dmitri Plotnikov1ac58b62010-11-19 16:12:09 -0800424 return;
425 }
426
Katherine Kuan717e3432011-07-13 17:03:24 -0700427 // Add new group members
428 addMembersToGroup(resolver, rawContactsToAdd, ContentUris.parseId(groupUri));
429
430 // TODO: Move this into the contact editor where it belongs. This needs to be integrated
431 // with the way other intent extras that are passed to the {@link ContactEditorActivity}.
Dmitri Plotnikov1ac58b62010-11-19 16:12:09 -0800432 values.clear();
433 values.put(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
434 values.put(GroupMembership.GROUP_ROW_ID, ContentUris.parseId(groupUri));
435
436 Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
Katherine Kuanc6b8afe2011-06-22 19:03:50 -0700437 callbackIntent.setData(groupUri);
Katherine Kuan717e3432011-07-13 17:03:24 -0700438 // TODO: This can be taken out when the above TODO is addressed
Dmitri Plotnikov1ac58b62010-11-19 16:12:09 -0800439 callbackIntent.putExtra(ContactsContract.Intents.Insert.DATA, Lists.newArrayList(values));
Dmitri Plotnikov3a6a9052011-03-02 10:14:43 -0800440 deliverCallback(callbackIntent);
Dmitri Plotnikov1ac58b62010-11-19 16:12:09 -0800441 }
442
443 /**
Dmitri Plotnikov9d730dd2010-11-24 13:22:23 -0800444 * Creates an intent that can be sent to this service to rename a group.
Dmitri Plotnikov1ac58b62010-11-19 16:12:09 -0800445 */
Katherine Kuanc6b8afe2011-06-22 19:03:50 -0700446 public static Intent createGroupRenameIntent(Context context, long groupId, String newLabel,
447 Class<?> callbackActivity, String callbackAction) {
Dmitri Plotnikov9d730dd2010-11-24 13:22:23 -0800448 Intent serviceIntent = new Intent(context, ContactSaveService.class);
449 serviceIntent.setAction(ContactSaveService.ACTION_RENAME_GROUP);
450 serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_ID, groupId);
451 serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_LABEL, newLabel);
Katherine Kuanc6b8afe2011-06-22 19:03:50 -0700452
453 // Callback intent will be invoked by the service once the group is renamed.
454 Intent callbackIntent = new Intent(context, callbackActivity);
455 callbackIntent.setAction(callbackAction);
456 serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent);
457
Dmitri Plotnikov1ac58b62010-11-19 16:12:09 -0800458 return serviceIntent;
459 }
460
Dmitri Plotnikove898a9f2010-11-18 16:58:25 -0800461 private void renameGroup(Intent intent) {
462 long groupId = intent.getLongExtra(EXTRA_GROUP_ID, -1);
463 String label = intent.getStringExtra(EXTRA_GROUP_LABEL);
464
465 if (groupId == -1) {
466 Log.e(TAG, "Invalid arguments for renameGroup request");
467 return;
468 }
469
470 ContentValues values = new ContentValues();
471 values.put(Groups.TITLE, label);
Katherine Kuanc6b8afe2011-06-22 19:03:50 -0700472 final Uri groupUri = ContentUris.withAppendedId(Groups.CONTENT_URI, groupId);
473 getContentResolver().update(groupUri, values, null, null);
474
475 Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
476 callbackIntent.setData(groupUri);
477 deliverCallback(callbackIntent);
Dmitri Plotnikove898a9f2010-11-18 16:58:25 -0800478 }
479
480 /**
Dmitri Plotnikov9d730dd2010-11-24 13:22:23 -0800481 * Creates an intent that can be sent to this service to delete a group.
Dmitri Plotnikove898a9f2010-11-18 16:58:25 -0800482 */
Dmitri Plotnikov9d730dd2010-11-24 13:22:23 -0800483 public static Intent createGroupDeletionIntent(Context context, long groupId) {
484 Intent serviceIntent = new Intent(context, ContactSaveService.class);
485 serviceIntent.setAction(ContactSaveService.ACTION_DELETE_GROUP);
Dmitri Plotnikove898a9f2010-11-18 16:58:25 -0800486 serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_ID, groupId);
Dmitri Plotnikove898a9f2010-11-18 16:58:25 -0800487 return serviceIntent;
488 }
489
490 private void deleteGroup(Intent intent) {
491 long groupId = intent.getLongExtra(EXTRA_GROUP_ID, -1);
492 if (groupId == -1) {
493 Log.e(TAG, "Invalid arguments for deleteGroup request");
494 return;
495 }
496
497 getContentResolver().delete(
498 ContentUris.withAppendedId(Groups.CONTENT_URI, groupId), null, null);
499 }
500
501 /**
Katherine Kuan2d851cc2011-07-05 16:23:27 -0700502 * Creates an intent that can be sent to this service to rename a group as
503 * well as add and remove members from the group.
504 *
505 * @param context of the application
506 * @param groupId of the group that should be modified
507 * @param newLabel is the updated name of the group (can be null if the name
508 * should not be updated)
509 * @param rawContactsToAdd is an array of raw contact IDs for contacts that
510 * should be added to the group
511 * @param rawContactsToRemove is an array of raw contact IDs for contacts
512 * that should be removed from the group
513 * @param callbackActivity is the activity to send the callback intent to
514 * @param callbackAction is the intent action for the callback intent
515 */
516 public static Intent createGroupUpdateIntent(Context context, long groupId, String newLabel,
517 long[] rawContactsToAdd, long[] rawContactsToRemove,
518 Class<?> callbackActivity, String callbackAction) {
519 Intent serviceIntent = new Intent(context, ContactSaveService.class);
520 serviceIntent.setAction(ContactSaveService.ACTION_UPDATE_GROUP);
521 serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_ID, groupId);
522 serviceIntent.putExtra(ContactSaveService.EXTRA_GROUP_LABEL, newLabel);
523 serviceIntent.putExtra(ContactSaveService.EXTRA_RAW_CONTACTS_TO_ADD, rawContactsToAdd);
524 serviceIntent.putExtra(ContactSaveService.EXTRA_RAW_CONTACTS_TO_REMOVE,
525 rawContactsToRemove);
526
527 // Callback intent will be invoked by the service once the group is updated
528 Intent callbackIntent = new Intent(context, callbackActivity);
529 callbackIntent.setAction(callbackAction);
530 serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent);
531
532 return serviceIntent;
533 }
534
535 private void updateGroup(Intent intent) {
536 long groupId = intent.getLongExtra(EXTRA_GROUP_ID, -1);
537 String label = intent.getStringExtra(EXTRA_GROUP_LABEL);
538 long[] rawContactsToAdd = intent.getLongArrayExtra(EXTRA_RAW_CONTACTS_TO_ADD);
539 long[] rawContactsToRemove = intent.getLongArrayExtra(EXTRA_RAW_CONTACTS_TO_REMOVE);
540
541 if (groupId == -1) {
542 Log.e(TAG, "Invalid arguments for updateGroup request");
543 return;
544 }
545
546 final ContentResolver resolver = getContentResolver();
547 final Uri groupUri = ContentUris.withAppendedId(Groups.CONTENT_URI, groupId);
548
549 // Update group name if necessary
550 if (label != null) {
551 ContentValues values = new ContentValues();
552 values.put(Groups.TITLE, label);
Katherine Kuan717e3432011-07-13 17:03:24 -0700553 resolver.update(groupUri, values, null, null);
Katherine Kuan2d851cc2011-07-05 16:23:27 -0700554 }
555
Katherine Kuan717e3432011-07-13 17:03:24 -0700556 // Add and remove members if necessary
557 addMembersToGroup(resolver, rawContactsToAdd, groupId);
558 removeMembersFromGroup(resolver, rawContactsToRemove, groupId);
559
560 Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
561 callbackIntent.setData(groupUri);
562 deliverCallback(callbackIntent);
563 }
564
565 private void addMembersToGroup(ContentResolver resolver, long[] rawContactsToAdd,
566 long groupId) {
567 if (rawContactsToAdd == null) {
568 return;
569 }
Katherine Kuan2d851cc2011-07-05 16:23:27 -0700570 for (long rawContactId : rawContactsToAdd) {
571 try {
572 final ArrayList<ContentProviderOperation> rawContactOperations =
573 new ArrayList<ContentProviderOperation>();
574
575 // Build an assert operation to ensure the contact is not already in the group
576 final ContentProviderOperation.Builder assertBuilder = ContentProviderOperation
577 .newAssertQuery(Data.CONTENT_URI);
578 assertBuilder.withSelection(Data.RAW_CONTACT_ID + "=? AND " +
579 Data.MIMETYPE + "=? AND " + GroupMembership.GROUP_ROW_ID + "=?",
580 new String[] { String.valueOf(rawContactId),
581 GroupMembership.CONTENT_ITEM_TYPE, String.valueOf(groupId)});
582 assertBuilder.withExpectedCount(0);
583 rawContactOperations.add(assertBuilder.build());
584
585 // Build an insert operation to add the contact to the group
586 final ContentProviderOperation.Builder insertBuilder = ContentProviderOperation
587 .newInsert(Data.CONTENT_URI);
588 insertBuilder.withValue(Data.RAW_CONTACT_ID, rawContactId);
589 insertBuilder.withValue(Data.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE);
590 insertBuilder.withValue(GroupMembership.GROUP_ROW_ID, groupId);
591 rawContactOperations.add(insertBuilder.build());
592
593 if (DEBUG) {
594 for (ContentProviderOperation operation : rawContactOperations) {
595 Log.v(TAG, operation.toString());
596 }
597 }
598
599 // Apply batch
600 ContentProviderResult[] results = null;
601 if (!rawContactOperations.isEmpty()) {
602 results = resolver.applyBatch(ContactsContract.AUTHORITY, rawContactOperations);
603 }
604 } catch (RemoteException e) {
605 // Something went wrong, bail without success
606 Log.e(TAG, "Problem persisting user edits for raw contact ID " +
607 String.valueOf(rawContactId), e);
608 } catch (OperationApplicationException e) {
609 // The assert could have failed because the contact is already in the group,
610 // just continue to the next contact
611 Log.w(TAG, "Assert failed in adding raw contact ID " +
612 String.valueOf(rawContactId) + ". Already exists in group " +
613 String.valueOf(groupId), e);
614 }
615 }
Katherine Kuan717e3432011-07-13 17:03:24 -0700616 }
Katherine Kuan2d851cc2011-07-05 16:23:27 -0700617
Katherine Kuan717e3432011-07-13 17:03:24 -0700618 private void removeMembersFromGroup(ContentResolver resolver, long[] rawContactsToRemove,
619 long groupId) {
620 if (rawContactsToRemove == null) {
621 return;
622 }
Katherine Kuan2d851cc2011-07-05 16:23:27 -0700623 for (long rawContactId : rawContactsToRemove) {
624 // Apply the delete operation on the data row for the given raw contact's
625 // membership in the given group. If no contact matches the provided selection, then
626 // nothing will be done. Just continue to the next contact.
627 getContentResolver().delete(Data.CONTENT_URI, Data.RAW_CONTACT_ID + "=? AND " +
628 Data.MIMETYPE + "=? AND " + GroupMembership.GROUP_ROW_ID + "=?",
629 new String[] { String.valueOf(rawContactId),
630 GroupMembership.CONTENT_ITEM_TYPE, String.valueOf(groupId)});
631 }
Katherine Kuan2d851cc2011-07-05 16:23:27 -0700632 }
633
634 /**
Dmitri Plotnikov9d730dd2010-11-24 13:22:23 -0800635 * Creates an intent that can be sent to this service to star or un-star a contact.
Dmitri Plotnikove898a9f2010-11-18 16:58:25 -0800636 */
Dmitri Plotnikov9d730dd2010-11-24 13:22:23 -0800637 public static Intent createSetStarredIntent(Context context, Uri contactUri, boolean value) {
638 Intent serviceIntent = new Intent(context, ContactSaveService.class);
639 serviceIntent.setAction(ContactSaveService.ACTION_SET_STARRED);
640 serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_URI, contactUri);
641 serviceIntent.putExtra(ContactSaveService.EXTRA_STARRED_FLAG, value);
642
Dmitri Plotnikove898a9f2010-11-18 16:58:25 -0800643 return serviceIntent;
644 }
Dmitri Plotnikov9d730dd2010-11-24 13:22:23 -0800645
646 private void setStarred(Intent intent) {
647 Uri contactUri = intent.getParcelableExtra(EXTRA_CONTACT_URI);
648 boolean value = intent.getBooleanExtra(EXTRA_STARRED_FLAG, false);
649 if (contactUri == null) {
650 Log.e(TAG, "Invalid arguments for setStarred request");
651 return;
652 }
653
654 final ContentValues values = new ContentValues(1);
655 values.put(Contacts.STARRED, value);
656 getContentResolver().update(contactUri, values, null, null);
Dmitri Plotnikov9d730dd2010-11-24 13:22:23 -0800657 }
Daniel Lehmann0f78e8b2010-11-24 17:32:03 -0800658
659 /**
660 * Creates an intent that sets the selected data item as super primary (default)
661 */
662 public static Intent createSetSuperPrimaryIntent(Context context, long dataId) {
663 Intent serviceIntent = new Intent(context, ContactSaveService.class);
664 serviceIntent.setAction(ContactSaveService.ACTION_SET_SUPER_PRIMARY);
665 serviceIntent.putExtra(ContactSaveService.EXTRA_DATA_ID, dataId);
666 return serviceIntent;
667 }
668
669 private void setSuperPrimary(Intent intent) {
670 long dataId = intent.getLongExtra(EXTRA_DATA_ID, -1);
671 if (dataId == -1) {
672 Log.e(TAG, "Invalid arguments for setSuperPrimary request");
673 return;
674 }
675
676 // Update the primary values in the data record.
677 ContentValues values = new ContentValues(1);
678 values.put(Data.IS_SUPER_PRIMARY, 1);
679 values.put(Data.IS_PRIMARY, 1);
680
681 getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
682 values, null, null);
683 }
684
685 /**
686 * Creates an intent that clears the primary flag of all data items that belong to the same
687 * raw_contact as the given data item. Will only clear, if the data item was primary before
688 * this call
689 */
690 public static Intent createClearPrimaryIntent(Context context, long dataId) {
691 Intent serviceIntent = new Intent(context, ContactSaveService.class);
692 serviceIntent.setAction(ContactSaveService.ACTION_CLEAR_PRIMARY);
693 serviceIntent.putExtra(ContactSaveService.EXTRA_DATA_ID, dataId);
694 return serviceIntent;
695 }
696
697 private void clearPrimary(Intent intent) {
698 long dataId = intent.getLongExtra(EXTRA_DATA_ID, -1);
699 if (dataId == -1) {
700 Log.e(TAG, "Invalid arguments for clearPrimary request");
701 return;
702 }
703
704 // Update the primary values in the data record.
705 ContentValues values = new ContentValues(1);
706 values.put(Data.IS_SUPER_PRIMARY, 0);
707 values.put(Data.IS_PRIMARY, 0);
708
709 getContentResolver().update(ContentUris.withAppendedId(Data.CONTENT_URI, dataId),
710 values, null, null);
711 }
Dmitri Plotnikov7d8cabb2010-11-24 17:40:01 -0800712
713 /**
714 * Creates an intent that can be sent to this service to delete a contact.
715 */
716 public static Intent createDeleteContactIntent(Context context, Uri contactUri) {
717 Intent serviceIntent = new Intent(context, ContactSaveService.class);
718 serviceIntent.setAction(ContactSaveService.ACTION_DELETE_CONTACT);
719 serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_URI, contactUri);
720 return serviceIntent;
721 }
722
723 private void deleteContact(Intent intent) {
724 Uri contactUri = intent.getParcelableExtra(EXTRA_CONTACT_URI);
725 if (contactUri == null) {
726 Log.e(TAG, "Invalid arguments for deleteContact request");
727 return;
728 }
729
730 getContentResolver().delete(contactUri, null, null);
731 }
Dmitri Plotnikov2b46f032010-11-29 16:41:43 -0800732
733 /**
734 * Creates an intent that can be sent to this service to join two contacts.
735 */
736 public static Intent createJoinContactsIntent(Context context, long contactId1,
737 long contactId2, boolean contactWritable,
738 Class<?> callbackActivity, String callbackAction) {
739 Intent serviceIntent = new Intent(context, ContactSaveService.class);
740 serviceIntent.setAction(ContactSaveService.ACTION_JOIN_CONTACTS);
741 serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_ID1, contactId1);
742 serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_ID2, contactId2);
743 serviceIntent.putExtra(ContactSaveService.EXTRA_CONTACT_WRITABLE, contactWritable);
744
745 // Callback intent will be invoked by the service once the contacts are joined.
746 Intent callbackIntent = new Intent(context, callbackActivity);
747 callbackIntent.setAction(callbackAction);
Dmitri Plotnikov2b46f032010-11-29 16:41:43 -0800748 serviceIntent.putExtra(ContactSaveService.EXTRA_CALLBACK_INTENT, callbackIntent);
749
750 return serviceIntent;
751 }
752
753
754 private interface JoinContactQuery {
755 String[] PROJECTION = {
756 RawContacts._ID,
757 RawContacts.CONTACT_ID,
758 RawContacts.NAME_VERIFIED,
759 RawContacts.DISPLAY_NAME_SOURCE,
760 };
761
762 String SELECTION = RawContacts.CONTACT_ID + "=? OR " + RawContacts.CONTACT_ID + "=?";
763
764 int _ID = 0;
765 int CONTACT_ID = 1;
766 int NAME_VERIFIED = 2;
767 int DISPLAY_NAME_SOURCE = 3;
768 }
769
770 private void joinContacts(Intent intent) {
771 long contactId1 = intent.getLongExtra(EXTRA_CONTACT_ID1, -1);
772 long contactId2 = intent.getLongExtra(EXTRA_CONTACT_ID2, -1);
773 boolean writable = intent.getBooleanExtra(EXTRA_CONTACT_WRITABLE, false);
774 if (contactId1 == -1 || contactId2 == -1) {
775 Log.e(TAG, "Invalid arguments for joinContacts request");
776 return;
777 }
778
779 final ContentResolver resolver = getContentResolver();
780
781 // Load raw contact IDs for all raw contacts involved - currently edited and selected
782 // in the join UIs
783 Cursor c = resolver.query(RawContacts.CONTENT_URI,
784 JoinContactQuery.PROJECTION,
785 JoinContactQuery.SELECTION,
786 new String[]{String.valueOf(contactId1), String.valueOf(contactId2)}, null);
787
788 long rawContactIds[];
789 long verifiedNameRawContactId = -1;
790 try {
791 int maxDisplayNameSource = -1;
792 rawContactIds = new long[c.getCount()];
793 for (int i = 0; i < rawContactIds.length; i++) {
794 c.moveToPosition(i);
795 long rawContactId = c.getLong(JoinContactQuery._ID);
796 rawContactIds[i] = rawContactId;
797 int nameSource = c.getInt(JoinContactQuery.DISPLAY_NAME_SOURCE);
798 if (nameSource > maxDisplayNameSource) {
799 maxDisplayNameSource = nameSource;
800 }
801 }
802
803 // Find an appropriate display name for the joined contact:
804 // if should have a higher DisplayNameSource or be the name
805 // of the original contact that we are joining with another.
806 if (writable) {
807 for (int i = 0; i < rawContactIds.length; i++) {
808 c.moveToPosition(i);
809 if (c.getLong(JoinContactQuery.CONTACT_ID) == contactId1) {
810 int nameSource = c.getInt(JoinContactQuery.DISPLAY_NAME_SOURCE);
811 if (nameSource == maxDisplayNameSource
812 && (verifiedNameRawContactId == -1
813 || c.getInt(JoinContactQuery.NAME_VERIFIED) != 0)) {
814 verifiedNameRawContactId = c.getLong(JoinContactQuery._ID);
815 }
816 }
817 }
818 }
819 } finally {
820 c.close();
821 }
822
823 // For each pair of raw contacts, insert an aggregation exception
824 ArrayList<ContentProviderOperation> operations = new ArrayList<ContentProviderOperation>();
825 for (int i = 0; i < rawContactIds.length; i++) {
826 for (int j = 0; j < rawContactIds.length; j++) {
827 if (i != j) {
828 buildJoinContactDiff(operations, rawContactIds[i], rawContactIds[j]);
829 }
830 }
831 }
832
833 // Mark the original contact as "name verified" to make sure that the contact
834 // display name does not change as a result of the join
835 if (verifiedNameRawContactId != -1) {
836 Builder builder = ContentProviderOperation.newUpdate(
837 ContentUris.withAppendedId(RawContacts.CONTENT_URI, verifiedNameRawContactId));
838 builder.withValue(RawContacts.NAME_VERIFIED, 1);
839 operations.add(builder.build());
840 }
841
842 boolean success = false;
843 // Apply all aggregation exceptions as one batch
844 try {
845 resolver.applyBatch(ContactsContract.AUTHORITY, operations);
Dmitri Plotnikov886d3d62011-01-03 10:08:47 -0800846 showToast(R.string.contactsJoinedMessage);
Dmitri Plotnikov2b46f032010-11-29 16:41:43 -0800847 success = true;
848 } catch (RemoteException e) {
849 Log.e(TAG, "Failed to apply aggregation exception batch", e);
Dmitri Plotnikov886d3d62011-01-03 10:08:47 -0800850 showToast(R.string.contactSavedErrorToast);
Dmitri Plotnikov2b46f032010-11-29 16:41:43 -0800851 } catch (OperationApplicationException e) {
852 Log.e(TAG, "Failed to apply aggregation exception batch", e);
Dmitri Plotnikov886d3d62011-01-03 10:08:47 -0800853 showToast(R.string.contactSavedErrorToast);
Dmitri Plotnikov2b46f032010-11-29 16:41:43 -0800854 }
855
856 Intent callbackIntent = intent.getParcelableExtra(EXTRA_CALLBACK_INTENT);
857 if (success) {
858 Uri uri = RawContacts.getContactLookupUri(resolver,
859 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactIds[0]));
860 callbackIntent.setData(uri);
861 }
Dmitri Plotnikov3a6a9052011-03-02 10:14:43 -0800862 deliverCallback(callbackIntent);
Dmitri Plotnikov2b46f032010-11-29 16:41:43 -0800863 }
864
865 /**
866 * Construct a {@link AggregationExceptions#TYPE_KEEP_TOGETHER} ContentProviderOperation.
867 */
868 private void buildJoinContactDiff(ArrayList<ContentProviderOperation> operations,
869 long rawContactId1, long rawContactId2) {
870 Builder builder =
871 ContentProviderOperation.newUpdate(AggregationExceptions.CONTENT_URI);
872 builder.withValue(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_TOGETHER);
873 builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
874 builder.withValue(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
875 operations.add(builder.build());
876 }
Dmitri Plotnikov886d3d62011-01-03 10:08:47 -0800877
878 /**
879 * Shows a toast on the UI thread.
880 */
881 private void showToast(final int message) {
Dmitri Plotnikov3a6a9052011-03-02 10:14:43 -0800882 mMainHandler.post(new Runnable() {
Dmitri Plotnikov886d3d62011-01-03 10:08:47 -0800883
884 @Override
885 public void run() {
886 Toast.makeText(ContactSaveService.this, message, Toast.LENGTH_LONG).show();
887 }
888 });
889 }
Dmitri Plotnikov3a6a9052011-03-02 10:14:43 -0800890
891 private void deliverCallback(final Intent callbackIntent) {
892 mMainHandler.post(new Runnable() {
893
894 @Override
895 public void run() {
896 deliverCallbackOnUiThread(callbackIntent);
897 }
898 });
899 }
900
901 void deliverCallbackOnUiThread(final Intent callbackIntent) {
902 // TODO: this assumes that if there are multiple instances of the same
903 // activity registered, the last one registered is the one waiting for
904 // the callback. Validity of this assumption needs to be verified.
905 synchronized (sListeners) {
906 for (Listener listener : sListeners) {
907 if (callbackIntent.getComponent().equals(
908 ((Activity) listener).getIntent().getComponent())) {
909 listener.onServiceCompleted(callbackIntent);
910 return;
911 }
912 }
913 }
914 }
Daniel Lehmann173ffe12010-06-14 18:19:10 -0700915}