blob: 1b7e475857df5c3c370ef9cc87e9db6510f0395b [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.android.providers.contacts;
import com.android.internal.util.Objects;
import com.android.providers.contacts.ContactsDatabaseHelper.Clauses;
import com.android.providers.contacts.ContactsDatabaseHelper.DataColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.GroupsColumns;
import com.android.providers.contacts.ContactsDatabaseHelper.Tables;
import com.android.providers.contacts.ContactsProvider2.GroupIdCacheEntry;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.provider.ContactsContract.Groups;
import android.provider.ContactsContract.RawContacts;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.HashMap;
/**
* Handler for group membership data rows.
*/
public class DataRowHandlerForGroupMembership extends DataRowHandler {
interface RawContactsQuery {
String TABLE = Tables.RAW_CONTACTS;
String[] COLUMNS = new String[] {
RawContacts.DELETED,
RawContacts.ACCOUNT_TYPE,
RawContacts.ACCOUNT_NAME,
RawContacts.DATA_SET,
};
int DELETED = 0;
int ACCOUNT_TYPE = 1;
int ACCOUNT_NAME = 2;
int DATA_SET = 3;
}
private static final String SELECTION_RAW_CONTACT_ID = RawContacts._ID + "=?";
private static final String QUERY_COUNT_FAVORITES_GROUP_MEMBERSHIPS_BY_RAW_CONTACT_ID =
"SELECT COUNT(*) FROM " + Tables.DATA + " LEFT OUTER JOIN " + Tables .GROUPS
+ " ON " + Tables.DATA + "." + GroupMembership.GROUP_ROW_ID
+ "=" + GroupsColumns.CONCRETE_ID
+ " WHERE " + DataColumns.MIMETYPE_ID + "=?"
+ " AND " + Tables.DATA + "." + GroupMembership.RAW_CONTACT_ID + "=?"
+ " AND " + Groups.FAVORITES + "!=0";
private final HashMap<String, ArrayList<GroupIdCacheEntry>> mGroupIdCache;
public DataRowHandlerForGroupMembership(Context context, ContactsDatabaseHelper dbHelper,
ContactAggregator aggregator,
HashMap<String, ArrayList<GroupIdCacheEntry>> groupIdCache) {
super(context, dbHelper, aggregator, GroupMembership.CONTENT_ITEM_TYPE);
mGroupIdCache = groupIdCache;
}
@Override
public long insert(SQLiteDatabase db, TransactionContext txContext, long rawContactId,
ContentValues values) {
resolveGroupSourceIdInValues(txContext, rawContactId, db, values, true);
long dataId = super.insert(db, txContext, rawContactId, values);
if (hasFavoritesGroupMembership(db, rawContactId)) {
updateRawContactsStar(db, rawContactId, true /* starred */);
}
updateVisibility(txContext, rawContactId);
return dataId;
}
@Override
public boolean update(SQLiteDatabase db, TransactionContext txContext, ContentValues values,
Cursor c, boolean callerIsSyncAdapter) {
long rawContactId = c.getLong(DataUpdateQuery.RAW_CONTACT_ID);
boolean wasStarred = hasFavoritesGroupMembership(db, rawContactId);
resolveGroupSourceIdInValues(txContext, rawContactId, db, values, false);
if (!super.update(db, txContext, values, c, callerIsSyncAdapter)) {
return false;
}
boolean isStarred = hasFavoritesGroupMembership(db, rawContactId);
if (wasStarred != isStarred) {
updateRawContactsStar(db, rawContactId, isStarred);
}
updateVisibility(txContext, rawContactId);
return true;
}
private void updateRawContactsStar(SQLiteDatabase db, long rawContactId, boolean starred) {
ContentValues rawContactValues = new ContentValues();
rawContactValues.put(RawContacts.STARRED, starred ? 1 : 0);
if (db.update(Tables.RAW_CONTACTS, rawContactValues, SELECTION_RAW_CONTACT_ID,
new String[]{Long.toString(rawContactId)}) > 0) {
mContactAggregator.updateStarred(rawContactId);
}
}
private boolean hasFavoritesGroupMembership(SQLiteDatabase db, long rawContactId) {
// TODO compiled SQL statement
final long groupMembershipMimetypeId = mDbHelper
.getMimeTypeId(GroupMembership.CONTENT_ITEM_TYPE);
boolean isStarred = 0 < DatabaseUtils
.longForQuery(db, QUERY_COUNT_FAVORITES_GROUP_MEMBERSHIPS_BY_RAW_CONTACT_ID,
new String[]{Long.toString(groupMembershipMimetypeId), Long.toString(rawContactId)});
return isStarred;
}
@Override
public int delete(SQLiteDatabase db, TransactionContext txContext, Cursor c) {
long rawContactId = c.getLong(DataDeleteQuery.RAW_CONTACT_ID);
boolean wasStarred = hasFavoritesGroupMembership(db, rawContactId);
int count = super.delete(db, txContext, c);
boolean isStarred = hasFavoritesGroupMembership(db, rawContactId);
if (wasStarred && !isStarred) {
updateRawContactsStar(db, rawContactId, false /* starred */);
}
updateVisibility(txContext, rawContactId);
return count;
}
private void updateVisibility(TransactionContext txContext, long rawContactId) {
long contactId = mDbHelper.getContactId(rawContactId);
if (contactId == 0) {
return;
}
if (mDbHelper.updateContactVisibleOnlyIfChanged(txContext, contactId)) {
mContactAggregator.updateAggregationAfterVisibilityChange(contactId);
}
}
private void resolveGroupSourceIdInValues(TransactionContext txContext,
long rawContactId, SQLiteDatabase db, ContentValues values, boolean isInsert) {
boolean containsGroupSourceId = values.containsKey(GroupMembership.GROUP_SOURCE_ID);
boolean containsGroupId = values.containsKey(GroupMembership.GROUP_ROW_ID);
if (containsGroupSourceId && containsGroupId) {
throw new IllegalArgumentException(
"you are not allowed to set both the GroupMembership.GROUP_SOURCE_ID "
+ "and GroupMembership.GROUP_ROW_ID");
}
if (!containsGroupSourceId && !containsGroupId) {
if (isInsert) {
throw new IllegalArgumentException(
"you must set exactly one of GroupMembership.GROUP_SOURCE_ID "
+ "and GroupMembership.GROUP_ROW_ID");
} else {
return;
}
}
if (containsGroupSourceId) {
final String sourceId = values.getAsString(GroupMembership.GROUP_SOURCE_ID);
final long groupId = getOrMakeGroup(db, rawContactId, sourceId,
txContext.getAccountWithDataSetForRawContact(rawContactId));
values.remove(GroupMembership.GROUP_SOURCE_ID);
values.put(GroupMembership.GROUP_ROW_ID, groupId);
}
}
/**
* Returns the group id of the group with sourceId and the same account as rawContactId.
* If the group doesn't already exist then it is first created,
* @param db SQLiteDatabase to use for this operation
* @param rawContactId the contact this group is associated with
* @param sourceId the sourceIf of the group to query or create
* @return the group id of the existing or created group
* @throws IllegalArgumentException if the contact is not associated with an account
* @throws IllegalStateException if a group needs to be created but the creation failed
*/
private long getOrMakeGroup(SQLiteDatabase db, long rawContactId, String sourceId,
AccountWithDataSet accountWithDataSet) {
if (accountWithDataSet == null) {
mSelectionArgs1[0] = String.valueOf(rawContactId);
Cursor c = db.query(RawContactsQuery.TABLE, RawContactsQuery.COLUMNS,
RawContacts._ID + "=?", mSelectionArgs1, null, null, null);
try {
if (c.moveToFirst()) {
String accountName = c.getString(RawContactsQuery.ACCOUNT_NAME);
String accountType = c.getString(RawContactsQuery.ACCOUNT_TYPE);
String dataSet = c.getString(RawContactsQuery.DATA_SET);
if (!TextUtils.isEmpty(accountName) && !TextUtils.isEmpty(accountType)) {
accountWithDataSet = new AccountWithDataSet(
accountName, accountType, dataSet);
}
}
} finally {
c.close();
}
}
if (accountWithDataSet == null) {
throw new IllegalArgumentException("if the groupmembership only "
+ "has a sourceid the the contact must be associated with "
+ "an account");
}
ArrayList<GroupIdCacheEntry> entries = mGroupIdCache.get(sourceId);
if (entries == null) {
entries = new ArrayList<GroupIdCacheEntry>(1);
mGroupIdCache.put(sourceId, entries);
}
int count = entries.size();
for (int i = 0; i < count; i++) {
GroupIdCacheEntry entry = entries.get(i);
if (entry.accountName.equals(accountWithDataSet.getAccountName())
&& entry.accountType.equals(accountWithDataSet.getAccountType())
&& Objects.equal(entry.dataSet, accountWithDataSet.getDataSet())) {
return entry.groupId;
}
}
GroupIdCacheEntry entry = new GroupIdCacheEntry();
entry.accountName = accountWithDataSet.getAccountName();
entry.accountType = accountWithDataSet.getAccountType();
entry.dataSet = accountWithDataSet.getDataSet();
entry.sourceId = sourceId;
entries.add(0, entry);
// look up the group that contains this sourceId and has the same account name, type, and
// data set as the contact refered to by rawContactId
Cursor c;
if (accountWithDataSet.getDataSet() == null) {
c = db.query(Tables.GROUPS, new String[]{RawContacts._ID},
Clauses.GROUP_HAS_ACCOUNT_AND_SOURCE_ID,
new String[]{
sourceId,
accountWithDataSet.getAccountName(),
accountWithDataSet.getAccountType()
}, null, null, null);
} else {
c = db.query(Tables.GROUPS, new String[]{RawContacts._ID},
Clauses.GROUP_HAS_ACCOUNT_AND_DATA_SET_AND_SOURCE_ID,
new String[]{
sourceId,
accountWithDataSet.getAccountName(),
accountWithDataSet.getAccountType(),
accountWithDataSet.getDataSet()
}, null, null, null);
}
try {
if (c.moveToFirst()) {
entry.groupId = c.getLong(0);
} else {
ContentValues groupValues = new ContentValues();
groupValues.put(Groups.ACCOUNT_NAME, accountWithDataSet.getAccountName());
groupValues.put(Groups.ACCOUNT_TYPE, accountWithDataSet.getAccountType());
groupValues.put(Groups.DATA_SET, accountWithDataSet.getDataSet());
groupValues.put(Groups.SOURCE_ID, sourceId);
long groupId = db.insert(Tables.GROUPS, Groups.ACCOUNT_NAME, groupValues);
if (groupId < 0) {
throw new IllegalStateException("unable to create a new group with "
+ "this sourceid: " + groupValues);
}
entry.groupId = groupId;
}
} finally {
c.close();
}
return entry.groupId;
}
}