blob: 22f4f26755168774af983c00f7eec0eba43ab25e [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.example.android.samplesync.platform;
import com.example.android.samplesync.Constants;
import com.example.android.samplesync.R;
import com.example.android.samplesync.client.RawContact;
import android.accounts.Account;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Im;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.Groups;
import android.provider.ContactsContract.RawContacts;
import android.provider.ContactsContract.Settings;
import android.provider.ContactsContract.StatusUpdates;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/**
* 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 = "SampleSyncAdapter";
private static final String TAG = "ContactManager";
public static final String SAMPLE_GROUP_NAME = "Sample Group";
public static long ensureSampleGroupExists(Context context, Account account) {
final ContentResolver resolver = context.getContentResolver();
// Lookup the sample group
long groupId = 0;
final Cursor cursor = resolver.query(Groups.CONTENT_URI, new String[] { Groups._ID },
Groups.ACCOUNT_NAME + "=? AND " + Groups.ACCOUNT_TYPE + "=? AND " +
Groups.TITLE + "=?",
new String[] { account.name, account.type, SAMPLE_GROUP_NAME }, null);
if (cursor != null) {
try {
if (cursor.moveToFirst()) {
groupId = cursor.getLong(0);
}
} finally {
cursor.close();
}
}
if (groupId == 0) {
// Sample group doesn't exist yet, so create it
final ContentValues contentValues = new ContentValues();
contentValues.put(Groups.ACCOUNT_NAME, account.name);
contentValues.put(Groups.ACCOUNT_TYPE, account.type);
contentValues.put(Groups.TITLE, SAMPLE_GROUP_NAME);
contentValues.put(Groups.GROUP_IS_READ_ONLY, true);
final Uri newGroupUri = resolver.insert(Groups.CONTENT_URI, contentValues);
groupId = ContentUris.parseId(newGroupUri);
}
return groupId;
}
/**
* Take a list of updated contacts and apply those changes to the
* contacts database. Typically this list of contacts would have been
* returned from the server, and we want to apply those changes locally.
*
* @param context The context of Authenticator Activity
* @param account The username for the account
* @param rawContacts The list of contacts to update
* @param lastSyncMarker The previous server sync-state
* @return the server syncState that should be used in our next
* sync request.
*/
public static synchronized long updateContacts(Context context, String account,
List<RawContact> rawContacts, long groupId, long lastSyncMarker) {
long currentSyncMarker = lastSyncMarker;
final ContentResolver resolver = context.getContentResolver();
final BatchOperation batchOperation = new BatchOperation(context, resolver);
final List<RawContact> newUsers = new ArrayList<RawContact>();
Log.d(TAG, "In SyncContacts");
for (final RawContact rawContact : rawContacts) {
// The server returns a syncState (x) value with each contact record.
// The syncState is sequential, so higher values represent more recent
// changes than lower values. We keep track of the highest value we
// see, and consider that a "high water mark" for the changes we've
// received from the server. That way, on our next sync, we can just
// ask for changes that have occurred since that most-recent change.
if (rawContact.getSyncState() > currentSyncMarker) {
currentSyncMarker = rawContact.getSyncState();
}
// If the server returned a clientId for this user, then it's likely
// that the user was added here, and was just pushed to the server
// for the first time. In that case, we need to update the main
// row for this contact so that the RawContacts.SOURCE_ID value
// contains the correct serverId.
final long rawContactId;
final boolean updateServerId;
if (rawContact.getRawContactId() > 0) {
rawContactId = rawContact.getRawContactId();
updateServerId = true;
} else {
long serverContactId = rawContact.getServerContactId();
rawContactId = lookupRawContact(resolver, serverContactId);
updateServerId = false;
}
if (rawContactId != 0) {
if (!rawContact.isDeleted()) {
updateContact(context, resolver, rawContact, updateServerId,
true, true, true, rawContactId, batchOperation);
} else {
deleteContact(context, rawContactId, batchOperation);
}
} else {
Log.d(TAG, "In addContact");
if (!rawContact.isDeleted()) {
newUsers.add(rawContact);
addContact(context, account, rawContact, groupId, true, batchOperation);
}
}
// A sync adapter should batch operations on multiple contacts,
// because it will make a dramatic performance difference.
// (UI updates, etc)
if (batchOperation.size() >= 50) {
batchOperation.execute();
}
}
batchOperation.execute();
return currentSyncMarker;
}
/**
* Return a list of the local contacts that have been marked as
* "dirty", and need syncing to the SampleSync server.
*
* @param context The context of Authenticator Activity
* @param account The account that we're interested in syncing
* @return a list of Users that are considered "dirty"
*/
public static List<RawContact> getDirtyContacts(Context context, Account account) {
Log.i(TAG, "*** Looking for local dirty contacts");
List<RawContact> dirtyContacts = new ArrayList<RawContact>();
final ContentResolver resolver = context.getContentResolver();
final Cursor c = resolver.query(DirtyQuery.CONTENT_URI,
DirtyQuery.PROJECTION,
DirtyQuery.SELECTION,
new String[] {account.name},
null);
try {
while (c.moveToNext()) {
final long rawContactId = c.getLong(DirtyQuery.COLUMN_RAW_CONTACT_ID);
final long serverContactId = c.getLong(DirtyQuery.COLUMN_SERVER_ID);
final boolean isDirty = "1".equals(c.getString(DirtyQuery.COLUMN_DIRTY));
final boolean isDeleted = "1".equals(c.getString(DirtyQuery.COLUMN_DELETED));
// The system actually keeps track of a change version number for
// each contact. It may be something you're interested in for your
// client-server sync protocol. We're not using it in this example,
// other than to log it.
final long version = c.getLong(DirtyQuery.COLUMN_VERSION);
Log.i(TAG, "Dirty Contact: " + Long.toString(rawContactId));
Log.i(TAG, "Contact Version: " + Long.toString(version));
if (isDeleted) {
Log.i(TAG, "Contact is marked for deletion");
RawContact rawContact = RawContact.createDeletedContact(rawContactId,
serverContactId);
dirtyContacts.add(rawContact);
} else if (isDirty) {
RawContact rawContact = getRawContact(context, rawContactId);
Log.i(TAG, "Contact Name: " + rawContact.getBestName());
dirtyContacts.add(rawContact);
}
}
} finally {
if (c != null) {
c.close();
}
}
return dirtyContacts;
}
/**
* Update the status messages for a list of users. This is typically called
* for contacts we've just added to the system, since we can't monkey with
* the contact's status until they have a profileId.
*
* @param context The context of Authenticator Activity
* @param rawContacts The list of users we want to update
*/
public static void updateStatusMessages(Context context, List<RawContact> rawContacts) {
final ContentResolver resolver = context.getContentResolver();
final BatchOperation batchOperation = new BatchOperation(context, resolver);
for (RawContact rawContact : rawContacts) {
updateContactStatus(context, rawContact, batchOperation);
}
batchOperation.execute();
}
/**
* After we've finished up a sync operation, we want to clean up the sync-state
* so that we're ready for the next time. This involves clearing out the 'dirty'
* flag on the synced contacts - but we also have to finish the DELETE operation
* on deleted contacts. When the user initially deletes them on the client, they're
* marked for deletion - but they're not actually deleted until we delete them
* again, and include the ContactsContract.CALLER_IS_SYNCADAPTER parameter to
* tell the contacts provider that we're really ready to let go of this contact.
*
* @param context The context of Authenticator Activity
* @param dirtyContacts The list of contacts that we're cleaning up
*/
public static void clearSyncFlags(Context context, List<RawContact> dirtyContacts) {
Log.i(TAG, "*** Clearing Sync-related Flags");
final ContentResolver resolver = context.getContentResolver();
final BatchOperation batchOperation = new BatchOperation(context, resolver);
for (RawContact rawContact : dirtyContacts) {
if (rawContact.isDeleted()) {
Log.i(TAG, "Deleting contact: " + Long.toString(rawContact.getRawContactId()));
deleteContact(context, rawContact.getRawContactId(), batchOperation);
} else if (rawContact.isDirty()) {
Log.i(TAG, "Clearing dirty flag for: " + rawContact.getBestName());
clearDirtyFlag(context, rawContact.getRawContactId(), batchOperation);
}
}
batchOperation.execute();
}
/**
* Adds a single contact to the platform contacts provider.
* This can be used to respond to a new contact found as part
* of sync information returned from the server, or because a
* user added a new contact.
*
* @param context the Authenticator Activity context
* @param accountName the account the contact belongs to
* @param rawContact the sample SyncAdapter User object
* @param groupId the id of the sample group
* @param inSync is the add part of a client-server sync?
* @param batchOperation allow us to batch together multiple operations
* into a single provider call
*/
public static void addContact(Context context, String accountName, RawContact rawContact,
long groupId, boolean inSync, BatchOperation batchOperation) {
// Put the data in the contacts provider
final ContactOperations contactOp = ContactOperations.createNewContact(
context, rawContact.getServerContactId(), accountName, inSync, batchOperation);
contactOp.addName(rawContact.getFullName(), rawContact.getFirstName(),
rawContact.getLastName())
.addEmail(rawContact.getEmail())
.addPhone(rawContact.getCellPhone(), Phone.TYPE_MOBILE)
.addPhone(rawContact.getHomePhone(), Phone.TYPE_HOME)
.addPhone(rawContact.getOfficePhone(), Phone.TYPE_WORK)
.addGroupMembership(groupId)
.addAvatar(rawContact.getAvatarUrl());
// If we have a serverId, then go ahead and create our status profile.
// Otherwise skip it - and we'll create it after we sync-up to the
// server later on.
if (rawContact.getServerContactId() > 0) {
contactOp.addProfileAction(rawContact.getServerContactId());
}
}
/**
* Updates a single contact to the platform contacts provider.
* This method can be used to update a contact from a sync
* operation or as a result of a user editing a contact
* record.
*
* This operation is actually relatively complex. We query
* the database to find all the rows of info that already
* exist for this Contact. For rows that exist (and thus we're
* modifying existing fields), we create an update operation
* to change that field. But for fields we're adding, we create
* "add" operations to create new rows for those fields.
*
* @param context the Authenticator Activity context
* @param resolver the ContentResolver to use
* @param rawContact the sample SyncAdapter contact object
* @param updateStatus should we update this user's status
* @param updateAvatar should we update this user's avatar image
* @param inSync is the update part of a client-server sync?
* @param rawContactId the unique Id for this rawContact in contacts
* provider
* @param batchOperation allow us to batch together multiple operations
* into a single provider call
*/
public static void updateContact(Context context, ContentResolver resolver,
RawContact rawContact, boolean updateServerId, boolean updateStatus, boolean updateAvatar,
boolean inSync, long rawContactId, BatchOperation batchOperation) {
boolean existingCellPhone = false;
boolean existingHomePhone = false;
boolean existingWorkPhone = false;
boolean existingEmail = false;
boolean existingAvatar = false;
final Cursor c =
resolver.query(DataQuery.CONTENT_URI, DataQuery.PROJECTION, DataQuery.SELECTION,
new String[] {String.valueOf(rawContactId)}, null);
final ContactOperations contactOp =
ContactOperations.updateExistingContact(context, rawContactId,
inSync, batchOperation);
try {
// Iterate over the existing rows of data, and update each one
// with the information we received from the server.
while (c.moveToNext()) {
final long id = c.getLong(DataQuery.COLUMN_ID);
final String mimeType = c.getString(DataQuery.COLUMN_MIMETYPE);
final Uri uri = ContentUris.withAppendedId(Data.CONTENT_URI, id);
if (mimeType.equals(StructuredName.CONTENT_ITEM_TYPE)) {
contactOp.updateName(uri,
c.getString(DataQuery.COLUMN_GIVEN_NAME),
c.getString(DataQuery.COLUMN_FAMILY_NAME),
c.getString(DataQuery.COLUMN_FULL_NAME),
rawContact.getFirstName(),
rawContact.getLastName(),
rawContact.getFullName());
} else if (mimeType.equals(Phone.CONTENT_ITEM_TYPE)) {
final int type = c.getInt(DataQuery.COLUMN_PHONE_TYPE);
if (type == Phone.TYPE_MOBILE) {
existingCellPhone = true;
contactOp.updatePhone(c.getString(DataQuery.COLUMN_PHONE_NUMBER),
rawContact.getCellPhone(), uri);
} else if (type == Phone.TYPE_HOME) {
existingHomePhone = true;
contactOp.updatePhone(c.getString(DataQuery.COLUMN_PHONE_NUMBER),
rawContact.getHomePhone(), uri);
} else if (type == Phone.TYPE_WORK) {
existingWorkPhone = true;
contactOp.updatePhone(c.getString(DataQuery.COLUMN_PHONE_NUMBER),
rawContact.getOfficePhone(), uri);
}
} else if (mimeType.equals(Email.CONTENT_ITEM_TYPE)) {
existingEmail = true;
contactOp.updateEmail(rawContact.getEmail(),
c.getString(DataQuery.COLUMN_EMAIL_ADDRESS), uri);
} else if (mimeType.equals(Photo.CONTENT_ITEM_TYPE)) {
existingAvatar = true;
contactOp.updateAvatar(rawContact.getAvatarUrl(), uri);
}
} // while
} finally {
c.close();
}
// Add the cell phone, if present and not updated above
if (!existingCellPhone) {
contactOp.addPhone(rawContact.getCellPhone(), Phone.TYPE_MOBILE);
}
// Add the home phone, if present and not updated above
if (!existingHomePhone) {
contactOp.addPhone(rawContact.getHomePhone(), Phone.TYPE_HOME);
}
// Add the work phone, if present and not updated above
if (!existingWorkPhone) {
contactOp.addPhone(rawContact.getOfficePhone(), Phone.TYPE_WORK);
}
// Add the email address, if present and not updated above
if (!existingEmail) {
contactOp.addEmail(rawContact.getEmail());
}
// Add the avatar if we didn't update the existing avatar
if (!existingAvatar) {
contactOp.addAvatar(rawContact.getAvatarUrl());
}
// If we need to update the serverId of the contact record, take
// care of that. This will happen if the contact is created on the
// client, and then synced to the server. When we get the updated
// record back from the server, we can set the SOURCE_ID property
// on the contact, so we can (in the future) lookup contacts by
// the serverId.
if (updateServerId) {
Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
contactOp.updateServerId(rawContact.getServerContactId(), uri);
}
// If we don't have a status profile, then create one. This could
// happen for contacts that were created on the client - we don't
// create the status profile until after the first sync...
final long serverId = rawContact.getServerContactId();
final long profileId = lookupProfile(resolver, serverId);
if (profileId <= 0) {
contactOp.addProfileAction(serverId);
}
}
/**
* When we first add a sync adapter to the system, the contacts from that
* sync adapter will be hidden unless they're merged/grouped with an existing
* contact. But typically we want to actually show those contacts, so we
* need to mess with the Settings table to get them to show up.
*
* @param context the Authenticator Activity context
* @param account the Account who's visibility we're changing
* @param visible true if we want the contacts visible, false for hidden
*/
public static void setAccountContactsVisibility(Context context, Account account,
boolean visible) {
ContentValues values = new ContentValues();
values.put(RawContacts.ACCOUNT_NAME, account.name);
values.put(RawContacts.ACCOUNT_TYPE, Constants.ACCOUNT_TYPE);
values.put(Settings.UNGROUPED_VISIBLE, visible ? 1 : 0);
context.getContentResolver().insert(Settings.CONTENT_URI, values);
}
/**
* Return a User object with data extracted from a contact stored
* in the local contacts database.
*
* Because a contact is actually stored over several rows in the
* database, our query will return those multiple rows of information.
* We then iterate over the rows and build the User structure from
* what we find.
*
* @param context the Authenticator Activity context
* @param rawContactId the unique ID for the local contact
* @return a User object containing info on that contact
*/
private static RawContact getRawContact(Context context, long rawContactId) {
String firstName = null;
String lastName = null;
String fullName = null;
String cellPhone = null;
String homePhone = null;
String workPhone = null;
String email = null;
long serverId = -1;
final ContentResolver resolver = context.getContentResolver();
final Cursor c =
resolver.query(DataQuery.CONTENT_URI, DataQuery.PROJECTION, DataQuery.SELECTION,
new String[] {String.valueOf(rawContactId)}, null);
try {
while (c.moveToNext()) {
final long id = c.getLong(DataQuery.COLUMN_ID);
final String mimeType = c.getString(DataQuery.COLUMN_MIMETYPE);
final long tempServerId = c.getLong(DataQuery.COLUMN_SERVER_ID);
if (tempServerId > 0) {
serverId = tempServerId;
}
final Uri uri = ContentUris.withAppendedId(Data.CONTENT_URI, id);
if (mimeType.equals(StructuredName.CONTENT_ITEM_TYPE)) {
lastName = c.getString(DataQuery.COLUMN_FAMILY_NAME);
firstName = c.getString(DataQuery.COLUMN_GIVEN_NAME);
fullName = c.getString(DataQuery.COLUMN_FULL_NAME);
} else if (mimeType.equals(Phone.CONTENT_ITEM_TYPE)) {
final int type = c.getInt(DataQuery.COLUMN_PHONE_TYPE);
if (type == Phone.TYPE_MOBILE) {
cellPhone = c.getString(DataQuery.COLUMN_PHONE_NUMBER);
} else if (type == Phone.TYPE_HOME) {
homePhone = c.getString(DataQuery.COLUMN_PHONE_NUMBER);
} else if (type == Phone.TYPE_WORK) {
workPhone = c.getString(DataQuery.COLUMN_PHONE_NUMBER);
}
} else if (mimeType.equals(Email.CONTENT_ITEM_TYPE)) {
email = c.getString(DataQuery.COLUMN_EMAIL_ADDRESS);
}
} // while
} finally {
c.close();
}
// Now that we've extracted all the information we care about,
// create the actual User object.
RawContact rawContact = RawContact.create(fullName, firstName, lastName, cellPhone,
workPhone, homePhone, email, null, false, rawContactId, serverId);
return rawContact;
}
/**
* Update the status message associated with the specified user. The status
* message would be something that is likely to be used by IM or social
* networking sync providers, and less by a straightforward contact provider.
* But it's a useful demo to see how it's done.
*
* @param context the Authenticator Activity context
* @param rawContact the contact whose status we should update
* @param batchOperation allow us to batch together multiple operations
*/
private static void updateContactStatus(Context context, RawContact rawContact,
BatchOperation batchOperation) {
final ContentValues values = new ContentValues();
final ContentResolver resolver = context.getContentResolver();
final long userId = rawContact.getServerContactId();
final String username = rawContact.getUserName();
final String status = rawContact.getStatus();
// Look up the user's sample SyncAdapter data row
final long profileId = lookupProfile(resolver, userId);
// Insert the activity into the stream
if (profileId > 0) {
values.put(StatusUpdates.DATA_ID, profileId);
values.put(StatusUpdates.STATUS, status);
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, userId);
values.put(StatusUpdates.STATUS_RES_PACKAGE, context.getPackageName());
values.put(StatusUpdates.STATUS_ICON, R.drawable.icon);
values.put(StatusUpdates.STATUS_LABEL, R.string.label);
batchOperation.add(ContactOperations.newInsertCpo(StatusUpdates.CONTENT_URI,
false, true).withValues(values).build());
}
}
/**
* Clear the local system 'dirty' flag for a contact.
*
* @param context the Authenticator Activity context
* @param rawContactId the id of the contact update
* @param batchOperation allow us to batch together multiple operations
*/
private static void clearDirtyFlag(Context context, long rawContactId,
BatchOperation batchOperation) {
final ContactOperations contactOp =
ContactOperations.updateExistingContact(context, rawContactId, true,
batchOperation);
final Uri uri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
contactOp.updateDirtyFlag(false, uri);
}
/**
* Deletes a contact from the platform contacts provider. This method is used
* both for contacts that were deleted locally and then that deletion was synced
* to the server, and for contacts that were deleted on the server and the
* deletion was synced to the client.
*
* @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, true).build());
}
/**
* Returns the RawContact id for a sample SyncAdapter contact, or 0 if the
* sample SyncAdapter user isn't found.
*
* @param resolver the content resolver to use
* @param serverContactId the sample SyncAdapter user ID to lookup
* @return the RawContact id, or 0 if not found
*/
private static long lookupRawContact(ContentResolver resolver, long serverContactId) {
long rawContactId = 0;
final Cursor c = resolver.query(
UserIdQuery.CONTENT_URI,
UserIdQuery.PROJECTION,
UserIdQuery.SELECTION,
new String[] {String.valueOf(serverContactId)},
null);
try {
if ((c != null) && c.moveToFirst()) {
rawContactId = c.getLong(UserIdQuery.COLUMN_RAW_CONTACT_ID);
}
} finally {
if (c != null) {
c.close();
}
}
return rawContactId;
}
/**
* 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, long userId) {
long profileId = 0;
final Cursor c =
resolver.query(Data.CONTENT_URI, ProfileQuery.PROJECTION, ProfileQuery.SELECTION,
new String[] {String.valueOf(userId)}, null);
try {
if ((c != null) && c.moveToFirst()) {
profileId = c.getLong(ProfileQuery.COLUMN_ID);
}
} finally {
if (c != null) {
c.close();
}
}
return profileId;
}
final public static class EditorQuery {
private EditorQuery() {
}
public static final String[] PROJECTION = new String[] {
RawContacts.ACCOUNT_NAME,
Data._ID,
RawContacts.Entity.DATA_ID,
Data.MIMETYPE,
Data.DATA1,
Data.DATA2,
Data.DATA3,
Data.DATA15,
Data.SYNC1
};
public static final int COLUMN_ACCOUNT_NAME = 0;
public static final int COLUMN_RAW_CONTACT_ID = 1;
public static final int COLUMN_DATA_ID = 2;
public static final int COLUMN_MIMETYPE = 3;
public static final int COLUMN_DATA1 = 4;
public static final int COLUMN_DATA2 = 5;
public static final int COLUMN_DATA3 = 6;
public static final int COLUMN_DATA15 = 7;
public static final int COLUMN_SYNC1 = 8;
public static final int COLUMN_PHONE_NUMBER = COLUMN_DATA1;
public static final int COLUMN_PHONE_TYPE = COLUMN_DATA2;
public static final int COLUMN_EMAIL_ADDRESS = COLUMN_DATA1;
public static final int COLUMN_EMAIL_TYPE = COLUMN_DATA2;
public static final int COLUMN_FULL_NAME = COLUMN_DATA1;
public static final int COLUMN_GIVEN_NAME = COLUMN_DATA2;
public static final int COLUMN_FAMILY_NAME = COLUMN_DATA3;
public static final int COLUMN_AVATAR_IMAGE = COLUMN_DATA15;
public static final int COLUMN_SYNC_DIRTY = COLUMN_SYNC1;
public static final String SELECTION = Data.RAW_CONTACT_ID + "=?";
}
/**
* Constants for a query to find a contact given a sample SyncAdapter user
* ID.
*/
final private static class ProfileQuery {
private ProfileQuery() {
}
public final static String[] PROJECTION = new String[] {Data._ID};
public final static int COLUMN_ID = 0;
public static final String SELECTION =
Data.MIMETYPE + "='" + SampleSyncAdapterColumns.MIME_PROFILE + "' AND "
+ SampleSyncAdapterColumns.DATA_PID + "=?";
}
/**
* Constants for a query to find a contact given a sample SyncAdapter user
* ID.
*/
final private static class UserIdQuery {
private UserIdQuery() {
}
public final static String[] PROJECTION = new String[] {
RawContacts._ID,
RawContacts.CONTACT_ID
};
public final static int COLUMN_RAW_CONTACT_ID = 0;
public final static int COLUMN_LINKED_CONTACT_ID = 1;
public final static Uri CONTENT_URI = RawContacts.CONTENT_URI;
public static final String SELECTION =
RawContacts.ACCOUNT_TYPE + "='" + Constants.ACCOUNT_TYPE + "' AND "
+ RawContacts.SOURCE_ID + "=?";
}
/**
* Constants for a query to find SampleSyncAdapter contacts that are
* in need of syncing to the server. This should cover new, edited,
* and deleted contacts.
*/
final private static class DirtyQuery {
private DirtyQuery() {
}
public final static String[] PROJECTION = new String[] {
RawContacts._ID,
RawContacts.SOURCE_ID,
RawContacts.DIRTY,
RawContacts.DELETED,
RawContacts.VERSION
};
public final static int COLUMN_RAW_CONTACT_ID = 0;
public final static int COLUMN_SERVER_ID = 1;
public final static int COLUMN_DIRTY = 2;
public final static int COLUMN_DELETED = 3;
public final static int COLUMN_VERSION = 4;
public static final Uri CONTENT_URI = RawContacts.CONTENT_URI.buildUpon()
.appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
.build();
public static final String SELECTION =
RawContacts.DIRTY + "=1 AND "
+ RawContacts.ACCOUNT_TYPE + "='" + Constants.ACCOUNT_TYPE + "' AND "
+ RawContacts.ACCOUNT_NAME + "=?";
}
/**
* Constants for a query to get contact data for a given rawContactId
*/
final private static class DataQuery {
private DataQuery() {
}
public static final String[] PROJECTION =
new String[] {Data._ID, RawContacts.SOURCE_ID, Data.MIMETYPE, Data.DATA1,
Data.DATA2, Data.DATA3, Data.DATA15, Data.SYNC1};
public static final int COLUMN_ID = 0;
public static final int COLUMN_SERVER_ID = 1;
public static final int COLUMN_MIMETYPE = 2;
public static final int COLUMN_DATA1 = 3;
public static final int COLUMN_DATA2 = 4;
public static final int COLUMN_DATA3 = 5;
public static final int COLUMN_DATA15 = 6;
public static final int COLUMN_SYNC1 = 7;
public static final Uri CONTENT_URI = Data.CONTENT_URI;
public static final int COLUMN_PHONE_NUMBER = COLUMN_DATA1;
public static final int COLUMN_PHONE_TYPE = COLUMN_DATA2;
public static final int COLUMN_EMAIL_ADDRESS = COLUMN_DATA1;
public static final int COLUMN_EMAIL_TYPE = COLUMN_DATA2;
public static final int COLUMN_FULL_NAME = COLUMN_DATA1;
public static final int COLUMN_GIVEN_NAME = COLUMN_DATA2;
public static final int COLUMN_FAMILY_NAME = COLUMN_DATA3;
public static final int COLUMN_AVATAR_IMAGE = COLUMN_DATA15;
public static final int COLUMN_SYNC_DIRTY = COLUMN_SYNC1;
public static final String SELECTION = Data.RAW_CONTACT_ID + "=?";
}
/**
* Constants for a query to read basic contact columns
*/
final public static class ContactQuery {
private ContactQuery() {
}
public static final String[] PROJECTION =
new String[] {Contacts._ID, Contacts.DISPLAY_NAME};
public static final int COLUMN_ID = 0;
public static final int COLUMN_DISPLAY_NAME = 1;
}
}