Java tutorial
/* * DeliciousDroid - http://code.google.com/p/DeliciousDroid/ * * Copyright (C) 2010 Matt Schmidt * * DeliciousDroid is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation; either version 3 of the License, * or (at your option) any later version. * * DeliciousDroid is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with DeliciousDroid; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA */ package com.deliciousdroid.platform; import android.annotation.TargetApi; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.provider.ContactsContract.Data; import android.provider.ContactsContract.RawContacts; import android.provider.ContactsContract.StatusUpdates; import android.provider.ContactsContract.CommonDataKinds.Im; import android.provider.ContactsContract.StreamItems; import android.util.Log; import com.deliciousdroid.R; import com.deliciousdroid.Constants; import com.deliciousdroid.client.DeliciousFeed; import com.deliciousdroid.client.User; import com.deliciousdroid.client.User.Status; import java.io.IOException; import java.util.List; import java.util.ArrayList; import org.apache.http.auth.AuthenticationException; import org.json.JSONException; /** * Class for managing contacts sync related mOperations */ public class ContactManager { /** * Custom IM protocol used when storing status messages. */ public static final String CUSTOM_IM_PROTOCOL = "DeliciousDroid"; private static final String TAG = "ContactManager"; /** * Synchronize raw contacts * * @param context The context of Authenticator Activity * @param account The username for the account * @param users The list of users */ public static synchronized void syncContacts(Context context, String account, List<User> users) { String userName; long rawContactId = 0; final ContentResolver resolver = context.getContentResolver(); List<Long> currentContacts = lookupAllContacts(resolver); final BatchOperation batchOperation = new BatchOperation(context, resolver); Log.d(TAG, "In SyncContacts"); for (final User user : users) { userName = user.getUserName(); // Check to see if the contact needs to be inserted or updated rawContactId = lookupRawContact(resolver, userName); if (rawContactId == 0) { // add new contact Log.d(TAG, "In addContact"); addContact(context, account, user, batchOperation); } else { currentContacts.remove(rawContactId); } // A sync adapter should batch operations on multiple contacts, // because it will make a dramatic performance difference. if (batchOperation.size() >= 50) { batchOperation.execute(); } } for (final Long l : currentContacts) { Log.d(TAG, "Deleting contact"); deleteContact(context, l, batchOperation); } batchOperation.execute(); } /** * Add a list of status messages to the contacts provider. * * @param context the context to use * @param accountName the username of the logged in user * @param statuses the list of statuses to store */ public static void insertStatuses(Context context, String username, List<User.Status> list) { final ContentValues values = new ContentValues(); final ContentResolver resolver = context.getContentResolver(); final ArrayList<String> processedUsers = new ArrayList<String>(); final BatchOperation batchOperation = new BatchOperation(context, resolver); for (final User.Status status : list) { // Look up the user's sample SyncAdapter data row final String userName = status.getUserName(); if (!processedUsers.contains(userName)) { final long profileId = lookupProfile(resolver, userName); // Insert the activity into the stream if (profileId > 0) { values.put(StatusUpdates.DATA_ID, profileId); values.put(StatusUpdates.STATUS, status.getStatus()); values.put(StatusUpdates.PROTOCOL, Im.PROTOCOL_CUSTOM); values.put(StatusUpdates.CUSTOM_PROTOCOL, CUSTOM_IM_PROTOCOL); values.put(StatusUpdates.IM_ACCOUNT, username); values.put(StatusUpdates.IM_HANDLE, status.getUserName()); values.put(StatusUpdates.STATUS_TIMESTAMP, status.getTimeStamp().getTime()); values.put(StatusUpdates.STATUS_RES_PACKAGE, context.getPackageName()); values.put(StatusUpdates.STATUS_ICON, R.drawable.ic_main); values.put(StatusUpdates.STATUS_LABEL, R.string.label); batchOperation.add(ContactOperations.newInsertCpo(StatusUpdates.CONTENT_URI, true) .withValues(values).build()); // A sync adapter should batch operations on multiple contacts, // because it will make a dramatic performance difference. if (batchOperation.size() >= 50) { batchOperation.execute(); } } processedUsers.add(userName); } } batchOperation.execute(); } /** * Add a list of status messages to the contacts provider. * * @param context the context to use * @param accountName the username of the logged in user * @param statuses the list of statuses to store */ @TargetApi(15) public static void insertStreamStatuses(Context context, String username) { final ContentValues values = new ContentValues(); final ContentResolver resolver = context.getContentResolver(); final BatchOperation batchOperation = new BatchOperation(context, resolver); List<Long> currentContacts = lookupAllContacts(resolver); for (long id : currentContacts) { String friendUsername = lookupUsername(resolver, id); long watermark = lookupHighWatermark(resolver, id); long newWatermark = watermark; try { List<Status> statuses = DeliciousFeed.fetchFriendStatuses(friendUsername); for (Status status : statuses) { if (status.getTimeStamp().getTime() > watermark) { if (status.getTimeStamp().getTime() > newWatermark) newWatermark = status.getTimeStamp().getTime(); values.clear(); values.put(StreamItems.RAW_CONTACT_ID, id); values.put(StreamItems.TEXT, status.getStatus()); values.put(StreamItems.TIMESTAMP, status.getTimeStamp().getTime()); values.put(StreamItems.ACCOUNT_NAME, username); values.put(StreamItems.ACCOUNT_TYPE, Constants.ACCOUNT_TYPE); values.put(StreamItems.RES_ICON, R.drawable.ic_main); values.put(StreamItems.RES_PACKAGE, context.getPackageName()); values.put(StreamItems.RES_LABEL, R.string.label); batchOperation.add(ContactOperations.newInsertCpo(StreamItems.CONTENT_URI, false) .withValues(values).build()); // A sync adapter should batch operations on multiple contacts, // because it will make a dramatic performance difference. if (batchOperation.size() >= 50) { batchOperation.execute(); } } } values.clear(); values.put(RawContacts.SYNC1, Long.toString(newWatermark)); batchOperation.add(ContactOperations.newUpdateCpo(RawContacts.CONTENT_URI, false).withValues(values) .withSelection(RawContacts._ID + "=?", new String[] { Long.toString(id) }).build()); batchOperation.execute(); } catch (AuthenticationException e) { e.printStackTrace(); } catch (JSONException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } /** * Adds a single contact to the platform contacts provider. * * @param context the Authenticator Activity context * @param accountName the account the contact belongs to * @param user the sample SyncAdapter User object */ private static void addContact(Context context, String accountName, User user, BatchOperation batchOperation) { // Put the data in the contacts provider final ContactOperations contactOp = ContactOperations.createNewContact(context, user.getUserName(), accountName, batchOperation); contactOp.addName(user.getUserName()).addProfileAction(user.getUserName()); } /** * Deletes a contact from the platform contacts provider. * * @param context the Authenticator Activity context * @param rawContactId the unique Id for this rawContact in contacts * provider */ private static void deleteContact(Context context, long rawContactId, BatchOperation batchOperation) { batchOperation.add(ContactOperations .newDeleteCpo(ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId), true).build()); } /** * Returns the RawContact id for a sample SyncAdapter contact, or 0 if the * sample SyncAdapter user isn't found. * * @param context the Authenticator Activity context * @param userId the sample SyncAdapter user ID to lookup * @return the RawContact id, or 0 if not found */ private static long lookupRawContact(ContentResolver resolver, String userName) { long authorId = 0; final Cursor c = resolver.query(RawContacts.CONTENT_URI, UserIdQuery.PROJECTION, UserIdQuery.SELECTION, new String[] { userName }, null); try { if (c.moveToFirst()) { authorId = c.getLong(UserIdQuery.COLUMN_ID); } } finally { if (c != null) { c.close(); } } return authorId; } /** * Returns the Data id for a sample SyncAdapter contact's profile row, or 0 * if the sample SyncAdapter user isn't found. * * @param resolver a content resolver * @param userId the sample SyncAdapter user ID to lookup * @return the profile Data row id, or 0 if not found */ private static long lookupProfile(ContentResolver resolver, String userName) { long profileId = 0; final Cursor c = resolver.query(Data.CONTENT_URI, ProfileQuery.PROJECTION, ProfileQuery.SELECTION, new String[] { userName }, null); try { if (c != null && c.moveToFirst()) { profileId = c.getLong(ProfileQuery.COLUMN_ID); Log.d("ProfileLookup", Long.toString(c.getLong(ProfileQuery.COLUMN_ID))); } } finally { if (c != null) { c.close(); } } return profileId; } /** * Returns the RawContact id for a sample SyncAdapter contact, or 0 if the * sample SyncAdapter user isn't found. * * @param context the Authenticator Activity context * @param userId the sample SyncAdapter user ID to lookup * @return the RawContact id, or 0 if not found */ private static List<Long> lookupAllContacts(ContentResolver resolver) { List<Long> result = new ArrayList<Long>(); final Cursor c = resolver.query(RawContacts.CONTENT_URI, AllUsersQuery.PROJECTION, AllUsersQuery.SELECTION, null, null); try { while (c.moveToNext()) { result.add(c.getLong(AllUsersQuery.COLUMN_ID)); } } finally { if (c != null) { c.close(); } } return result; } private static long lookupHighWatermark(ContentResolver resolver, long id) { long result = 0; final Cursor c = resolver.query(RawContacts.CONTENT_URI, HighWatermarkQuery.PROJECTION, HighWatermarkQuery.SELECTION, new String[] { Long.toString(id) }, null); try { while (c.moveToNext()) { result = c.getLong(HighWatermarkQuery.COLUMN_ID); } } finally { if (c != null) { c.close(); } } return result; } private static String lookupUsername(ContentResolver resolver, long id) { String result = null; final Cursor c = resolver.query(RawContacts.CONTENT_URI, UsernameQuery.PROJECTION, UsernameQuery.SELECTION, new String[] { Long.toString(id) }, null); try { while (c.moveToNext()) { result = c.getString(UsernameQuery.COLUMN_ID); } } finally { if (c != null) { c.close(); } } return result; } /** * Constants for a query to find a contact given a sample SyncAdapter user * ID. */ private interface ProfileQuery { public final static String[] PROJECTION = new String[] { Data._ID }; public final static int COLUMN_ID = 0; public static final String SELECTION = Data.MIMETYPE + "='" + ContactSyncAdapterColumns.MIME_PROFILE + "' AND " + ContactSyncAdapterColumns.DATA_PID + "=?"; } /** * Constants for a query to find a contact given a sample SyncAdapter user * ID. */ private interface UserIdQuery { public final static String[] PROJECTION = new String[] { RawContacts._ID }; public final static int COLUMN_ID = 0; public static final String SELECTION = RawContacts.ACCOUNT_TYPE + "='" + Constants.ACCOUNT_TYPE + "' AND " + RawContacts.SOURCE_ID + "=?"; } /** * Constants for a query to find all DeliciousDroid contacts */ private interface AllUsersQuery { public final static String[] PROJECTION = new String[] { RawContacts._ID }; public final static int COLUMN_ID = 0; public static final String SELECTION = RawContacts.ACCOUNT_TYPE + "='" + Constants.ACCOUNT_TYPE + "'"; } private interface HighWatermarkQuery { public final static String[] PROJECTION = new String[] { RawContacts.SYNC1 }; public final static int COLUMN_ID = 0; public static final String SELECTION = RawContacts._ID + "=?"; } private interface UsernameQuery { public final static String[] PROJECTION = new String[] { RawContacts.SOURCE_ID }; public final static int COLUMN_ID = 0; public static final String SELECTION = RawContacts._ID + "=?"; } }