• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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