You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
etesync-android/app/src/main/java/at/bitfire/davdroid/resource/LocalContact.java

216 lines
8.0 KiB

/*
* Copyright © 2013 2015 Ricki Hirner (bitfire web engineering).
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*/
package at.bitfire.davdroid.resource;
import android.content.ContentProviderOperation;
import android.content.ContentValues;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.provider.ContactsContract.RawContacts.Data;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.model.UnknownProperties;
import at.bitfire.vcard4android.AndroidAddressBook;
import at.bitfire.vcard4android.AndroidContact;
import at.bitfire.vcard4android.AndroidContactFactory;
import at.bitfire.vcard4android.BatchOperation;
import at.bitfire.vcard4android.CachedGroupMembership;
import at.bitfire.vcard4android.Contact;
import at.bitfire.vcard4android.ContactsStorageException;
import ezvcard.VCardVersion;
import static at.bitfire.vcard4android.GroupMethod.GROUP_VCARDS;
public class LocalContact extends AndroidContact implements LocalResource {
protected final Set<Long>
cachedGroupMemberships = new HashSet<>(),
groupMemberships = new HashSet<>();
protected LocalContact(AndroidAddressBook addressBook, long id, String uuid, String eTag) {
super(addressBook, id, uuid, eTag);
}
public LocalContact(AndroidAddressBook addressBook, Contact contact, String uuid, String eTag) {
super(addressBook, contact, uuid, eTag);
}
public String getUuid() {
// The same now
return getFileName();
}
@Override
public boolean isLocalOnly() {
return TextUtils.isEmpty(getETag());
}
public void clearDirty(String eTag) throws ContactsStorageException {
try {
ContentValues values = new ContentValues(1);
values.put(ContactsContract.RawContacts.DIRTY, 0);
values.put(COLUMN_ETAG, eTag);
addressBook.provider.update(rawContactSyncURI(), values, null, null);
this.eTag = eTag;
} catch (RemoteException e) {
throw new ContactsStorageException("Couldn't clear dirty flag", e);
}
}
public void updateFileNameAndUID(String uid) throws ContactsStorageException {
try {
String newFileName = uid;
ContentValues values = new ContentValues(2);
values.put(COLUMN_FILENAME, newFileName);
values.put(COLUMN_UID, uid);
addressBook.provider.update(rawContactSyncURI(), values, null, null);
fileName = newFileName;
} catch (RemoteException e) {
throw new ContactsStorageException("Couldn't update UID", e);
}
}
@Override
public String getContent() throws IOException, ContactsStorageException {
final Contact contact;
contact = getContact();
App.log.log(Level.FINE, "Preparing upload of VCard " + getUuid(), contact);
ByteArrayOutputStream os = new ByteArrayOutputStream();
contact.write(VCardVersion.V4_0, GROUP_VCARDS, os);
return os.toString();
}
@Override
protected void populateData(String mimeType, ContentValues row) {
switch (mimeType) {
case CachedGroupMembership.CONTENT_ITEM_TYPE:
cachedGroupMemberships.add(row.getAsLong(CachedGroupMembership.GROUP_ID));
break;
case GroupMembership.CONTENT_ITEM_TYPE:
groupMemberships.add(row.getAsLong(GroupMembership.GROUP_ROW_ID));
break;
case UnknownProperties.CONTENT_ITEM_TYPE:
contact.unknownProperties = row.getAsString(UnknownProperties.UNKNOWN_PROPERTIES);
break;
}
}
@Override
protected void insertDataRows(BatchOperation batch) throws ContactsStorageException {
super.insertDataRows(batch);
if (contact.unknownProperties != null) {
final BatchOperation.Operation op;
final ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(dataSyncURI());
if (id == null) {
op = new BatchOperation.Operation(builder, UnknownProperties.RAW_CONTACT_ID, 0);
} else {
op = new BatchOperation.Operation(builder);
builder.withValue(UnknownProperties.RAW_CONTACT_ID, id);
}
builder .withValue(UnknownProperties.MIMETYPE, UnknownProperties.CONTENT_ITEM_TYPE)
.withValue(UnknownProperties.UNKNOWN_PROPERTIES, contact.unknownProperties);
batch.enqueue(op);
}
}
public void addToGroup(BatchOperation batch, long groupID) {
assertID();
batch.enqueue(new BatchOperation.Operation(
ContentProviderOperation.newInsert(dataSyncURI())
.withValue(GroupMembership.MIMETYPE, GroupMembership.CONTENT_ITEM_TYPE)
.withValue(GroupMembership.RAW_CONTACT_ID, id)
.withValue(GroupMembership.GROUP_ROW_ID, groupID)
));
batch.enqueue(new BatchOperation.Operation(
ContentProviderOperation.newInsert(dataSyncURI())
.withValue(CachedGroupMembership.MIMETYPE, CachedGroupMembership.CONTENT_ITEM_TYPE)
.withValue(CachedGroupMembership.RAW_CONTACT_ID, id)
.withValue(CachedGroupMembership.GROUP_ID, groupID)
.withYieldAllowed(true)
));
}
public void removeGroupMemberships(BatchOperation batch) {
assertID();
batch.enqueue(new BatchOperation.Operation(
ContentProviderOperation.newDelete(dataSyncURI())
.withSelection(
Data.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + " IN (?,?)",
new String[] { String.valueOf(id), GroupMembership.CONTENT_ITEM_TYPE, CachedGroupMembership.CONTENT_ITEM_TYPE }
)
.withYieldAllowed(true)
));
}
/**
* Returns the IDs of all groups the contact was member of (cached memberships).
* Cached memberships are kept in sync with memberships by DAVdroid and are used to determine
* whether a membership has been deleted/added when a raw contact is dirty.
* @return set of {@link GroupMembership#GROUP_ROW_ID} (may be empty)
* @throws ContactsStorageException on contact provider errors
* @throws FileNotFoundException if the current contact can't be found
*/
@NonNull
public Set<Long> getCachedGroupMemberships() throws ContactsStorageException, FileNotFoundException {
getContact();
return cachedGroupMemberships;
}
/**
* Returns the IDs of all groups the contact is member of.
* @return set of {@link GroupMembership#GROUP_ROW_ID}s (may be empty)
* @throws ContactsStorageException on contact provider errors
* @throws FileNotFoundException if the current contact can't be found
*/
@NonNull
public Set<Long> getGroupMemberships() throws ContactsStorageException, FileNotFoundException {
getContact();
return groupMemberships;
}
// factory
static class Factory extends AndroidContactFactory {
static final Factory INSTANCE = new Factory();
@Override
public LocalContact newInstance(AndroidAddressBook addressBook, long id, String fileName, String eTag) {
return new LocalContact(addressBook, id, fileName, eTag);
}
@Override
public LocalContact[] newArray(int size) {
return new LocalContact[size];
}
}
}