com.deliciousdroid.platform.ContactManager.java Source code

Java tutorial

Introduction

Here is the source code for com.deliciousdroid.platform.ContactManager.java

Source

/*
 * 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 + "=?";
    }
}