1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package com.example.android.samplesync.platform; 18 19 import android.content.ContentResolver; 20 import android.content.ContentUris; 21 import android.content.ContentValues; 22 import android.content.Context; 23 import android.database.Cursor; 24 import android.net.Uri; 25 import android.provider.ContactsContract.Data; 26 import android.provider.ContactsContract.RawContacts; 27 import android.provider.ContactsContract.StatusUpdates; 28 import android.provider.ContactsContract.CommonDataKinds.Email; 29 import android.provider.ContactsContract.CommonDataKinds.Im; 30 import android.provider.ContactsContract.CommonDataKinds.Phone; 31 import android.provider.ContactsContract.CommonDataKinds.StructuredName; 32 import android.util.Log; 33 34 import com.example.android.samplesync.Constants; 35 import com.example.android.samplesync.R; 36 import com.example.android.samplesync.client.User; 37 38 import java.util.List; 39 40 /** 41 * Class for managing contacts sync related mOperations 42 */ 43 public class ContactManager { 44 /** 45 * Custom IM protocol used when storing status messages. 46 */ 47 public static final String CUSTOM_IM_PROTOCOL = "SampleSyncAdapter"; 48 private static final String TAG = "ContactManager"; 49 50 /** 51 * Synchronize raw contacts 52 * 53 * @param context The context of Authenticator Activity 54 * @param account The username for the account 55 * @param users The list of users 56 */ syncContacts(Context context, String account, List<User> users)57 public static synchronized void syncContacts(Context context, 58 String account, List<User> users) { 59 long userId; 60 long rawContactId = 0; 61 final ContentResolver resolver = context.getContentResolver(); 62 final BatchOperation batchOperation = 63 new BatchOperation(context, resolver); 64 Log.d(TAG, "In SyncContacts"); 65 for (final User user : users) { 66 userId = user.getUserId(); 67 // Check to see if the contact needs to be inserted or updated 68 rawContactId = lookupRawContact(resolver, userId); 69 if (rawContactId != 0) { 70 if (!user.isDeleted()) { 71 // update contact 72 updateContact(context, resolver, account, user, 73 rawContactId, batchOperation); 74 } else { 75 // delete contact 76 deleteContact(context, rawContactId, batchOperation); 77 } 78 } else { 79 // add new contact 80 Log.d(TAG, "In addContact"); 81 if (!user.isDeleted()) { 82 addContact(context, account, user, batchOperation); 83 } 84 } 85 // A sync adapter should batch operations on multiple contacts, 86 // because it will make a dramatic performance difference. 87 if (batchOperation.size() >= 50) { 88 batchOperation.execute(); 89 } 90 } 91 batchOperation.execute(); 92 } 93 94 /** 95 * Add a list of status messages to the contacts provider. 96 * 97 * @param context the context to use 98 * @param accountName the username of the logged in user 99 * @param statuses the list of statuses to store 100 */ insertStatuses(Context context, String username, List<User.Status> list)101 public static void insertStatuses(Context context, String username, 102 List<User.Status> list) { 103 final ContentValues values = new ContentValues(); 104 final ContentResolver resolver = context.getContentResolver(); 105 final BatchOperation batchOperation = 106 new BatchOperation(context, resolver); 107 for (final User.Status status : list) { 108 // Look up the user's sample SyncAdapter data row 109 final long userId = status.getUserId(); 110 final long profileId = lookupProfile(resolver, userId); 111 112 // Insert the activity into the stream 113 if (profileId > 0) { 114 values.put(StatusUpdates.DATA_ID, profileId); 115 values.put(StatusUpdates.STATUS, status.getStatus()); 116 values.put(StatusUpdates.PROTOCOL, Im.PROTOCOL_CUSTOM); 117 values.put(StatusUpdates.CUSTOM_PROTOCOL, CUSTOM_IM_PROTOCOL); 118 values.put(StatusUpdates.IM_ACCOUNT, username); 119 values.put(StatusUpdates.IM_HANDLE, status.getUserId()); 120 values.put(StatusUpdates.STATUS_RES_PACKAGE, context 121 .getPackageName()); 122 values.put(StatusUpdates.STATUS_ICON, R.drawable.icon); 123 values.put(StatusUpdates.STATUS_LABEL, R.string.label); 124 125 batchOperation 126 .add(ContactOperations.newInsertCpo( 127 StatusUpdates.CONTENT_URI, true).withValues(values) 128 .build()); 129 // A sync adapter should batch operations on multiple contacts, 130 // because it will make a dramatic performance difference. 131 if (batchOperation.size() >= 50) { 132 batchOperation.execute(); 133 } 134 } 135 } 136 batchOperation.execute(); 137 } 138 139 /** 140 * Adds a single contact to the platform contacts provider. 141 * 142 * @param context the Authenticator Activity context 143 * @param accountName the account the contact belongs to 144 * @param user the sample SyncAdapter User object 145 */ addContact(Context context, String accountName, User user, BatchOperation batchOperation)146 private static void addContact(Context context, String accountName, 147 User user, BatchOperation batchOperation) { 148 // Put the data in the contacts provider 149 final ContactOperations contactOp = 150 ContactOperations.createNewContact(context, user.getUserId(), 151 accountName, batchOperation); 152 contactOp.addName(user.getFirstName(), user.getLastName()).addEmail( 153 user.getEmail()).addPhone(user.getCellPhone(), Phone.TYPE_MOBILE) 154 .addPhone(user.getHomePhone(), Phone.TYPE_OTHER).addProfileAction( 155 user.getUserId()); 156 } 157 158 /** 159 * Updates a single contact to the platform contacts provider. 160 * 161 * @param context the Authenticator Activity context 162 * @param resolver the ContentResolver to use 163 * @param accountName the account the contact belongs to 164 * @param user the sample SyncAdapter contact object. 165 * @param rawContactId the unique Id for this rawContact in contacts 166 * provider 167 */ updateContact(Context context, ContentResolver resolver, String accountName, User user, long rawContactId, BatchOperation batchOperation)168 private static void updateContact(Context context, 169 ContentResolver resolver, String accountName, User user, 170 long rawContactId, BatchOperation batchOperation) { 171 Uri uri; 172 String cellPhone = null; 173 String otherPhone = null; 174 String email = null; 175 176 final Cursor c = 177 resolver.query(Data.CONTENT_URI, DataQuery.PROJECTION, 178 DataQuery.SELECTION, 179 new String[] {String.valueOf(rawContactId)}, null); 180 final ContactOperations contactOp = 181 ContactOperations.updateExistingContact(context, rawContactId, 182 batchOperation); 183 184 try { 185 while (c.moveToNext()) { 186 final long id = c.getLong(DataQuery.COLUMN_ID); 187 final String mimeType = c.getString(DataQuery.COLUMN_MIMETYPE); 188 uri = ContentUris.withAppendedId(Data.CONTENT_URI, id); 189 190 if (mimeType.equals(StructuredName.CONTENT_ITEM_TYPE)) { 191 final String lastName = 192 c.getString(DataQuery.COLUMN_FAMILY_NAME); 193 final String firstName = 194 c.getString(DataQuery.COLUMN_GIVEN_NAME); 195 contactOp.updateName(uri, firstName, lastName, user 196 .getFirstName(), user.getLastName()); 197 } 198 199 else if (mimeType.equals(Phone.CONTENT_ITEM_TYPE)) { 200 final int type = c.getInt(DataQuery.COLUMN_PHONE_TYPE); 201 202 if (type == Phone.TYPE_MOBILE) { 203 cellPhone = c.getString(DataQuery.COLUMN_PHONE_NUMBER); 204 contactOp.updatePhone(cellPhone, user.getCellPhone(), 205 uri); 206 } else if (type == Phone.TYPE_OTHER) { 207 otherPhone = c.getString(DataQuery.COLUMN_PHONE_NUMBER); 208 contactOp.updatePhone(otherPhone, user.getHomePhone(), 209 uri); 210 } 211 } 212 213 else if (Data.MIMETYPE.equals(Email.CONTENT_ITEM_TYPE)) { 214 email = c.getString(DataQuery.COLUMN_EMAIL_ADDRESS); 215 contactOp.updateEmail(user.getEmail(), email, uri); 216 217 } 218 } // while 219 } finally { 220 c.close(); 221 } 222 223 // Add the cell phone, if present and not updated above 224 if (cellPhone == null) { 225 contactOp.addPhone(user.getCellPhone(), Phone.TYPE_MOBILE); 226 } 227 228 // Add the other phone, if present and not updated above 229 if (otherPhone == null) { 230 contactOp.addPhone(user.getHomePhone(), Phone.TYPE_OTHER); 231 } 232 233 // Add the email address, if present and not updated above 234 if (email == null) { 235 contactOp.addEmail(user.getEmail()); 236 } 237 238 } 239 240 /** 241 * Deletes a contact from the platform contacts provider. 242 * 243 * @param context the Authenticator Activity context 244 * @param rawContactId the unique Id for this rawContact in contacts 245 * provider 246 */ deleteContact(Context context, long rawContactId, BatchOperation batchOperation)247 private static void deleteContact(Context context, long rawContactId, 248 BatchOperation batchOperation) { 249 batchOperation.add(ContactOperations.newDeleteCpo( 250 ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), 251 true).build()); 252 } 253 254 /** 255 * Returns the RawContact id for a sample SyncAdapter contact, or 0 if the 256 * sample SyncAdapter user isn't found. 257 * 258 * @param context the Authenticator Activity context 259 * @param userId the sample SyncAdapter user ID to lookup 260 * @return the RawContact id, or 0 if not found 261 */ lookupRawContact(ContentResolver resolver, long userId)262 private static long lookupRawContact(ContentResolver resolver, long userId) { 263 long authorId = 0; 264 final Cursor c = 265 resolver.query(RawContacts.CONTENT_URI, UserIdQuery.PROJECTION, 266 UserIdQuery.SELECTION, new String[] {String.valueOf(userId)}, 267 null); 268 try { 269 if (c.moveToFirst()) { 270 authorId = c.getLong(UserIdQuery.COLUMN_ID); 271 } 272 } finally { 273 if (c != null) { 274 c.close(); 275 } 276 } 277 return authorId; 278 } 279 280 /** 281 * Returns the Data id for a sample SyncAdapter contact's profile row, or 0 282 * if the sample SyncAdapter user isn't found. 283 * 284 * @param resolver a content resolver 285 * @param userId the sample SyncAdapter user ID to lookup 286 * @return the profile Data row id, or 0 if not found 287 */ lookupProfile(ContentResolver resolver, long userId)288 private static long lookupProfile(ContentResolver resolver, long userId) { 289 long profileId = 0; 290 final Cursor c = 291 resolver.query(Data.CONTENT_URI, ProfileQuery.PROJECTION, 292 ProfileQuery.SELECTION, new String[] {String.valueOf(userId)}, 293 null); 294 try { 295 if (c != null && c.moveToFirst()) { 296 profileId = c.getLong(ProfileQuery.COLUMN_ID); 297 } 298 } finally { 299 if (c != null) { 300 c.close(); 301 } 302 } 303 return profileId; 304 } 305 306 /** 307 * Constants for a query to find a contact given a sample SyncAdapter user 308 * ID. 309 */ 310 private interface ProfileQuery { 311 public final static String[] PROJECTION = new String[] {Data._ID}; 312 313 public final static int COLUMN_ID = 0; 314 315 public static final String SELECTION = 316 Data.MIMETYPE + "='" + SampleSyncAdapterColumns.MIME_PROFILE 317 + "' AND " + SampleSyncAdapterColumns.DATA_PID + "=?"; 318 } 319 /** 320 * Constants for a query to find a contact given a sample SyncAdapter user 321 * ID. 322 */ 323 private interface UserIdQuery { 324 public final static String[] PROJECTION = 325 new String[] {RawContacts._ID}; 326 327 public final static int COLUMN_ID = 0; 328 329 public static final String SELECTION = 330 RawContacts.ACCOUNT_TYPE + "='" + Constants.ACCOUNT_TYPE + "' AND " 331 + RawContacts.SOURCE_ID + "=?"; 332 } 333 334 /** 335 * Constants for a query to get contact data for a given rawContactId 336 */ 337 private interface DataQuery { 338 public static final String[] PROJECTION = 339 new String[] {Data._ID, Data.MIMETYPE, Data.DATA1, Data.DATA2, 340 Data.DATA3,}; 341 342 public static final int COLUMN_ID = 0; 343 public static final int COLUMN_MIMETYPE = 1; 344 public static final int COLUMN_DATA1 = 2; 345 public static final int COLUMN_DATA2 = 3; 346 public static final int COLUMN_DATA3 = 4; 347 public static final int COLUMN_PHONE_NUMBER = COLUMN_DATA1; 348 public static final int COLUMN_PHONE_TYPE = COLUMN_DATA2; 349 public static final int COLUMN_EMAIL_ADDRESS = COLUMN_DATA1; 350 public static final int COLUMN_EMAIL_TYPE = COLUMN_DATA2; 351 public static final int COLUMN_GIVEN_NAME = COLUMN_DATA2; 352 public static final int COLUMN_FAMILY_NAME = COLUMN_DATA3; 353 354 public static final String SELECTION = Data.RAW_CONTACT_ID + "=?"; 355 } 356 } 357