mirror of
https://github.com/etesync/android
synced 2024-11-26 18:08:11 +00:00
0.5.6α Wage Slave
* support primary (preferred) phone numbers/email addresses * fix (crash) bug in URI sanitation * don't set calendar properties not supported by device's Android version * require already-set remote file name when finding updated records * version bump to 0.5.6α
This commit is contained in:
parent
4dc65d0144
commit
4f1488ece5
@ -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="23"
|
android:versionCode="24"
|
||||||
android:versionName="0.5.5-alpha" >
|
android:versionName="0.5.6-alpha" >
|
||||||
|
|
||||||
<uses-sdk
|
<uses-sdk
|
||||||
android:minSdkVersion="14"
|
android:minSdkVersion="14"
|
||||||
|
@ -46,13 +46,9 @@
|
|||||||
<Type type="custom" />
|
<Type type="custom" />
|
||||||
</DataKind>
|
</DataKind>
|
||||||
|
|
||||||
<DataKind
|
<DataKind kind="photo" maxOccurs="1" />
|
||||||
kind="photo"
|
|
||||||
maxOccurs="1" />
|
|
||||||
|
|
||||||
<DataKind
|
<DataKind kind="organization" maxOccurs="1" />
|
||||||
kind="organization"
|
|
||||||
maxOccurs="1" />
|
|
||||||
|
|
||||||
<DataKind kind="im" >
|
<DataKind kind="im" >
|
||||||
<Type type="aim" />
|
<Type type="aim" />
|
||||||
@ -66,17 +62,11 @@
|
|||||||
<Type type="custom" />
|
<Type type="custom" />
|
||||||
</DataKind>
|
</DataKind>
|
||||||
|
|
||||||
<DataKind
|
<DataKind kind="nickname" maxOccurs="1" />
|
||||||
kind="nickname"
|
|
||||||
maxOccurs="1" />
|
|
||||||
|
|
||||||
<DataKind
|
<DataKind kind="note" maxOccurs="1" />
|
||||||
kind="note"
|
|
||||||
maxOccurs="1" />
|
|
||||||
|
|
||||||
<DataKind
|
<DataKind kind="postal" needsStructured="true" >
|
||||||
kind="postal"
|
|
||||||
needsStructured="true" >
|
|
||||||
<Type type="home" />
|
<Type type="home" />
|
||||||
<Type type="work" />
|
<Type type="work" />
|
||||||
<Type type="other" />
|
<Type type="other" />
|
||||||
@ -85,13 +75,8 @@
|
|||||||
|
|
||||||
<DataKind kind="website" />
|
<DataKind kind="website" />
|
||||||
|
|
||||||
<DataKind
|
<DataKind kind="event" dateWithTime="false" >
|
||||||
dateWithTime="false"
|
<Type maxOccurs="1" type="birthday" yearOptional="false" />
|
||||||
kind="event" >
|
|
||||||
<Type
|
|
||||||
maxOccurs="1"
|
|
||||||
type="birthday"
|
|
||||||
yearOptional="false" />
|
|
||||||
<Type type="anniversary" />
|
<Type type="anniversary" />
|
||||||
</DataKind>
|
</DataKind>
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ package at.bitfire.davdroid;
|
|||||||
|
|
||||||
public class Constants {
|
public class Constants {
|
||||||
public static final String
|
public static final String
|
||||||
APP_VERSION = "0.5.5-alpha",
|
APP_VERSION = "0.5.6-alpha",
|
||||||
|
|
||||||
ACCOUNT_TYPE = "bitfire.at.davdroid",
|
ACCOUNT_TYPE = "bitfire.at.davdroid",
|
||||||
|
|
||||||
|
@ -23,6 +23,9 @@ public class URIUtils {
|
|||||||
|
|
||||||
// handles invalid URLs/paths as good as possible
|
// handles invalid URLs/paths as good as possible
|
||||||
public static String sanitize(String original) {
|
public static String sanitize(String original) {
|
||||||
|
if (original == null)
|
||||||
|
return null;
|
||||||
|
|
||||||
Pattern p = Pattern.compile("^((https?:)?//([^/]+))?(.*)", Pattern.CASE_INSENSITIVE);
|
Pattern p = Pattern.compile("^((https?:)?//([^/]+))?(.*)", Pattern.CASE_INSENSITIVE);
|
||||||
// $1: "http://hostname" or "https://hostname" or "//hostname" or empty (hostname may end with":port")
|
// $1: "http://hostname" or "https://hostname" or "//hostname" or empty (hostname may end with":port")
|
||||||
// $2: "http:" or "https:" or empty
|
// $2: "http:" or "https:" or empty
|
||||||
@ -30,7 +33,7 @@ public class URIUtils {
|
|||||||
// $4: path or empty
|
// $4: path or empty
|
||||||
|
|
||||||
Matcher m = p.matcher(original);
|
Matcher m = p.matcher(original);
|
||||||
if (m.find()) {
|
if (m.matches()) {
|
||||||
String schema = m.group(2),
|
String schema = m.group(2),
|
||||||
host = m.group(3),
|
host = m.group(3),
|
||||||
path = m.group(4);
|
path = m.group(4);
|
||||||
|
@ -193,7 +193,7 @@ public class LocalAddressBook extends LocalCollection<Contact> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void populatePhoneNumbers(Contact c) throws RemoteException {
|
protected void populatePhoneNumbers(Contact c) throws RemoteException {
|
||||||
@Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { Phone.TYPE, Phone.LABEL, Phone.NUMBER },
|
@Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { Phone.TYPE, Phone.LABEL, Phone.NUMBER, Phone.IS_SUPER_PRIMARY },
|
||||||
Phone.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
|
Phone.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
|
||||||
new String[] { String.valueOf(c.getLocalID()), Phone.CONTENT_ITEM_TYPE }, null);
|
new String[] { String.valueOf(c.getLocalID()), Phone.CONTENT_ITEM_TYPE }, null);
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
@ -265,12 +265,14 @@ public class LocalAddressBook extends LocalCollection<Contact> {
|
|||||||
if (!StringUtils.isEmpty(customType))
|
if (!StringUtils.isEmpty(customType))
|
||||||
number.addType(TelephoneType.get(labelToXName(customType)));
|
number.addType(TelephoneType.get(labelToXName(customType)));
|
||||||
}
|
}
|
||||||
|
if (cursor.getInt(3) != 0) // IS_PRIMARY
|
||||||
|
number.addType(TelephoneType.PREF);
|
||||||
c.getPhoneNumbers().add(number);
|
c.getPhoneNumbers().add(number);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void populateEmailAddresses(Contact c) throws RemoteException {
|
protected void populateEmailAddresses(Contact c) throws RemoteException {
|
||||||
@Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { Email.TYPE, Email.ADDRESS, Email.LABEL },
|
@Cleanup Cursor cursor = providerClient.query(dataURI(), new String[] { Email.TYPE, Email.ADDRESS, Email.LABEL, Email.IS_SUPER_PRIMARY },
|
||||||
Email.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
|
Email.RAW_CONTACT_ID + "=? AND " + Data.MIMETYPE + "=?",
|
||||||
new String[] { String.valueOf(c.getLocalID()), Email.CONTENT_ITEM_TYPE }, null);
|
new String[] { String.valueOf(c.getLocalID()), Email.CONTENT_ITEM_TYPE }, null);
|
||||||
while (cursor != null && cursor.moveToNext()) {
|
while (cursor != null && cursor.moveToNext()) {
|
||||||
@ -290,6 +292,8 @@ public class LocalAddressBook extends LocalCollection<Contact> {
|
|||||||
if (!StringUtils.isEmpty(customType))
|
if (!StringUtils.isEmpty(customType))
|
||||||
email.addType(EmailType.get(labelToXName(customType)));
|
email.addType(EmailType.get(labelToXName(customType)));
|
||||||
}
|
}
|
||||||
|
if (cursor.getInt(3) != 0) // IS_PRIMARY
|
||||||
|
email.addType(EmailType.PREF);
|
||||||
c.getEmails().add(email);
|
c.getEmails().add(email);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -572,7 +576,13 @@ public class LocalAddressBook extends LocalCollection<Contact> {
|
|||||||
protected Builder buildPhoneNumber(Builder builder, Telephone number) {
|
protected Builder buildPhoneNumber(Builder builder, Telephone number) {
|
||||||
int typeCode = Phone.TYPE_OTHER;
|
int typeCode = Phone.TYPE_OTHER;
|
||||||
String typeLabel = null;
|
String typeLabel = null;
|
||||||
|
boolean is_primary = false;
|
||||||
|
|
||||||
Set<TelephoneType> types = number.getTypes();
|
Set<TelephoneType> types = number.getTypes();
|
||||||
|
// preferred number?
|
||||||
|
if (types.contains(TelephoneType.PREF))
|
||||||
|
is_primary = true;
|
||||||
|
|
||||||
// 1 Android type <-> 2 VCard types: fax, cell, pager
|
// 1 Android type <-> 2 VCard types: fax, cell, pager
|
||||||
if (types.contains(TelephoneType.FAX)) {
|
if (types.contains(TelephoneType.FAX)) {
|
||||||
if (types.contains(TelephoneType.HOME))
|
if (types.contains(TelephoneType.HOME))
|
||||||
@ -625,7 +635,9 @@ public class LocalAddressBook extends LocalCollection<Contact> {
|
|||||||
builder = builder
|
builder = builder
|
||||||
.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
|
.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE)
|
||||||
.withValue(Phone.NUMBER, number.getText())
|
.withValue(Phone.NUMBER, number.getText())
|
||||||
.withValue(Phone.TYPE, typeCode);
|
.withValue(Phone.TYPE, typeCode)
|
||||||
|
.withValue(Phone.IS_PRIMARY, is_primary ? 1 : 0)
|
||||||
|
.withValue(Phone.IS_SUPER_PRIMARY, is_primary ? 1 : 0);
|
||||||
if (typeLabel != null)
|
if (typeLabel != null)
|
||||||
builder = builder.withValue(Phone.LABEL, typeLabel);
|
builder = builder.withValue(Phone.LABEL, typeLabel);
|
||||||
return builder;
|
return builder;
|
||||||
@ -634,9 +646,12 @@ public class LocalAddressBook extends LocalCollection<Contact> {
|
|||||||
protected Builder buildEmail(Builder builder, ezvcard.property.Email email) {
|
protected Builder buildEmail(Builder builder, ezvcard.property.Email email) {
|
||||||
int typeCode = 0;
|
int typeCode = 0;
|
||||||
String typeLabel = null;
|
String typeLabel = null;
|
||||||
|
boolean is_primary = false;
|
||||||
|
|
||||||
for (EmailType type : email.getTypes())
|
for (EmailType type : email.getTypes())
|
||||||
if (type == EmailType.HOME)
|
if (type == EmailType.PREF)
|
||||||
|
is_primary = true;
|
||||||
|
else if (type == EmailType.HOME)
|
||||||
typeCode = Email.TYPE_HOME;
|
typeCode = Email.TYPE_HOME;
|
||||||
else if (type == EmailType.WORK)
|
else if (type == EmailType.WORK)
|
||||||
typeCode = Email.TYPE_WORK;
|
typeCode = Email.TYPE_WORK;
|
||||||
@ -654,7 +669,9 @@ public class LocalAddressBook extends LocalCollection<Contact> {
|
|||||||
builder = builder
|
builder = builder
|
||||||
.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)
|
.withValue(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE)
|
||||||
.withValue(Email.ADDRESS, email.getValue())
|
.withValue(Email.ADDRESS, email.getValue())
|
||||||
.withValue(Email.TYPE, typeCode);
|
.withValue(Email.TYPE, typeCode)
|
||||||
|
.withValue(Email.IS_PRIMARY, is_primary ? 1 : 0)
|
||||||
|
.withValue(Phone.IS_SUPER_PRIMARY, is_primary ? 1 : 0);;
|
||||||
if (typeLabel != null)
|
if (typeLabel != null)
|
||||||
builder = builder.withValue(Email.LABEL, typeLabel);
|
builder = builder.withValue(Email.LABEL, typeLabel);
|
||||||
return builder;
|
return builder;
|
||||||
|
@ -95,7 +95,7 @@ public class LocalCalendar extends LocalCollection<Event> {
|
|||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||||
protected String entryColumnUID() {
|
protected String entryColumnUID() {
|
||||||
return (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) ?
|
return (android.os.Build.VERSION.SDK_INT >= 17) ?
|
||||||
Events.UID_2445 : Events.SYNC_DATA2;
|
Events.UID_2445 : Events.SYNC_DATA2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,8 +124,6 @@ public class LocalCalendar extends LocalCollection<Event> {
|
|||||||
values.put(Calendars.CALENDAR_DISPLAY_NAME, info.getTitle());
|
values.put(Calendars.CALENDAR_DISPLAY_NAME, info.getTitle());
|
||||||
values.put(Calendars.CALENDAR_COLOR, color);
|
values.put(Calendars.CALENDAR_COLOR, color);
|
||||||
values.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CAL_ACCESS_OWNER);
|
values.put(Calendars.CALENDAR_ACCESS_LEVEL, Calendars.CAL_ACCESS_OWNER);
|
||||||
values.put(Calendars.ALLOWED_AVAILABILITY, Events.AVAILABILITY_BUSY + "," + Events.AVAILABILITY_FREE + "," + Events.AVAILABILITY_TENTATIVE);
|
|
||||||
values.put(Calendars.ALLOWED_ATTENDEE_TYPES, Attendees.TYPE_NONE + "," + Attendees.TYPE_OPTIONAL + "," + Attendees.TYPE_REQUIRED + "," + Attendees.TYPE_RESOURCE);
|
|
||||||
values.put(Calendars.ALLOWED_REMINDERS, Reminders.METHOD_ALERT);
|
values.put(Calendars.ALLOWED_REMINDERS, Reminders.METHOD_ALERT);
|
||||||
values.put(Calendars.CAN_ORGANIZER_RESPOND, 1);
|
values.put(Calendars.CAN_ORGANIZER_RESPOND, 1);
|
||||||
values.put(Calendars.CAN_MODIFY_TIME_ZONE, 1);
|
values.put(Calendars.CAN_MODIFY_TIME_ZONE, 1);
|
||||||
@ -133,6 +131,11 @@ public class LocalCalendar extends LocalCollection<Event> {
|
|||||||
values.put(Calendars.SYNC_EVENTS, 1);
|
values.put(Calendars.SYNC_EVENTS, 1);
|
||||||
values.put(Calendars.VISIBLE, 1);
|
values.put(Calendars.VISIBLE, 1);
|
||||||
|
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= 15) {
|
||||||
|
values.put(Calendars.ALLOWED_AVAILABILITY, Events.AVAILABILITY_BUSY + "," + Events.AVAILABILITY_FREE + "," + Events.AVAILABILITY_TENTATIVE);
|
||||||
|
values.put(Calendars.ALLOWED_ATTENDEE_TYPES, Attendees.TYPE_NONE + "," + Attendees.TYPE_OPTIONAL + "," + Attendees.TYPE_REQUIRED + "," + Attendees.TYPE_RESOURCE);
|
||||||
|
}
|
||||||
|
|
||||||
if (info.getTimezone() != null)
|
if (info.getTimezone() != null)
|
||||||
values.put(Calendars.CALENDAR_TIME_ZONE, info.getTimezone());
|
values.put(Calendars.CALENDAR_TIME_ZONE, info.getTimezone());
|
||||||
|
|
||||||
|
@ -67,8 +67,43 @@ public abstract class LocalCollection<T extends Resource> {
|
|||||||
|
|
||||||
// content provider (= database) querying
|
// content provider (= database) querying
|
||||||
|
|
||||||
public long[] findDirty() throws LocalStorageException {
|
public long[] findNew() throws LocalStorageException {
|
||||||
String where = entryColumnDirty() + "=1";
|
// new records are 1) dirty, and 2) don't have a remote file name yet
|
||||||
|
String where = entryColumnDirty() + "=1 AND " + entryColumnRemoteName() + " IS NULL";
|
||||||
|
if (entryColumnParentID() != null)
|
||||||
|
where += " AND " + entryColumnParentID() + "=" + String.valueOf(getId());
|
||||||
|
try {
|
||||||
|
@Cleanup Cursor cursor = providerClient.query(entriesURI(),
|
||||||
|
new String[] { entryColumnID() },
|
||||||
|
where, null, null);
|
||||||
|
if (cursor == null)
|
||||||
|
throw new LocalStorageException("Couldn't query new records");
|
||||||
|
|
||||||
|
long[] fresh = new long[cursor.getCount()];
|
||||||
|
for (int idx = 0; cursor.moveToNext(); idx++) {
|
||||||
|
long id = cursor.getLong(0);
|
||||||
|
|
||||||
|
// new record: generate UID + remote file name so that we can upload
|
||||||
|
T resource = findById(id, false);
|
||||||
|
resource.generateUID();
|
||||||
|
resource.generateName();
|
||||||
|
// write generated UID + remote file name into database
|
||||||
|
ContentValues values = new ContentValues(2);
|
||||||
|
values.put(entryColumnUID(), resource.getUid());
|
||||||
|
values.put(entryColumnRemoteName(), resource.getName());
|
||||||
|
providerClient.update(ContentUris.withAppendedId(entriesURI(), id), values, null, null);
|
||||||
|
|
||||||
|
fresh[idx] = id;
|
||||||
|
}
|
||||||
|
return fresh;
|
||||||
|
} catch(RemoteException ex) {
|
||||||
|
throw new LocalStorageException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public long[] findUpdated() throws LocalStorageException {
|
||||||
|
// updated records are 1) dirty, and 2) already have a remote file name
|
||||||
|
String where = entryColumnDirty() + "=1 AND " + entryColumnRemoteName() + " IS NOT NULL";
|
||||||
if (entryColumnParentID() != null)
|
if (entryColumnParentID() != null)
|
||||||
where += " AND " + entryColumnParentID() + "=" + String.valueOf(getId());
|
where += " AND " + entryColumnParentID() + "=" + String.valueOf(getId());
|
||||||
try {
|
try {
|
||||||
@ -107,40 +142,6 @@ public abstract class LocalCollection<T extends Resource> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public long[] findNew() throws LocalStorageException {
|
|
||||||
// new records are 1) dirty, and 2) don't have a remote file name yet
|
|
||||||
String where = entryColumnDirty() + "=1 AND " + entryColumnRemoteName() + " IS NULL";
|
|
||||||
if (entryColumnParentID() != null)
|
|
||||||
where += " AND " + entryColumnParentID() + "=" + String.valueOf(getId());
|
|
||||||
try {
|
|
||||||
@Cleanup Cursor cursor = providerClient.query(entriesURI(),
|
|
||||||
new String[] { entryColumnID() },
|
|
||||||
where, null, null);
|
|
||||||
if (cursor == null)
|
|
||||||
throw new LocalStorageException("Couldn't query new records");
|
|
||||||
|
|
||||||
long[] fresh = new long[cursor.getCount()];
|
|
||||||
for (int idx = 0; cursor.moveToNext(); idx++) {
|
|
||||||
long id = cursor.getLong(0);
|
|
||||||
|
|
||||||
// new record: generate UID + remote file name so that we can upload
|
|
||||||
T resource = findById(id, false);
|
|
||||||
resource.generateUID();
|
|
||||||
resource.generateName();
|
|
||||||
// write generated UID + remote file name into database
|
|
||||||
ContentValues values = new ContentValues(2);
|
|
||||||
values.put(entryColumnUID(), resource.getUid());
|
|
||||||
values.put(entryColumnRemoteName(), resource.getName());
|
|
||||||
providerClient.update(ContentUris.withAppendedId(entriesURI(), id), values, null, null);
|
|
||||||
|
|
||||||
fresh[idx] = id;
|
|
||||||
}
|
|
||||||
return fresh;
|
|
||||||
} catch(RemoteException ex) {
|
|
||||||
throw new LocalStorageException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public T findById(long localID, boolean populate) throws LocalStorageException {
|
public T findById(long localID, boolean populate) throws LocalStorageException {
|
||||||
try {
|
try {
|
||||||
@Cleanup Cursor cursor = providerClient.query(ContentUris.withAppendedId(entriesURI(), localID),
|
@Cleanup Cursor cursor = providerClient.query(ContentUris.withAppendedId(entriesURI(), localID),
|
||||||
|
@ -146,7 +146,7 @@ public class SyncManager {
|
|||||||
|
|
||||||
private int pushDirty() throws LocalStorageException, IOException, HttpException {
|
private int pushDirty() throws LocalStorageException, IOException, HttpException {
|
||||||
int count = 0;
|
int count = 0;
|
||||||
long[] dirtyIDs = local.findDirty();
|
long[] dirtyIDs = local.findUpdated();
|
||||||
Log.i(TAG, "Uploading " + dirtyIDs.length + " modified resource(s) (if not changed)");
|
Log.i(TAG, "Uploading " + dirtyIDs.length + " modified resource(s) (if not changed)");
|
||||||
try {
|
try {
|
||||||
for (long id : dirtyIDs) {
|
for (long id : dirtyIDs) {
|
||||||
|
@ -6,6 +6,8 @@ import at.bitfire.davdroid.URIUtils;
|
|||||||
public class URIUtilsTest extends TestCase {
|
public class URIUtilsTest extends TestCase {
|
||||||
|
|
||||||
public void testSanitize() {
|
public void testSanitize() {
|
||||||
|
assertNull(URIUtils.sanitize(null));
|
||||||
|
|
||||||
// escape "@"
|
// escape "@"
|
||||||
assertEquals("https://my%40server/my%40email.com/dir", URIUtils.sanitize("https://my@server/my@email.com/dir"));
|
assertEquals("https://my%40server/my%40email.com/dir", URIUtils.sanitize("https://my@server/my@email.com/dir"));
|
||||||
assertEquals("http://my%40server/my%40email.com/dir", URIUtils.sanitize("http://my@server/my@email.com/dir"));
|
assertEquals("http://my%40server/my%40email.com/dir", URIUtils.sanitize("http://my@server/my@email.com/dir"));
|
||||||
|
@ -206,7 +206,7 @@ public class WebDavResourceTest extends InstrumentationTestCase {
|
|||||||
dav.propfind(HttpPropfind.Mode.MEMBERS_COLLECTIONS);
|
dav.propfind(HttpPropfind.Mode.MEMBERS_COLLECTIONS);
|
||||||
List<WebDavResource> members = dav.getMembers();
|
List<WebDavResource> members = dav.getMembers();
|
||||||
assertEquals(2, members.size());
|
assertEquals(2, members.size());
|
||||||
assertEquals(ROBOHYDRA_BASE + "dav/addressbooks/user%40domain/My%20Contacts%3A1.vcf/", members.get(0).getLocation().toString());
|
assertEquals(ROBOHYDRA_BASE + "dav/addressbooks/user%40domain/My%20Contacts%3a1.vcf/", members.get(0).getLocation().toString());
|
||||||
assertEquals("HTTPS://example.com/user%40domain/absolute-url.vcf", members.get(1).getLocation().toString());
|
assertEquals("HTTPS://example.com/user%40domain/absolute-url.vcf", members.get(1).getLocation().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user