mirror of
https://github.com/etesync/android
synced 2024-12-27 08:58:09 +00:00
Fix "can't edit contact" / "account doesn't show up" and other bugs; refactoring
* support for phonetic names (closes #19) * update contacts.xml, tested with 4.0 (Samsung), 4.2 (Cyanogen), 4.3 (Cyanogen) (fixes #5, fixes #6, fixes #7) * smarter error handling (1): notify sync manager in case of HTTP auth errors * smarter error handling (2): just ignore the dubious resources instead of notifying Android sync service * refactoring: created DavSyncAdapter and move common code to it * version bump to 0.3.4-alpha
This commit is contained in:
parent
c87fb7bedd
commit
5db8bcb9d8
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="at.bitfire.davdroid"
|
package="at.bitfire.davdroid"
|
||||||
android:versionCode="6"
|
android:versionCode="7"
|
||||||
android:versionName="0.3.3-alpha" >
|
android:versionName="0.3.4-alpha" >
|
||||||
|
|
||||||
<uses-sdk
|
<uses-sdk
|
||||||
android:minSdkVersion="14"
|
android:minSdkVersion="14"
|
||||||
@ -34,7 +34,8 @@
|
|||||||
</service>
|
</service>
|
||||||
<service
|
<service
|
||||||
android:name=".syncadapter.ContactsSyncAdapterService"
|
android:name=".syncadapter.ContactsSyncAdapterService"
|
||||||
android:exported="true" >
|
android:exported="true"
|
||||||
|
android:process=":sync" >
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.content.SyncAdapter" />
|
<action android:name="android.content.SyncAdapter" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@ -48,7 +49,8 @@
|
|||||||
</service>
|
</service>
|
||||||
<service
|
<service
|
||||||
android:name=".syncadapter.CalendarsSyncAdapterService"
|
android:name=".syncadapter.CalendarsSyncAdapterService"
|
||||||
android:exported="true" >
|
android:exported="true"
|
||||||
|
android:process=":sync" >
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.content.SyncAdapter" />
|
<action android:name="android.content.SyncAdapter" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
@ -30,6 +30,6 @@
|
|||||||
<string name="calendars">Calendars</string>
|
<string name="calendars">Calendars</string>
|
||||||
<string name="select_address_book">Select up to one address book (tap again to unselect):</string>
|
<string name="select_address_book">Select up to one address book (tap again to unselect):</string>
|
||||||
<string name="select_calendars">Select your calendars:</string>
|
<string name="select_calendars">Select your calendars:</string>
|
||||||
<string name="auth_preemptive">Preemptive authentification (recommended, but incompatible with Digest auth)</string>
|
<string name="auth_preemptive">Preemptive authentication (recommended, but incompatible with Digest auth)</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
<Preference android:title="DAVdroid Web site" >
|
<Preference android:title="DAVdroid Web site" >
|
||||||
<intent
|
<intent
|
||||||
android:action="android.intent.action.VIEW"
|
android:action="android.intent.action.VIEW"
|
||||||
android:data="http://davdroid.bitfire.at" />
|
android:data="http://davdroid.bitfire.at/?pk_campaign=in-app" />
|
||||||
</Preference>
|
</Preference>
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
@ -1,5 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<ContactsSource xmlns:android="http://schemas.android.com/apk/res/android">
|
<ContactsAccountType
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<EditSchema>
|
<EditSchema>
|
||||||
<DataKind
|
<DataKind
|
||||||
kind="name"
|
kind="name"
|
||||||
@ -7,9 +9,9 @@
|
|||||||
supportsDisplayName="true"
|
supportsDisplayName="true"
|
||||||
supportsMiddleName="true"
|
supportsMiddleName="true"
|
||||||
supportsFamilyName="true"
|
supportsFamilyName="true"
|
||||||
supportsPhoneticFamilyName="false"
|
supportsPhoneticFamilyName="true"
|
||||||
supportsPhoneticGivenName="false"
|
supportsPhoneticGivenName="true"
|
||||||
supportsPhoneticMiddleName="false"
|
supportsPhoneticMiddleName="true"
|
||||||
supportsPrefix="true"
|
supportsPrefix="true"
|
||||||
supportsSuffix="true" />
|
supportsSuffix="true" />
|
||||||
|
|
||||||
@ -40,7 +42,9 @@
|
|||||||
maxOccurs="1" />
|
maxOccurs="1" />
|
||||||
|
|
||||||
<DataKind kind="website" />
|
<DataKind kind="website" />
|
||||||
<DataKind kind="note" />
|
<DataKind
|
||||||
|
kind="note"
|
||||||
|
maxOccurs="1" />
|
||||||
|
|
||||||
<DataKind
|
<DataKind
|
||||||
dateWithTime="false"
|
dateWithTime="false"
|
||||||
@ -51,4 +55,4 @@
|
|||||||
yearOptional="false" />
|
yearOptional="false" />
|
||||||
</DataKind>
|
</DataKind>
|
||||||
</EditSchema>
|
</EditSchema>
|
||||||
</ContactsSource>
|
</ContactsAccountType>
|
||||||
|
@ -9,7 +9,7 @@ package at.bitfire.davdroid;
|
|||||||
|
|
||||||
public class Constants {
|
public class Constants {
|
||||||
public static final String
|
public static final String
|
||||||
APP_VERSION = "0.3.3-alpha",
|
APP_VERSION = "0.3.4-alpha",
|
||||||
|
|
||||||
ACCOUNT_TYPE = "bitfire.at.davdroid",
|
ACCOUNT_TYPE = "bitfire.at.davdroid",
|
||||||
|
|
||||||
|
46
src/at/bitfire/davdroid/ical4j/PhoneticFirstName.java
Normal file
46
src/at/bitfire/davdroid/ical4j/PhoneticFirstName.java
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package at.bitfire.davdroid.ical4j;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.fortuna.ical4j.model.ValidationException;
|
||||||
|
import net.fortuna.ical4j.vcard.Group;
|
||||||
|
import net.fortuna.ical4j.vcard.Parameter;
|
||||||
|
import net.fortuna.ical4j.vcard.Property;
|
||||||
|
import net.fortuna.ical4j.vcard.PropertyFactory;
|
||||||
|
|
||||||
|
public class PhoneticFirstName extends Property {
|
||||||
|
private static final long serialVersionUID = 8096989375023262021L;
|
||||||
|
|
||||||
|
public static final String PROPERTY_NAME = "PHONETIC-FIRST-NAME";
|
||||||
|
|
||||||
|
protected String phoneticFirstName;
|
||||||
|
|
||||||
|
|
||||||
|
public PhoneticFirstName(String value) {
|
||||||
|
super(PROPERTY_NAME);
|
||||||
|
|
||||||
|
phoneticFirstName = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getValue() {
|
||||||
|
return phoneticFirstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() throws ValidationException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements PropertyFactory<Property> {
|
||||||
|
@Override
|
||||||
|
public PhoneticFirstName createProperty(List<Parameter> params, String value) {
|
||||||
|
return new PhoneticFirstName(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PhoneticFirstName createProperty(Group group, List<Parameter> params, String value) {
|
||||||
|
return new PhoneticFirstName(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
src/at/bitfire/davdroid/ical4j/PhoneticLastName.java
Normal file
46
src/at/bitfire/davdroid/ical4j/PhoneticLastName.java
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package at.bitfire.davdroid.ical4j;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.fortuna.ical4j.model.ValidationException;
|
||||||
|
import net.fortuna.ical4j.vcard.Group;
|
||||||
|
import net.fortuna.ical4j.vcard.Parameter;
|
||||||
|
import net.fortuna.ical4j.vcard.Property;
|
||||||
|
import net.fortuna.ical4j.vcard.PropertyFactory;
|
||||||
|
|
||||||
|
public class PhoneticLastName extends Property {
|
||||||
|
private static final long serialVersionUID = 8637699713562385556L;
|
||||||
|
|
||||||
|
public static final String PROPERTY_NAME = "PHONETIC-LAST-NAME";
|
||||||
|
|
||||||
|
protected String phoneticFirstName;
|
||||||
|
|
||||||
|
|
||||||
|
public PhoneticLastName(String value) {
|
||||||
|
super(PROPERTY_NAME);
|
||||||
|
|
||||||
|
phoneticFirstName = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getValue() {
|
||||||
|
return phoneticFirstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() throws ValidationException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements PropertyFactory<Property> {
|
||||||
|
@Override
|
||||||
|
public PhoneticLastName createProperty(List<Parameter> params, String value) {
|
||||||
|
return new PhoneticLastName(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PhoneticLastName createProperty(Group group, List<Parameter> params, String value) {
|
||||||
|
return new PhoneticLastName(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
src/at/bitfire/davdroid/ical4j/PhoneticMiddleName.java
Normal file
46
src/at/bitfire/davdroid/ical4j/PhoneticMiddleName.java
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package at.bitfire.davdroid.ical4j;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import net.fortuna.ical4j.model.ValidationException;
|
||||||
|
import net.fortuna.ical4j.vcard.Group;
|
||||||
|
import net.fortuna.ical4j.vcard.Parameter;
|
||||||
|
import net.fortuna.ical4j.vcard.Property;
|
||||||
|
import net.fortuna.ical4j.vcard.PropertyFactory;
|
||||||
|
|
||||||
|
public class PhoneticMiddleName extends Property {
|
||||||
|
private static final long serialVersionUID = 1310410178765057503L;
|
||||||
|
|
||||||
|
public static final String PROPERTY_NAME = "PHONETIC-MIDDLE-NAME";
|
||||||
|
|
||||||
|
protected String phoneticFirstName;
|
||||||
|
|
||||||
|
|
||||||
|
public PhoneticMiddleName(String value) {
|
||||||
|
super(PROPERTY_NAME);
|
||||||
|
|
||||||
|
phoneticFirstName = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getValue() {
|
||||||
|
return phoneticFirstName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void validate() throws ValidationException {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class Factory implements PropertyFactory<Property> {
|
||||||
|
@Override
|
||||||
|
public PhoneticMiddleName createProperty(List<Parameter> params, String value) {
|
||||||
|
return new PhoneticMiddleName(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PhoneticMiddleName createProperty(Group group, List<Parameter> params, String value) {
|
||||||
|
return new PhoneticMiddleName(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -31,7 +31,7 @@ public class CalDavCalendar extends RemoteCollection<Event> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public CalDavCalendar(String baseURL, String user, String password, boolean preemptiveAuth) throws IOException, URISyntaxException {
|
public CalDavCalendar(String baseURL, String user, String password, boolean preemptiveAuth) throws URISyntaxException {
|
||||||
super(baseURL, user, password, preemptiveAuth);
|
super(baseURL, user, password, preemptiveAuth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ public class CardDavAddressBook extends RemoteCollection<Contact> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public CardDavAddressBook(String baseURL, String user, String password, boolean preemptiveAuth) throws IOException, URISyntaxException {
|
public CardDavAddressBook(String baseURL, String user, String password, boolean preemptiveAuth) throws URISyntaxException {
|
||||||
super(baseURL, user, password, preemptiveAuth);
|
super(baseURL, user, password, preemptiveAuth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,6 +51,9 @@ import org.apache.commons.lang.StringUtils;
|
|||||||
|
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import at.bitfire.davdroid.ical4j.PhoneticFirstName;
|
||||||
|
import at.bitfire.davdroid.ical4j.PhoneticLastName;
|
||||||
|
import at.bitfire.davdroid.ical4j.PhoneticMiddleName;
|
||||||
import at.bitfire.davdroid.ical4j.Starred;
|
import at.bitfire.davdroid.ical4j.Starred;
|
||||||
|
|
||||||
@ToString(callSuper = true)
|
@ToString(callSuper = true)
|
||||||
@ -61,6 +64,7 @@ public class Contact extends Resource {
|
|||||||
|
|
||||||
@Getter @Setter private String displayName;
|
@Getter @Setter private String displayName;
|
||||||
@Getter @Setter private String prefix, givenName, middleName, familyName, suffix;
|
@Getter @Setter private String prefix, givenName, middleName, familyName, suffix;
|
||||||
|
@Getter @Setter private String phoneticGivenName, phoneticMiddleName, phoneticFamilyName;
|
||||||
@Getter @Setter private String[] nickNames;
|
@Getter @Setter private String[] nickNames;
|
||||||
|
|
||||||
@Getter @Setter private byte[] photo;
|
@Getter @Setter private byte[] photo;
|
||||||
@ -107,17 +111,30 @@ public class Contact extends Resource {
|
|||||||
@Override
|
@Override
|
||||||
public void parseEntity(InputStream is) throws IOException, ParserException {
|
public void parseEntity(InputStream is) throws IOException, ParserException {
|
||||||
PropertyFactoryRegistry propertyFactoryRegistry = new PropertyFactoryRegistry();
|
PropertyFactoryRegistry propertyFactoryRegistry = new PropertyFactoryRegistry();
|
||||||
|
|
||||||
|
// add support for X-DAVDROID-STARRED
|
||||||
propertyFactoryRegistry.register("X-" + Starred.PROPERTY_NAME, new Starred.Factory());
|
propertyFactoryRegistry.register("X-" + Starred.PROPERTY_NAME, new Starred.Factory());
|
||||||
|
|
||||||
|
// add support for phonetic names
|
||||||
|
propertyFactoryRegistry.register("X-" + PhoneticFirstName.PROPERTY_NAME, new PhoneticFirstName.Factory());
|
||||||
|
propertyFactoryRegistry.register("X-" + PhoneticMiddleName.PROPERTY_NAME, new PhoneticMiddleName.Factory());
|
||||||
|
propertyFactoryRegistry.register("X-" + PhoneticLastName.PROPERTY_NAME, new PhoneticLastName.Factory());
|
||||||
|
|
||||||
VCardBuilder builder = new VCardBuilder(
|
VCardBuilder builder = new VCardBuilder(
|
||||||
new InputStreamReader(is),
|
new InputStreamReader(is),
|
||||||
new GroupRegistry(),
|
new GroupRegistry(),
|
||||||
propertyFactoryRegistry,
|
propertyFactoryRegistry,
|
||||||
new ParameterFactoryRegistry()
|
new ParameterFactoryRegistry()
|
||||||
);
|
);
|
||||||
VCard vcard = builder.build();
|
|
||||||
|
VCard vcard;
|
||||||
|
try {
|
||||||
|
vcard = builder.build();
|
||||||
if (vcard == null)
|
if (vcard == null)
|
||||||
return;
|
return;
|
||||||
|
} catch(Exception ex) {
|
||||||
|
throw new ParserException("VCard parser crashed", -1);
|
||||||
|
}
|
||||||
|
|
||||||
Uid uid = (Uid)vcard.getProperty(Id.UID);
|
Uid uid = (Uid)vcard.getProperty(Id.UID);
|
||||||
if (uid != null)
|
if (uid != null)
|
||||||
@ -137,6 +154,7 @@ public class Contact extends Resource {
|
|||||||
if (nickname != null)
|
if (nickname != null)
|
||||||
nickNames = nickname.getNames();
|
nickNames = nickname.getNames();
|
||||||
|
|
||||||
|
// structured name
|
||||||
N n = (N)vcard.getProperty(Id.N);
|
N n = (N)vcard.getProperty(Id.N);
|
||||||
if (n != null) {
|
if (n != null) {
|
||||||
prefix = StringUtils.join(n.getPrefixes(), " ");
|
prefix = StringUtils.join(n.getPrefixes(), " ");
|
||||||
@ -146,6 +164,17 @@ public class Contact extends Resource {
|
|||||||
suffix = StringUtils.join(n.getSuffixes(), " ");
|
suffix = StringUtils.join(n.getSuffixes(), " ");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// phonetic name
|
||||||
|
PhoneticFirstName phoneticFirstName = (PhoneticFirstName)vcard.getExtendedProperty(PhoneticFirstName.PROPERTY_NAME);
|
||||||
|
if (phoneticFirstName != null)
|
||||||
|
phoneticGivenName = phoneticFirstName.getValue();
|
||||||
|
PhoneticMiddleName phoneticMiddleName = (PhoneticMiddleName)vcard.getExtendedProperty(PhoneticMiddleName.PROPERTY_NAME);
|
||||||
|
if (phoneticMiddleName != null)
|
||||||
|
this.phoneticMiddleName = phoneticMiddleName.getValue();
|
||||||
|
PhoneticLastName phoneticLastName = (PhoneticLastName)vcard.getExtendedProperty(PhoneticLastName.PROPERTY_NAME);
|
||||||
|
if (phoneticLastName != null)
|
||||||
|
phoneticFamilyName = phoneticLastName.getValue();
|
||||||
|
|
||||||
for (Property p : vcard.getProperties(Id.EMAIL))
|
for (Property p : vcard.getProperties(Id.EMAIL))
|
||||||
emails.add((Email)p);
|
emails.add((Email)p);
|
||||||
|
|
||||||
@ -228,6 +257,13 @@ public class Contact extends Resource {
|
|||||||
properties.add(new N(familyName, givenName, StringUtils.split(middleName),
|
properties.add(new N(familyName, givenName, StringUtils.split(middleName),
|
||||||
StringUtils.split(prefix), StringUtils.split(suffix)));
|
StringUtils.split(prefix), StringUtils.split(suffix)));
|
||||||
|
|
||||||
|
if (phoneticGivenName != null)
|
||||||
|
properties.add(new PhoneticFirstName(phoneticGivenName));
|
||||||
|
if (phoneticMiddleName != null)
|
||||||
|
properties.add(new PhoneticMiddleName(phoneticMiddleName));
|
||||||
|
if (phoneticFamilyName != null)
|
||||||
|
properties.add(new PhoneticLastName(phoneticFamilyName));
|
||||||
|
|
||||||
for (Email email : emails)
|
for (Email email : emails)
|
||||||
properties.add(email);
|
properties.add(email);
|
||||||
|
|
||||||
|
@ -125,8 +125,9 @@ public class LocalAddressBook extends LocalCollection<Contact> {
|
|||||||
|
|
||||||
// structured name
|
// structured name
|
||||||
cursor = providerClient.query(dataURI(), new String[] {
|
cursor = providerClient.query(dataURI(), new String[] {
|
||||||
StructuredName.DISPLAY_NAME, StructuredName.PREFIX, StructuredName.GIVEN_NAME,
|
/* 0 */ StructuredName.DISPLAY_NAME, StructuredName.PREFIX, StructuredName.GIVEN_NAME,
|
||||||
StructuredName.MIDDLE_NAME, StructuredName.FAMILY_NAME, StructuredName.SUFFIX
|
/* 3 */ StructuredName.MIDDLE_NAME, StructuredName.FAMILY_NAME, StructuredName.SUFFIX,
|
||||||
|
/* 6 */ StructuredName.PHONETIC_GIVEN_NAME, StructuredName.PHONETIC_MIDDLE_NAME, StructuredName.PHONETIC_FAMILY_NAME
|
||||||
}, StructuredName.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
|
}, StructuredName.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
|
||||||
new String[] { String.valueOf(res.getLocalID()), StructuredName.CONTENT_ITEM_TYPE }, null);
|
new String[] { String.valueOf(res.getLocalID()), StructuredName.CONTENT_ITEM_TYPE }, null);
|
||||||
if (cursor.moveToNext()) {
|
if (cursor.moveToNext()) {
|
||||||
@ -137,6 +138,10 @@ public class LocalAddressBook extends LocalCollection<Contact> {
|
|||||||
c.setMiddleName(cursor.getString(3));
|
c.setMiddleName(cursor.getString(3));
|
||||||
c.setFamilyName(cursor.getString(4));
|
c.setFamilyName(cursor.getString(4));
|
||||||
c.setSuffix(cursor.getString(5));
|
c.setSuffix(cursor.getString(5));
|
||||||
|
|
||||||
|
c.setPhoneticGivenName(cursor.getString(6));
|
||||||
|
c.setPhoneticMiddleName(cursor.getString(7));
|
||||||
|
c.setPhoneticFamilyName(cursor.getString(8));
|
||||||
}
|
}
|
||||||
|
|
||||||
// nick names
|
// nick names
|
||||||
@ -347,7 +352,10 @@ public class LocalAddressBook extends LocalCollection<Contact> {
|
|||||||
.withValue(StructuredName.GIVEN_NAME, contact.getGivenName())
|
.withValue(StructuredName.GIVEN_NAME, contact.getGivenName())
|
||||||
.withValue(StructuredName.MIDDLE_NAME, contact.getMiddleName())
|
.withValue(StructuredName.MIDDLE_NAME, contact.getMiddleName())
|
||||||
.withValue(StructuredName.FAMILY_NAME, contact.getFamilyName())
|
.withValue(StructuredName.FAMILY_NAME, contact.getFamilyName())
|
||||||
.withValue(StructuredName.SUFFIX, contact.getSuffix());
|
.withValue(StructuredName.SUFFIX, contact.getSuffix())
|
||||||
|
.withValue(StructuredName.PHONETIC_GIVEN_NAME, contact.getPhoneticGivenName())
|
||||||
|
.withValue(StructuredName.PHONETIC_MIDDLE_NAME, contact.getPhoneticMiddleName())
|
||||||
|
.withValue(StructuredName.PHONETIC_FAMILY_NAME, contact.getPhoneticFamilyName());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Builder buildNickName(Builder builder, String nickName) {
|
protected Builder buildNickName(Builder builder, String nickName) {
|
||||||
|
@ -11,7 +11,6 @@ import java.net.URISyntaxException;
|
|||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import net.fortuna.ical4j.model.Parameter;
|
import net.fortuna.ical4j.model.Parameter;
|
||||||
import net.fortuna.ical4j.model.ParameterList;
|
import net.fortuna.ical4j.model.ParameterList;
|
||||||
@ -26,11 +25,10 @@ import net.fortuna.ical4j.model.property.Organizer;
|
|||||||
import net.fortuna.ical4j.model.property.RDate;
|
import net.fortuna.ical4j.model.property.RDate;
|
||||||
import net.fortuna.ical4j.model.property.RRule;
|
import net.fortuna.ical4j.model.property.RRule;
|
||||||
import net.fortuna.ical4j.model.property.Status;
|
import net.fortuna.ical4j.model.property.Status;
|
||||||
|
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
|
import android.annotation.TargetApi;
|
||||||
import android.content.ContentProviderClient;
|
import android.content.ContentProviderClient;
|
||||||
import android.content.ContentProviderOperation;
|
import android.content.ContentProviderOperation;
|
||||||
import android.content.ContentProviderOperation.Builder;
|
import android.content.ContentProviderOperation.Builder;
|
||||||
@ -40,6 +38,7 @@ import android.content.ContentValues;
|
|||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.DatabaseUtils;
|
import android.database.DatabaseUtils;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.provider.CalendarContract;
|
import android.provider.CalendarContract;
|
||||||
import android.provider.CalendarContract.Attendees;
|
import android.provider.CalendarContract.Attendees;
|
||||||
@ -75,9 +74,9 @@ public class LocalCalendar extends LocalCollection<Event> {
|
|||||||
protected String entryColumnDirty() { return Events.DIRTY; }
|
protected String entryColumnDirty() { return Events.DIRTY; }
|
||||||
protected String entryColumnDeleted() { return Events.DELETED; }
|
protected String entryColumnDeleted() { return Events.DELETED; }
|
||||||
|
|
||||||
@SuppressLint("InlinedApi")
|
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||||
protected String entryColumnUID() {
|
protected String entryColumnUID() {
|
||||||
return (android.os.Build.VERSION.SDK_INT >= 17) ?
|
return (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) ?
|
||||||
Events.UID_2445 : Events.SYNC_DATA2;
|
Events.UID_2445 : Events.SYNC_DATA2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,20 +19,24 @@ import net.fortuna.ical4j.model.ValidationException;
|
|||||||
|
|
||||||
import org.apache.http.HttpException;
|
import org.apache.http.HttpException;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
import at.bitfire.davdroid.webdav.HttpPropfind;
|
import at.bitfire.davdroid.webdav.HttpPropfind;
|
||||||
|
import at.bitfire.davdroid.webdav.InvalidDavResponseException;
|
||||||
import at.bitfire.davdroid.webdav.WebDavCollection;
|
import at.bitfire.davdroid.webdav.WebDavCollection;
|
||||||
import at.bitfire.davdroid.webdav.WebDavCollection.MultigetType;
|
import at.bitfire.davdroid.webdav.WebDavCollection.MultigetType;
|
||||||
import at.bitfire.davdroid.webdav.WebDavResource;
|
import at.bitfire.davdroid.webdav.WebDavResource;
|
||||||
import at.bitfire.davdroid.webdav.WebDavResource.PutMode;
|
import at.bitfire.davdroid.webdav.WebDavResource.PutMode;
|
||||||
|
|
||||||
public abstract class RemoteCollection<ResourceType extends Resource> {
|
public abstract class RemoteCollection<ResourceType extends Resource> {
|
||||||
|
private static final String TAG = "davdroid.RemoteCollection";
|
||||||
|
|
||||||
@Getter WebDavCollection collection;
|
@Getter WebDavCollection collection;
|
||||||
|
|
||||||
abstract protected String memberContentType();
|
abstract protected String memberContentType();
|
||||||
abstract protected MultigetType multiGetType();
|
abstract protected MultigetType multiGetType();
|
||||||
abstract protected ResourceType newResourceSkeleton(String name, String ETag);
|
abstract protected ResourceType newResourceSkeleton(String name, String ETag);
|
||||||
|
|
||||||
public RemoteCollection(String baseURL, String user, String password, boolean preemptiveAuth) throws IOException, URISyntaxException {
|
public RemoteCollection(String baseURL, String user, String password, boolean preemptiveAuth) throws URISyntaxException {
|
||||||
collection = new WebDavCollection(new URI(baseURL), user, password, preemptiveAuth);
|
collection = new WebDavCollection(new URI(baseURL), user, password, preemptiveAuth);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,13 +47,13 @@ public abstract class RemoteCollection<ResourceType extends Resource> {
|
|||||||
try {
|
try {
|
||||||
if (collection.getCTag() == null && collection.getMembers() == null) // not already fetched
|
if (collection.getCTag() == null && collection.getMembers() == null) // not already fetched
|
||||||
collection.propfind(HttpPropfind.Mode.COLLECTION_CTAG);
|
collection.propfind(HttpPropfind.Mode.COLLECTION_CTAG);
|
||||||
} catch (IncapableResourceException e) {
|
} catch (InvalidDavResponseException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return collection.getCTag();
|
return collection.getCTag();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Resource[] getMemberETags() throws IOException, IncapableResourceException, HttpException {
|
public Resource[] getMemberETags() throws IOException, InvalidDavResponseException, HttpException {
|
||||||
collection.propfind(HttpPropfind.Mode.MEMBERS_ETAG);
|
collection.propfind(HttpPropfind.Mode.MEMBERS_ETAG);
|
||||||
|
|
||||||
List<ResourceType> resources = new LinkedList<ResourceType>();
|
List<ResourceType> resources = new LinkedList<ResourceType>();
|
||||||
@ -59,7 +63,7 @@ public abstract class RemoteCollection<ResourceType extends Resource> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Resource[] multiGet(ResourceType[] resources) throws IOException, IncapableResourceException, HttpException, ParserException {
|
public Resource[] multiGet(ResourceType[] resources) throws IOException, InvalidDavResponseException, HttpException {
|
||||||
try {
|
try {
|
||||||
if (resources.length == 1) {
|
if (resources.length == 1) {
|
||||||
Resource resource = get(resources[0]);
|
Resource resource = get(resources[0]);
|
||||||
@ -75,19 +79,25 @@ public abstract class RemoteCollection<ResourceType extends Resource> {
|
|||||||
LinkedList<ResourceType> foundResources = new LinkedList<ResourceType>();
|
LinkedList<ResourceType> foundResources = new LinkedList<ResourceType>();
|
||||||
for (WebDavResource member : collection.getMembers()) {
|
for (WebDavResource member : collection.getMembers()) {
|
||||||
ResourceType resource = newResourceSkeleton(member.getName(), member.getETag());
|
ResourceType resource = newResourceSkeleton(member.getName(), member.getETag());
|
||||||
|
try {
|
||||||
resource.parseEntity(member.getContent());
|
resource.parseEntity(member.getContent());
|
||||||
foundResources.add(resource);
|
foundResources.add(resource);
|
||||||
|
} catch (ParserException ex) {
|
||||||
|
Log.e(TAG, "Ignoring unparseable entity in multi-response: " + ex.toString(), ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return foundResources.toArray(new Resource[0]);
|
return foundResources.toArray(new Resource[0]);
|
||||||
} catch(ValidationException ex) {
|
} catch (ParserException ex) {
|
||||||
return null;
|
Log.w(TAG, "Couldn't parse single multi-get entity", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new Resource[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* internal member operations */
|
/* internal member operations */
|
||||||
|
|
||||||
public ResourceType get(ResourceType resource) throws IOException, HttpException, ParserException, ValidationException {
|
public ResourceType get(ResourceType resource) throws IOException, HttpException, ParserException {
|
||||||
WebDavResource member = new WebDavResource(collection, resource.getName());
|
WebDavResource member = new WebDavResource(collection, resource.getName());
|
||||||
member.get();
|
member.get();
|
||||||
resource.parseEntity(member.getContent());
|
resource.parseEntity(member.getContent());
|
||||||
|
@ -10,11 +10,14 @@ package at.bitfire.davdroid.syncadapter;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import lombok.Synchronized;
|
import lombok.Synchronized;
|
||||||
import net.fortuna.ical4j.data.ParserException;
|
import net.fortuna.ical4j.data.ParserException;
|
||||||
|
|
||||||
import org.apache.http.HttpException;
|
import org.apache.http.HttpException;
|
||||||
|
import org.apache.http.auth.AuthenticationException;
|
||||||
|
|
||||||
import android.accounts.Account;
|
import android.accounts.Account;
|
||||||
import android.accounts.AccountManager;
|
import android.accounts.AccountManager;
|
||||||
@ -34,6 +37,7 @@ import at.bitfire.davdroid.Constants;
|
|||||||
import at.bitfire.davdroid.resource.CalDavCalendar;
|
import at.bitfire.davdroid.resource.CalDavCalendar;
|
||||||
import at.bitfire.davdroid.resource.IncapableResourceException;
|
import at.bitfire.davdroid.resource.IncapableResourceException;
|
||||||
import at.bitfire.davdroid.resource.LocalCalendar;
|
import at.bitfire.davdroid.resource.LocalCalendar;
|
||||||
|
import at.bitfire.davdroid.resource.LocalCollection;
|
||||||
import at.bitfire.davdroid.resource.RemoteCollection;
|
import at.bitfire.davdroid.resource.RemoteCollection;
|
||||||
import at.bitfire.davdroid.webdav.WebDavResource;
|
import at.bitfire.davdroid.webdav.WebDavResource;
|
||||||
|
|
||||||
@ -51,58 +55,35 @@ public class CalendarsSyncAdapterService extends Service {
|
|||||||
return syncAdapter.getSyncAdapterBinder();
|
return syncAdapter.getSyncAdapterBinder();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SyncAdapter extends AbstractThreadedSyncAdapter {
|
private static class SyncAdapter extends DavSyncAdapter {
|
||||||
private final static String TAG = "davdroid.CalendarsSyncAdapter";
|
private final static String TAG = "davdroid.CalendarsSyncAdapter";
|
||||||
private AccountManager accountManager;
|
|
||||||
|
|
||||||
public SyncAdapter(Context context) {
|
public SyncAdapter(Context context) {
|
||||||
super(context, true);
|
super(context);
|
||||||
accountManager = AccountManager.get(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider,
|
protected Map<LocalCollection, RemoteCollection> getSyncPairs(Account account, ContentProviderClient provider) {
|
||||||
SyncResult syncResult) {
|
|
||||||
Log.i(TAG, "Performing sync for authority " + authority);
|
|
||||||
|
|
||||||
// set class loader for iCal4j ResourceLoader
|
|
||||||
Thread.currentThread().setContextClassLoader(getContext().getClassLoader());
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
SyncManager syncManager = new SyncManager(account, accountManager);
|
Map<LocalCollection, RemoteCollection> map = new HashMap<LocalCollection, RemoteCollection>();
|
||||||
|
|
||||||
LocalCalendar[] calendars = LocalCalendar.findAll(account, provider);
|
for (LocalCalendar calendar : LocalCalendar.findAll(account, provider)) {
|
||||||
for (LocalCalendar calendar : calendars) {
|
|
||||||
URI uri = new URI(accountManager.getUserData(account, Constants.ACCOUNT_KEY_BASE_URL)).resolve(calendar.getPath());
|
URI uri = new URI(accountManager.getUserData(account, Constants.ACCOUNT_KEY_BASE_URL)).resolve(calendar.getPath());
|
||||||
RemoteCollection dav = new CalDavCalendar(uri.toString(),
|
RemoteCollection dav = new CalDavCalendar(uri.toString(),
|
||||||
accountManager.getUserData(account, Constants.ACCOUNT_KEY_USERNAME),
|
accountManager.getUserData(account, Constants.ACCOUNT_KEY_USERNAME),
|
||||||
accountManager.getPassword(account),
|
accountManager.getPassword(account),
|
||||||
Boolean.parseBoolean(accountManager.getUserData(account, Constants.ACCOUNT_KEY_AUTH_PREEMPTIVE)));
|
Boolean.parseBoolean(accountManager.getUserData(account, Constants.ACCOUNT_KEY_AUTH_PREEMPTIVE)));
|
||||||
syncManager.synchronize(calendar, dav, extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL), syncResult);
|
|
||||||
|
map.put(calendar, dav);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
} catch (RemoteException ex) {
|
||||||
|
Log.e(TAG, "Couldn't find local calendars", ex);
|
||||||
|
} catch (URISyntaxException ex) {
|
||||||
|
Log.e(TAG, "Couldn't build calendar URI", ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (HttpException e) {
|
return null;
|
||||||
syncResult.stats.numParseExceptions++;
|
|
||||||
Log.e(TAG, e.toString());
|
|
||||||
} catch (ParserException e) {
|
|
||||||
syncResult.stats.numParseExceptions++;
|
|
||||||
Log.e(TAG, e.toString());
|
|
||||||
} catch (RemoteException e) {
|
|
||||||
syncResult.stats.numParseExceptions++;
|
|
||||||
Log.e(TAG, e.getLocalizedMessage());
|
|
||||||
} catch (OperationApplicationException e) {
|
|
||||||
syncResult.stats.numParseExceptions++;
|
|
||||||
Log.e(TAG, e.getLocalizedMessage());
|
|
||||||
} catch (IOException e) {
|
|
||||||
syncResult.stats.numIoExceptions++;
|
|
||||||
Log.e(TAG, e.toString());
|
|
||||||
} catch (IncapableResourceException e) {
|
|
||||||
syncResult.stats.numParseExceptions++;
|
|
||||||
Log.e(TAG, e.toString());
|
|
||||||
} catch (URISyntaxException e) {
|
|
||||||
syncResult.stats.numParseExceptions++;
|
|
||||||
Log.e(TAG, e.toString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,11 @@ package at.bitfire.davdroid.syncadapter;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import lombok.NonNull;
|
||||||
import lombok.Synchronized;
|
import lombok.Synchronized;
|
||||||
import net.fortuna.ical4j.data.ParserException;
|
import net.fortuna.ical4j.data.ParserException;
|
||||||
|
|
||||||
@ -38,12 +42,12 @@ import at.bitfire.davdroid.resource.LocalCollection;
|
|||||||
import at.bitfire.davdroid.resource.RemoteCollection;
|
import at.bitfire.davdroid.resource.RemoteCollection;
|
||||||
|
|
||||||
public class ContactsSyncAdapterService extends Service {
|
public class ContactsSyncAdapterService extends Service {
|
||||||
private static SyncAdapter syncAdapter;
|
private static ContactsSyncAdapter syncAdapter;
|
||||||
|
|
||||||
@Override @Synchronized
|
@Override @Synchronized
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
if (syncAdapter == null)
|
if (syncAdapter == null)
|
||||||
syncAdapter = new SyncAdapter(getApplicationContext());
|
syncAdapter = new ContactsSyncAdapter(getApplicationContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -51,59 +55,38 @@ public class ContactsSyncAdapterService extends Service {
|
|||||||
return syncAdapter.getSyncAdapterBinder();
|
return syncAdapter.getSyncAdapterBinder();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class SyncAdapter extends AbstractThreadedSyncAdapter {
|
private static class ContactsSyncAdapter extends DavSyncAdapter {
|
||||||
private final static String TAG = "davdroid.ContactsSyncAdapter";
|
private final static String TAG = "davdroid.ContactsSyncAdapter";
|
||||||
private AccountManager accountManager;
|
|
||||||
|
|
||||||
public SyncAdapter(Context context) {
|
public ContactsSyncAdapter(Context context) {
|
||||||
super(context, true);
|
super(context);
|
||||||
accountManager = AccountManager.get(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
|
protected Map<LocalCollection, RemoteCollection> getSyncPairs(Account account, ContentProviderClient provider) {
|
||||||
Log.i(TAG, "Performing sync for authority " + authority);
|
|
||||||
|
|
||||||
String addressBookPath = accountManager.getUserData(account, Constants.ACCOUNT_KEY_ADDRESSBOOK_PATH);
|
String addressBookPath = accountManager.getUserData(account, Constants.ACCOUNT_KEY_ADDRESSBOOK_PATH);
|
||||||
if (addressBookPath == null)
|
if (addressBookPath == null)
|
||||||
return;
|
return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
URI uri = new URI(accountManager.getUserData(account, Constants.ACCOUNT_KEY_BASE_URL)).resolve(addressBookPath);
|
LocalCollection database = new LocalAddressBook(account, provider, accountManager);
|
||||||
|
|
||||||
|
URI uri = new URI(accountManager.getUserData(account, Constants.ACCOUNT_KEY_BASE_URL)).resolve(addressBookPath);
|
||||||
RemoteCollection dav = new CardDavAddressBook(
|
RemoteCollection dav = new CardDavAddressBook(
|
||||||
uri.toString(),
|
uri.toString(),
|
||||||
accountManager.getUserData(account, Constants.ACCOUNT_KEY_USERNAME),
|
accountManager.getUserData(account, Constants.ACCOUNT_KEY_USERNAME),
|
||||||
accountManager.getPassword(account),
|
accountManager.getPassword(account),
|
||||||
Boolean.parseBoolean(accountManager.getUserData(account, Constants.ACCOUNT_KEY_AUTH_PREEMPTIVE)));
|
Boolean.parseBoolean(accountManager.getUserData(account, Constants.ACCOUNT_KEY_AUTH_PREEMPTIVE)));
|
||||||
|
|
||||||
LocalCollection database = new LocalAddressBook(account, provider, accountManager);
|
Map<LocalCollection, RemoteCollection> map = new HashMap<LocalCollection, RemoteCollection>();
|
||||||
|
map.put(database, dav);
|
||||||
|
|
||||||
SyncManager syncManager = new SyncManager(account, accountManager);
|
return map;
|
||||||
syncManager.synchronize(database, dav, extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL), syncResult);
|
} catch (URISyntaxException ex) {
|
||||||
|
Log.e(TAG, "Couldn't build address book URI", ex);
|
||||||
} catch (IOException e) {
|
|
||||||
syncResult.stats.numIoExceptions++;
|
|
||||||
Log.e(TAG, e.toString());
|
|
||||||
} catch (ParserException e) {
|
|
||||||
syncResult.stats.numParseExceptions++;
|
|
||||||
Log.e(TAG, e.toString());
|
|
||||||
} catch (HttpException e) {
|
|
||||||
syncResult.stats.numParseExceptions++;
|
|
||||||
Log.e(TAG, e.toString());
|
|
||||||
} catch (IncapableResourceException e) {
|
|
||||||
syncResult.stats.numParseExceptions++;
|
|
||||||
Log.e(TAG, e.toString());
|
|
||||||
} catch(RemoteException e) {
|
|
||||||
syncResult.databaseError = true;
|
|
||||||
Log.e(TAG, e.toString());
|
|
||||||
} catch(OperationApplicationException e) {
|
|
||||||
syncResult.databaseError = true;
|
|
||||||
Log.e(TAG, e.toString());
|
|
||||||
} catch (URISyntaxException e) {
|
|
||||||
syncResult.stats.numIoExceptions++;
|
|
||||||
Log.e(TAG, e.toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
78
src/at/bitfire/davdroid/syncadapter/DavSyncAdapter.java
Normal file
78
src/at/bitfire/davdroid/syncadapter/DavSyncAdapter.java
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package at.bitfire.davdroid.syncadapter;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import org.apache.http.HttpException;
|
||||||
|
import org.apache.http.auth.AuthenticationException;
|
||||||
|
|
||||||
|
import android.accounts.Account;
|
||||||
|
import android.accounts.AccountManager;
|
||||||
|
import android.content.AbstractThreadedSyncAdapter;
|
||||||
|
import android.content.ContentProviderClient;
|
||||||
|
import android.content.ContentResolver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.OperationApplicationException;
|
||||||
|
import android.content.SyncResult;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.util.Log;
|
||||||
|
import at.bitfire.davdroid.resource.IncapableResourceException;
|
||||||
|
import at.bitfire.davdroid.resource.LocalCollection;
|
||||||
|
import at.bitfire.davdroid.resource.RemoteCollection;
|
||||||
|
import at.bitfire.davdroid.webdav.InvalidDavResponseException;
|
||||||
|
|
||||||
|
public abstract class DavSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
|
private final static String TAG = "davdroid.DavSyncAdapter";
|
||||||
|
|
||||||
|
protected AccountManager accountManager;
|
||||||
|
|
||||||
|
|
||||||
|
public DavSyncAdapter(Context context) {
|
||||||
|
super(context, true);
|
||||||
|
accountManager = AccountManager.get(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected abstract Map<LocalCollection, RemoteCollection> getSyncPairs(Account account, ContentProviderClient provider);
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
|
||||||
|
Log.i(TAG, "Performing sync for authority " + authority);
|
||||||
|
|
||||||
|
// set class loader for iCal4j ResourceLoader
|
||||||
|
Thread.currentThread().setContextClassLoader(getContext().getClassLoader());
|
||||||
|
|
||||||
|
SyncManager syncManager = new SyncManager(account, accountManager);
|
||||||
|
|
||||||
|
Map<LocalCollection, RemoteCollection> syncCollections = getSyncPairs(account, provider);
|
||||||
|
if (syncCollections == null)
|
||||||
|
Log.i(TAG, "Nothing to synchronize");
|
||||||
|
else
|
||||||
|
try {
|
||||||
|
for (Map.Entry<LocalCollection, RemoteCollection> entry : syncCollections.entrySet())
|
||||||
|
syncManager.synchronize(entry.getKey(), entry.getValue(), extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL), syncResult);
|
||||||
|
|
||||||
|
} catch (AuthenticationException ex) {
|
||||||
|
syncResult.stats.numAuthExceptions++;
|
||||||
|
Log.e(TAG, "HTTP authorization error", ex);
|
||||||
|
} catch (InvalidDavResponseException ex) {
|
||||||
|
syncResult.stats.numParseExceptions++;
|
||||||
|
Log.e(TAG, "Invalid DAV response", ex);
|
||||||
|
} catch (HttpException ex) {
|
||||||
|
syncResult.stats.numIoExceptions++;
|
||||||
|
Log.e(TAG, "HTTP error", ex);
|
||||||
|
} catch (OperationApplicationException ex) {
|
||||||
|
syncResult.databaseError = true;
|
||||||
|
Log.e(TAG, "Content provider operation error", ex);
|
||||||
|
} catch (RemoteException ex) {
|
||||||
|
syncResult.databaseError = true;
|
||||||
|
Log.e(TAG, "Remote process (content provider?) died", ex);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
syncResult.stats.numIoExceptions++;
|
||||||
|
Log.e(TAG, "I/O error", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -40,7 +40,7 @@ public class SyncManager {
|
|||||||
this.accountManager = accountManager;
|
this.accountManager = accountManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void synchronize(LocalCollection local, RemoteCollection dav, boolean manualSync, SyncResult syncResult) throws RemoteException, OperationApplicationException, IOException, IncapableResourceException, HttpException, ParserException {
|
public void synchronize(LocalCollection local, RemoteCollection dav, boolean manualSync, SyncResult syncResult) throws RemoteException, OperationApplicationException, IOException, HttpException {
|
||||||
boolean fetchCollection = false;
|
boolean fetchCollection = false;
|
||||||
|
|
||||||
// PHASE 1: UPLOAD LOCALLY-CHANGED RESOURCES
|
// PHASE 1: UPLOAD LOCALLY-CHANGED RESOURCES
|
||||||
@ -139,8 +139,8 @@ public class SyncManager {
|
|||||||
Log.i(TAG, "Adding " + res.getName());
|
Log.i(TAG, "Adding " + res.getName());
|
||||||
try {
|
try {
|
||||||
local.add(res);
|
local.add(res);
|
||||||
} catch (ValidationException e) {
|
} catch (ValidationException ex) {
|
||||||
Log.e(TAG, "Invalid resource: " + res.getName());
|
Log.w(TAG, "Ignoring invalid remote resource: " + res.getName(), ex);
|
||||||
}
|
}
|
||||||
syncResult.stats.numInserts++;
|
syncResult.stats.numInserts++;
|
||||||
}
|
}
|
||||||
@ -151,8 +151,8 @@ public class SyncManager {
|
|||||||
for (Resource res : dav.multiGet(resourcesToUpdate.toArray(new Resource[0]))) {
|
for (Resource res : dav.multiGet(resourcesToUpdate.toArray(new Resource[0]))) {
|
||||||
try {
|
try {
|
||||||
local.updateByRemoteName(res);
|
local.updateByRemoteName(res);
|
||||||
} catch (ValidationException e) {
|
} catch (ValidationException ex) {
|
||||||
Log.e(TAG, "Invalid resource: " + res.getName());
|
Log.e(TAG, "Ignoring invalid remote resource: " + res.getName(), ex);
|
||||||
}
|
}
|
||||||
Log.i(TAG, "Updating " + res.getName());
|
Log.i(TAG, "Updating " + res.getName());
|
||||||
syncResult.stats.numInserts++;
|
syncResult.stats.numInserts++;
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
package at.bitfire.davdroid.webdav;
|
||||||
|
|
||||||
|
import org.apache.http.HttpException;
|
||||||
|
|
||||||
|
public class InvalidDavResponseException extends HttpException {
|
||||||
|
private static final long serialVersionUID = -2118919144443165706L;
|
||||||
|
|
||||||
|
public InvalidDavResponseException() {
|
||||||
|
super("Invalid DAV response");
|
||||||
|
}
|
||||||
|
}
|
@ -44,7 +44,7 @@ public class WebDavCollection extends WebDavResource {
|
|||||||
@Getter protected List<WebDavResource> members = new LinkedList<WebDavResource>();
|
@Getter protected List<WebDavResource> members = new LinkedList<WebDavResource>();
|
||||||
|
|
||||||
|
|
||||||
public WebDavCollection(URI baseURL, String username, String password, boolean preemptiveAuth) throws IOException {
|
public WebDavCollection(URI baseURL, String username, String password, boolean preemptiveAuth) {
|
||||||
super(baseURL, username, password, preemptiveAuth);
|
super(baseURL, username, password, preemptiveAuth);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ public class WebDavCollection extends WebDavResource {
|
|||||||
/* collection operations */
|
/* collection operations */
|
||||||
|
|
||||||
|
|
||||||
public boolean propfind(HttpPropfind.Mode mode) throws IOException, IncapableResourceException, HttpException {
|
public boolean propfind(HttpPropfind.Mode mode) throws IOException, InvalidDavResponseException, HttpException {
|
||||||
HttpPropfind propfind = new HttpPropfind(location, mode);
|
HttpPropfind propfind = new HttpPropfind(location, mode);
|
||||||
HttpResponse response = client.execute(propfind);
|
HttpResponse response = client.execute(propfind);
|
||||||
checkResponse(response);
|
checkResponse(response);
|
||||||
@ -74,9 +74,9 @@ public class WebDavCollection extends WebDavResource {
|
|||||||
multistatus = serializer.read(DavMultistatus.class, is, false);
|
multistatus = serializer.read(DavMultistatus.class, is, false);
|
||||||
|
|
||||||
Log.d(TAG, "Received multistatus response: " + baos.toString("UTF-8"));
|
Log.d(TAG, "Received multistatus response: " + baos.toString("UTF-8"));
|
||||||
} catch (Exception e) {
|
} catch (Exception ex) {
|
||||||
Log.w(TAG, e);
|
Log.w(TAG, "Invalid PROPFIND XML response", ex);
|
||||||
throw new IncapableResourceException();
|
throw new InvalidDavResponseException();
|
||||||
}
|
}
|
||||||
processMultiStatus(multistatus);
|
processMultiStatus(multistatus);
|
||||||
return true;
|
return true;
|
||||||
@ -85,7 +85,7 @@ public class WebDavCollection extends WebDavResource {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean multiGet(String[] names, MultigetType type) throws IOException, IncapableResourceException, HttpException {
|
public boolean multiGet(String[] names, MultigetType type) throws IOException, InvalidDavResponseException, HttpException {
|
||||||
DavMultiget multiget = (type == MultigetType.ADDRESS_BOOK) ? new DavAddressbookMultiget() : new DavCalendarMultiget();
|
DavMultiget multiget = (type == MultigetType.ADDRESS_BOOK) ? new DavAddressbookMultiget() : new DavCalendarMultiget();
|
||||||
|
|
||||||
multiget.prop = new DavProp();
|
multiget.prop = new DavProp();
|
||||||
@ -128,7 +128,7 @@ public class WebDavCollection extends WebDavResource {
|
|||||||
processMultiStatus(multistatus);
|
processMultiStatus(multistatus);
|
||||||
|
|
||||||
} else
|
} else
|
||||||
throw new IncapableResourceException();
|
throw new InvalidDavResponseException();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ public class WebDavResource {
|
|||||||
protected DefaultHttpClient client;
|
protected DefaultHttpClient client;
|
||||||
|
|
||||||
|
|
||||||
public WebDavResource(URI baseURL, String username, String password, boolean preemptive) throws IOException {
|
public WebDavResource(URI baseURL, String username, String password, boolean preemptive) {
|
||||||
location = baseURL.normalize();
|
location = baseURL.normalize();
|
||||||
|
|
||||||
client = new DefaultHttpClient();
|
client = new DefaultHttpClient();
|
||||||
|
Loading…
Reference in New Issue
Block a user