Library updates

* use ical4j/2.0.x instead of 1.0.x (thanks @benfortuna)
* use Apache Commons 3.x instead of 2.x
* code optimizations
pull/2/head
Ricki Hirner 9 years ago
parent c8cfbd6b07
commit a796a1e9b3

@ -32,6 +32,7 @@ android {
}
packagingOptions {
exclude 'LICENSE'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
}
@ -43,12 +44,15 @@ configurations.all {
dependencies {
// Apache Commons
compile 'commons-lang:commons-lang:2.6'
compile 'org.apache.commons:commons-lang3:3.4'
compile 'commons-io:commons-io:2.4'
// Lombok for useful @helpers
provided 'org.projectlombok:lombok:1.16.4'
// ical4j for parsing/generating iCalendars
compile 'org.mnode.ical4j:ical4j:1.0.6'
compile('org.mnode.ical4j:ical4j:2.0-alpha1') {
// we don't need content builders, see https://github.com/ical4j/ical4j/wiki/Groovy
exclude group: 'org.codehaus.groovy', module: 'groovy-all'
}
// ez-vcard for parsing/generating VCards
compile('com.googlecode.ez-vcard:ez-vcard:0.9.6') {
// hCard functionality not needed

@ -15,7 +15,7 @@ import java.net.URISyntaxException;
public class TestConstants {
public static final String ROBOHYDRA_BASE = "http://192.168.0.11:3000/";
public static URI roboHydra;
static {
try {

@ -73,22 +73,22 @@ public class ContactTest extends InstrumentationTestCase {
assertTrue(Arrays.equals(c.getPhoto(), expectedPhoto));
}
public void testParseInvalidUnknownProperties() throws IOException, InvalidResourceException {
public void testParseInvalidUnknownProperties() throws IOException {
Contact c = parseVCF("invalid-unknown-properties.vcf");
assertEquals("VCard with invalid unknown properties", c.getDisplayName());
assertNull(c.getUnknownProperties());
}
protected Contact parseVCF(String fname) throws IOException, InvalidResourceException {
protected Contact parseVCF(String fname) throws IOException {
@Cleanup InputStream in = assetMgr.open(fname, AssetManager.ACCESS_STREAMING);
Contact c = new Contact(fname, null);
c.parseEntity(in, new Resource.AssetDownloader() {
@Override
public byte[] download(URI uri) throws URISyntaxException, IOException, HttpException, DavException {
return IOUtils.toByteArray(uri);
}
});
@Override
public byte[] download(URI uri) throws URISyntaxException, IOException, HttpException, DavException {
return IOUtils.toByteArray(uri);
}
});
return c;
}
}

@ -53,7 +53,7 @@ public class LocalCalendarTest extends InstrumentationTestCase {
Context targetContext;
ContentProviderClient providerClient;
Account testAccount = new Account(calendarName, accountType);
final Account testAccount = new Account(calendarName, accountType);
Uri calendarURI;
LocalCalendar testCalendar;
@ -69,7 +69,7 @@ public class LocalCalendarTest extends InstrumentationTestCase {
build();
}
private long insertNewEvent() throws LocalStorageException, RemoteException {
private long insertNewEvent() throws RemoteException {
ContentValues values = new ContentValues();
values.put(Events.CALENDAR_ID, testCalendar.getId());
values.put(Events.TITLE, "Test Event");

@ -13,14 +13,12 @@ import android.test.InstrumentationTestCase;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGetHC4;
import org.apache.http.client.methods.HttpPostHC4;
import org.apache.http.client.methods.HttpRequestBaseHC4;
import org.apache.http.impl.client.CloseableHttpClient;
import java.io.IOException;
import java.net.URI;
import at.bitfire.davdroid.TestConstants;
import lombok.Cleanup;
public class DavHttpClientTest extends InstrumentationTestCase {
final static URI testCookieURI = TestConstants.roboHydra.resolve("/dav/testCookieStore");

@ -25,7 +25,7 @@ import at.bitfire.davdroid.TestConstants;
public class DavRedirectStrategyTest extends TestCase {
CloseableHttpClient httpClient;
DavRedirectStrategy strategy = DavRedirectStrategy.INSTANCE;
final DavRedirectStrategy strategy = DavRedirectStrategy.INSTANCE;
@Override
protected void setUp() {

@ -12,8 +12,8 @@ import android.util.Log;
import junit.framework.TestCase;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.http.HttpHost;
import java.io.IOException;
@ -29,7 +29,7 @@ import lombok.Cleanup;
public class TlsSniSocketFactoryTest extends TestCase {
private static final String TAG = "davdroid.TlsSniSocketFactoryTest";
TlsSniSocketFactory factory = TlsSniSocketFactory.getSocketFactory();
final TlsSniSocketFactory factory = TlsSniSocketFactory.getSocketFactory();
private InetSocketAddress sampleTlsEndpoint;
@ -75,7 +75,7 @@ public class TlsSniSocketFactoryTest extends TestCase {
}
public void testProtocolVersions() throws IOException {
String enabledProtocols[] = factory.protocols;
String enabledProtocols[] = TlsSniSocketFactory.protocols;
// SSL (all versions) should be disabled
for (String protocol : enabledProtocols)
assertFalse(protocol.contains("SSL"));

@ -28,9 +28,7 @@ import lombok.Cleanup;
// tests require running robohydra!
public class WebDavResourceTest extends InstrumentationTestCase {
static byte[] SAMPLE_CONTENT = new byte[] { 1, 2, 3, 4, 5 };
final static String PATH_SIMPLE_FILE = "collection/new.file";
final static byte[] SAMPLE_CONTENT = new byte[] { 1, 2, 3, 4, 5 };
AssetManager assetMgr;
CloseableHttpClient httpClient;

@ -7,6 +7,7 @@
*/
package at.bitfire.davdroid;
import net.fortuna.ical4j.model.Calendar;
import net.fortuna.ical4j.model.property.ProdId;
public class Constants {
@ -16,5 +17,5 @@ public class Constants {
WEB_URL_HELP = "https://davdroid.bitfire.at/configuration?pk_campaign=davdroid-app",
WEB_URL_VIEW_LOGS = "https://github.com/bitfireAT/davdroid/wiki/How-to-view-the-logs";
public static final ProdId ICAL_PRODID = new ProdId("-//bitfire web engineering//DAVdroid " + Constants.APP_VERSION + " (ical4j 1.0.x)//EN");
public static final ProdId ICAL_PRODID = new ProdId("-//bitfire web engineering//DAVdroid " + Constants.APP_VERSION + " (ical4j 2.0-alpha1)//EN");
}

@ -15,7 +15,7 @@ import net.fortuna.ical4j.model.DefaultTimeZoneRegistryFactory;
import net.fortuna.ical4j.model.TimeZone;
import net.fortuna.ical4j.model.TimeZoneRegistry;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.SimpleTimeZone;

@ -9,11 +9,6 @@ package at.bitfire.davdroid;
import android.util.Log;
import org.apache.commons.codec.EncoderException;
import org.apache.commons.codec.net.URLCodec;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import java.net.URI;
import java.net.URISyntaxException;

@ -7,7 +7,6 @@
*/
package at.bitfire.davdroid.resource;
import android.accounts.Account;
import android.util.Log;
import org.apache.http.impl.client.CloseableHttpClient;
@ -17,7 +16,6 @@ import org.simpleframework.xml.core.Persister;
import java.io.StringWriter;
import java.net.URISyntaxException;
import at.bitfire.davdroid.syncadapter.AccountSettings;
import at.bitfire.davdroid.webdav.DavCalendarQuery;
import at.bitfire.davdroid.webdav.DavCompFilter;
import at.bitfire.davdroid.webdav.DavFilter;

@ -7,8 +7,6 @@
*/
package at.bitfire.davdroid.resource;
import android.accounts.Account;
import org.apache.http.impl.client.CloseableHttpClient;
import java.net.URISyntaxException;

@ -9,20 +9,14 @@ package at.bitfire.davdroid.resource;
import android.util.Log;
import net.fortuna.ical4j.model.property.ProdId;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import at.bitfire.davdroid.Constants;
@ -30,7 +24,6 @@ import ezvcard.Ezvcard;
import ezvcard.VCard;
import ezvcard.VCardVersion;
import ezvcard.ValidationWarnings;
import ezvcard.Warning;
import ezvcard.parameter.EmailType;
import ezvcard.parameter.ImageType;
import ezvcard.parameter.RelatedType;
@ -59,8 +52,6 @@ import ezvcard.property.Telephone;
import ezvcard.property.Title;
import ezvcard.property.Uid;
import ezvcard.property.Url;
import ezvcard.property.VCardProperty;
import ezvcard.util.ListMultimap;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
@ -265,7 +256,7 @@ public class Contact extends Resource {
}
// NOTE
List<String> notes = new LinkedList<String>();
List<String> notes = new LinkedList<>();
for (Note note : vcard.getNotes())
notes.add(note.getValue());
if (!notes.isEmpty())
@ -420,7 +411,7 @@ public class Contact extends Resource {
// CATEGORY
if (!categories.isEmpty())
vcard.setCategories(categories.toArray(new String[0]));
vcard.setCategories(categories.toArray(new String[categories.size()]));
// URL
for (String url : URLs)

@ -9,7 +9,6 @@ package at.bitfire.davdroid.resource;
import android.content.Context;
import android.util.Log;
import android.widget.Toast;
import org.apache.http.HttpException;
import org.apache.http.impl.client.CloseableHttpClient;
@ -22,7 +21,6 @@ import org.xbill.DNS.Type;
import java.io.Closeable;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.LinkedList;
@ -40,8 +38,8 @@ import ezvcard.VCardVersion;
public class DavResourceFinder implements Closeable {
private final static String TAG = "davdroid.ResourceFinder";
protected Context context;
protected CloseableHttpClient httpClient;
final protected Context context;
final protected CloseableHttpClient httpClient;
public DavResourceFinder(Context context) {
@ -187,10 +185,9 @@ public class DavResourceFinder implements Closeable {
* @param serviceName Service name ("carddav" or "caldav")
* @return Initial service URL (HTTP/HTTPS), without user credentials
* @throws URISyntaxException when the user-given URI is invalid
* @throws MalformedURLException when the user-given URI is invalid
*/
public URI getInitialContextURL(ServerInfo serverInfo, String serviceName) throws URISyntaxException, MalformedURLException {
String scheme = null,
public URI getInitialContextURL(ServerInfo serverInfo, String serviceName) throws URISyntaxException {
String scheme,
domain;
int port = -1;
String path = "/";

@ -38,7 +38,6 @@ import net.fortuna.ical4j.model.property.ExRule;
import net.fortuna.ical4j.model.property.LastModified;
import net.fortuna.ical4j.model.property.Location;
import net.fortuna.ical4j.model.property.Organizer;
import net.fortuna.ical4j.model.property.ProdId;
import net.fortuna.ical4j.model.property.RDate;
import net.fortuna.ical4j.model.property.RRule;
import net.fortuna.ical4j.model.property.RecurrenceId;
@ -51,19 +50,15 @@ import net.fortuna.ical4j.util.CompatibilityHints;
import net.fortuna.ical4j.util.SimpleHostInfo;
import net.fortuna.ical4j.util.UidGenerator;
import org.apache.commons.lang.StringUtils;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.Calendar;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.SimpleTimeZone;
import java.util.TimeZone;
import at.bitfire.davdroid.Constants;
@ -98,9 +93,9 @@ public class Event extends Resource {
@Getter @Setter protected boolean opaque;
@Getter @Setter protected Organizer organizer;
@Getter protected List<Attendee> attendees = new LinkedList<Attendee>();
@Getter protected List<Attendee> attendees = new LinkedList<>();
@Getter protected List<VAlarm> alarms = new LinkedList<VAlarm>();
@Getter protected List<VAlarm> alarms = new LinkedList<>();
static {
CompatibilityHints.setHintEnabled(CompatibilityHints.KEY_RELAXED_UNFOLDING, true);
@ -201,10 +196,10 @@ public class Event extends Resource {
}
rrule = (RRule)event.getProperty(Property.RRULE);
for (RDate rdate : (Iterable<RDate>)event.getProperties(Property.RDATE))
for (RDate rdate : (List<RDate>)(List<?>)event.getProperties(Property.RDATE))
rdates.add(rdate);
exrule = (ExRule)event.getProperty(Property.EXRULE);
for (ExDate exdate : (Iterable<ExDate>)event.getProperties(Property.EXDATE))
for (ExDate exdate : (List<ExDate>)(List<?>)event.getProperties(Property.EXDATE))
exdates.add(exdate);
if (event.getSummary() != null)
@ -218,7 +213,7 @@ public class Event extends Resource {
opaque = event.getTransparency() != Transp.TRANSPARENT;
organizer = event.getOrganizer();
for (Attendee attendee : (Iterable<Attendee>)event.getProperties(Property.ATTENDEE))
for (Attendee attendee : (List<Attendee>)(List<?>)event.getProperties(Property.ATTENDEE))
attendees.add(attendee);
Clazz classification = event.getClassification();

@ -41,21 +41,17 @@ import android.provider.ContactsContract.RawContacts;
import android.util.Log;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.io.InputStream;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import at.bitfire.davdroid.syncadapter.AccountSettings;
@ -64,7 +60,6 @@ import ezvcard.parameter.EmailType;
import ezvcard.parameter.ImppType;
import ezvcard.parameter.RelatedType;
import ezvcard.parameter.TelephoneType;
import ezvcard.parameter.VCardParameter;
import ezvcard.property.Address;
import ezvcard.property.Anniversary;
import ezvcard.property.Birthday;
@ -81,7 +76,7 @@ public class LocalAddressBook extends LocalCollection<Contact> {
protected final static String COLUMN_UNKNOWN_PROPERTIES = RawContacts.SYNC3;
protected AccountSettings accountSettings;
final protected AccountSettings accountSettings;
/* database fields */
@ -138,7 +133,7 @@ public class LocalAddressBook extends LocalCollection<Contact> {
return c;
}
public void deleteAllExceptRemoteNames(Resource[] remoteResources) {
public int deleteAllExceptRemoteNames(Resource[] remoteResources) throws LocalStorageException {
String where;
if (remoteResources.length != 0) {
@ -148,16 +143,17 @@ public class LocalAddressBook extends LocalCollection<Contact> {
where = entryColumnRemoteName() + " NOT IN (" + StringUtils.join(sqlFileNames, ",") + ")";
} else
where = entryColumnRemoteName() + " IS NOT NULL";
Builder builder = ContentProviderOperation.newDelete(entriesURI()).withSelection(where, null);
pendingOperations.add(builder
.withYieldAllowed(true)
.build());
}
try {
return providerClient.delete(entriesURI(), where, null);
} catch (RemoteException e) {
throw new LocalStorageException("Couldn't delete contacts locally", e);
}
}
@Override
public void commit() throws LocalStorageException {
super.commit();
public int commit() throws LocalStorageException {
int affected = super.commit();
// update group details for groups we have just created
Uri groupsUri = syncAdapterURI(Groups.CONTENT_URI);
@ -175,11 +171,13 @@ public class LocalAddressBook extends LocalCollection<Contact> {
.withValue(Groups.TITLE, sourceID)
.withValue(Groups.GROUP_VISIBLE, 1)
.build());
super.commit();
affected += super.commit();
}
} catch (RemoteException e) {
throw new LocalStorageException("Couldn't update group names", e);
}
return affected;
}
@ -542,11 +540,11 @@ public class LocalAddressBook extends LocalCollection<Contact> {
Log.d(TAG, "Group not found (maybe deleted)");
}
protected void populateURL(Contact c, ContentValues row) throws RemoteException {
protected void populateURL(Contact c, ContentValues row) {
c.getURLs().add(row.getAsString(Website.URL));
}
protected void populateEvent(Contact c, ContentValues row) throws RemoteException {
protected void populateEvent(Contact c, ContentValues row) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd", Locale.US);
try {
Date date = formatter.parse(row.getAsString(CommonDataKinds.Event.START_DATE));
@ -563,7 +561,7 @@ public class LocalAddressBook extends LocalCollection<Contact> {
}
}
protected void populateRelation(Contact c, ContentValues row) throws RemoteException {
protected void populateRelation(Contact c, ContentValues row) {
String name = row.getAsString(Relation.NAME);
// don't process empty relations
@ -634,7 +632,7 @@ public class LocalAddressBook extends LocalCollection<Contact> {
}
}
protected void populateSipAddress(Contact c, ContentValues row) throws RemoteException {
protected void populateSipAddress(Contact c, ContentValues row) {
try {
Impp impp = new Impp("sip:" + row.getAsString(SipAddress.SIP_ADDRESS));
switch (row.getAsInteger(SipAddress.TYPE)) {
@ -971,11 +969,11 @@ public class LocalAddressBook extends LocalCollection<Contact> {
lineLocality = StringUtils.join(new String[] { address.getPostalCode(), address.getLocality() }, " ");
List<String> lines = new LinkedList<>();
if (lineStreet != null)
if (StringUtils.isNotBlank(lineStreet))
lines.add(lineStreet);
if (address.getRegion() != null && !address.getRegion().isEmpty())
lines.add(address.getRegion());
if (lineLocality != null)
if (StringUtils.isNotBlank(lineLocality))
lines.add(lineLocality);
formattedAddress = StringUtils.join(lines, "\n");
@ -1088,7 +1086,7 @@ public class LocalAddressBook extends LocalCollection<Contact> {
String lowerCase = StringUtils.lowerCase(xname, Locale.US),
withoutPrefix = StringUtils.removeStart(lowerCase, "x-"),
withSpaces = StringUtils.replace(withoutPrefix, "_", " ");
return WordUtils.capitalize(withSpaces);
return StringUtils.capitalize(withSpaces);
}
}

@ -28,21 +28,14 @@ import android.provider.CalendarContract.Attendees;
import android.provider.CalendarContract.Calendars;
import android.provider.CalendarContract.Events;
import android.provider.CalendarContract.Reminders;
import android.provider.ContactsContract;
import android.util.Log;
import net.fortuna.ical4j.model.Date;
import net.fortuna.ical4j.model.DateList;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.Dur;
import net.fortuna.ical4j.model.Parameter;
import net.fortuna.ical4j.model.ParameterList;
import net.fortuna.ical4j.model.Period;
import net.fortuna.ical4j.model.PeriodList;
import net.fortuna.ical4j.model.PropertyList;
import net.fortuna.ical4j.model.TimeZone;
import net.fortuna.ical4j.model.TimeZoneRegistry;
import net.fortuna.ical4j.model.TimeZoneRegistryFactory;
import net.fortuna.ical4j.model.component.VAlarm;
import net.fortuna.ical4j.model.parameter.Cn;
import net.fortuna.ical4j.model.parameter.CuType;
@ -51,7 +44,6 @@ import net.fortuna.ical4j.model.parameter.Role;
import net.fortuna.ical4j.model.property.Action;
import net.fortuna.ical4j.model.property.Attendee;
import net.fortuna.ical4j.model.property.DateListProperty;
import net.fortuna.ical4j.model.property.DateProperty;
import net.fortuna.ical4j.model.property.Description;
import net.fortuna.ical4j.model.property.Duration;
import net.fortuna.ical4j.model.property.ExDate;
@ -61,19 +53,14 @@ import net.fortuna.ical4j.model.property.RDate;
import net.fortuna.ical4j.model.property.RRule;
import net.fortuna.ical4j.model.property.RecurrenceId;
import net.fortuna.ical4j.model.property.Status;
import net.fortuna.ical4j.util.TimeZones;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.text.ParseException;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import at.bitfire.davdroid.DAVUtils;
import at.bitfire.davdroid.DateUtils;
@ -91,7 +78,7 @@ public class LocalCalendar extends LocalCollection<Event> {
@Getter protected String url;
@Getter protected long id;
protected static String COLLECTION_COLUMN_CTAG = Calendars.CAL_SYNC1;
protected static final String COLLECTION_COLUMN_CTAG = Calendars.CAL_SYNC1;
/* database fields */
@ -118,7 +105,7 @@ public class LocalCalendar extends LocalCollection<Event> {
@SuppressLint("InlinedApi")
public static Uri create(Account account, ContentResolver resolver, ServerInfo.ResourceInfo info) throws LocalStorageException {
final ContentProviderClient client = resolver.acquireContentProviderClient(CalendarContract.AUTHORITY);
@Cleanup("release") final ContentProviderClient client = resolver.acquireContentProviderClient(CalendarContract.AUTHORITY);
if (client == null)
throw new LocalStorageException("No Calendar Provider found (Calendar app disabled?)");
@ -168,7 +155,7 @@ public class LocalCalendar extends LocalCollection<Event> {
return calendars.toArray(new LocalCalendar[calendars.size()]);
}
public LocalCalendar(Account account, ContentProviderClient providerClient, long id, String url) throws RemoteException {
public LocalCalendar(Account account, ContentProviderClient providerClient, long id, String url) {
super(account, providerClient);
this.id = id;
this.url = url;
@ -229,7 +216,7 @@ public class LocalCalendar extends LocalCollection<Event> {
return new Event(localID, resourceName, eTag);
}
public void deleteAllExceptRemoteNames(Resource[] remoteResources) {
public int deleteAllExceptRemoteNames(Resource[] remoteResources) throws LocalStorageException {
List<String> sqlFileNames = new LinkedList<>();
for (Resource res : remoteResources)
sqlFileNames.add(DatabaseUtils.sqlEscapeString(res.getName()));
@ -256,6 +243,7 @@ public class LocalCalendar extends LocalCollection<Event> {
.withYieldAllowed(true)
.build()
);
return commit();
}
@Override
@ -454,7 +442,7 @@ public class LocalCalendar extends LocalCollection<Event> {
}
}
void populateAttendee(Event event, ContentValues values) throws RemoteException {
void populateAttendee(Event event, ContentValues values) {
try {
Attendee attendee = new Attendee(new URI("mailto", values.getAsString(Attendees.ATTENDEE_EMAIL), null));
ParameterList params = attendee.getParameters();
@ -504,7 +492,7 @@ public class LocalCalendar extends LocalCollection<Event> {
}
}
void populateReminder(Event event, ContentValues row) throws RemoteException {
void populateReminder(Event event, ContentValues row) {
VAlarm alarm = new VAlarm(new Dur(0, 0, -row.getAsInteger(Reminders.MINUTES), 0));
PropertyList props = alarm.getProperties();

@ -11,6 +11,7 @@ import android.accounts.Account;
import android.content.ContentProviderClient;
import android.content.ContentProviderOperation;
import android.content.ContentProviderOperation.Builder;
import android.content.ContentProviderResult;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.OperationApplicationException;
@ -21,14 +22,13 @@ import android.os.RemoteException;
import android.provider.CalendarContract;
import android.util.Log;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import lombok.Cleanup;
import lombok.Getter;
/**
* Represents a locally-stored synchronizable collection (for instance, the
@ -40,9 +40,9 @@ import lombok.Getter;
public abstract class LocalCollection<T extends Resource> {
private static final String TAG = "davdroid.Collection";
protected Account account;
protected ContentProviderClient providerClient;
protected ArrayList<ContentProviderOperation> pendingOperations = new ArrayList<ContentProviderOperation>();
final protected Account account;
final protected ContentProviderClient providerClient;
final protected ArrayList<ContentProviderOperation> pendingOperations = new ArrayList<>();
// database fields
@ -269,8 +269,8 @@ public abstract class LocalCollection<T extends Resource> {
* @return the new resource object */
abstract public T newResource(long localID, String resourceName, String eTag);
/** Enqueues adding the resource (including all data) to the local collection. Requires commit(). */
public void add(Resource resource) {
/** Enqueues adding the resource (including all data) to the local collection. */
public void add(Resource resource) throws LocalStorageException {
int idx = pendingOperations.size();
pendingOperations.add(
buildEntry(ContentProviderOperation.newInsert(entriesURI()), resource, false)
@ -278,6 +278,7 @@ public abstract class LocalCollection<T extends Resource> {
.build());
addDataRows(resource, -1, idx);
commit();
}
/** Enqueues updating an existing resource in the local collection. The resource will be found by
@ -292,6 +293,7 @@ public abstract class LocalCollection<T extends Resource> {
removeDataRows(localResource);
addDataRows(remoteResource, localResource.getLocalID(), -1);
commit();
}
/** Enqueues deleting a resource from the local collection. Requires commit(). */
@ -303,10 +305,11 @@ public abstract class LocalCollection<T extends Resource> {
}
/**
* Enqueues deleting all resources except the give ones from the local collection. Requires commit().
* Deletes all resources except the give ones from the local collection.
* @param remoteResources resources with these remote file names will be kept
* @return number of deleted resources
*/
public void deleteAllExceptRemoteNames(Resource[] remoteResources) {
public int deleteAllExceptRemoteNames(Resource[] remoteResources) throws LocalStorageException {
final String where;
if (remoteResources.length != 0) {
@ -319,13 +322,17 @@ public abstract class LocalCollection<T extends Resource> {
// delete all entries
where = entryColumnRemoteName() + " IS NOT NULL";
ContentProviderOperation.Builder builder = ContentProviderOperation.newDelete(entriesURI())
.withSelection( // restrict deletion to parent collection
entryColumnParentID() + "=? AND (" + where + ')',
new String[] { String.valueOf(getId()) }
);
pendingOperations.add(builder.withYieldAllowed(true).build());
}
try {
return providerClient.delete(
entriesURI(),
// restrict deletion to parent collection
entryColumnParentID() + "=? AND (" + where + ')',
new String[] { String.valueOf(getId()) }
);
} catch (RemoteException e) {
throw new LocalStorageException("Couldn't delete local resources", e);
}
}
/** Updates the locally-known ETag of a resource. */
@ -350,17 +357,21 @@ public abstract class LocalCollection<T extends Resource> {
}
/** Commits enqueued operations to the content provider (for batch operations). */
public void commit() throws LocalStorageException {
public int commit() throws LocalStorageException {
int affected = 0;
if (!pendingOperations.isEmpty())
try {
Log.d(TAG, "Committing " + pendingOperations.size() + " operations");
providerClient.applyBatch(pendingOperations);
Log.d(TAG, "Committing " + pendingOperations.size() + " operations ...");
ContentProviderResult[] results = providerClient.applyBatch(pendingOperations);
for (ContentProviderResult result : results)
if (result != null && result.count != null)
affected += result.count;
Log.d(TAG, "... " + affected + " row(s) affected");
pendingOperations.clear();
} catch (RemoteException ex) {
throw new LocalStorageException(ex);
} catch(OperationApplicationException ex) {
} catch(OperationApplicationException | RemoteException ex) {
throw new LocalStorageException(ex);
}
return affected;
}

@ -24,17 +24,15 @@ import net.fortuna.ical4j.model.Date;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.Dur;
import net.fortuna.ical4j.model.TimeZone;
import net.fortuna.ical4j.model.TimeZoneRegistry;
import net.fortuna.ical4j.model.property.Clazz;
import net.fortuna.ical4j.model.property.Completed;
import net.fortuna.ical4j.model.property.Created;
import net.fortuna.ical4j.model.property.DtStart;
import net.fortuna.ical4j.model.property.Due;
import net.fortuna.ical4j.model.property.Duration;
import net.fortuna.ical4j.model.property.Status;
import net.fortuna.ical4j.util.TimeZones;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.dmfs.provider.tasks.TaskContract;
import java.util.LinkedList;
@ -52,7 +50,7 @@ public class LocalTaskList extends LocalCollection<Task> {
public static final String TASKS_AUTHORITY = "org.dmfs.tasks";
protected static String COLLECTION_COLUMN_CTAG = TaskContract.TaskLists.SYNC1;
protected static final String COLLECTION_COLUMN_CTAG = TaskContract.TaskLists.SYNC1;
@Override protected Uri entriesURI() { return syncAdapterURI(TaskContract.Tasks.getContentUri(TASKS_AUTHORITY)); }
@Override protected String entryColumnAccountType() { return TaskContract.Tasks.ACCOUNT_TYPE; }
@ -67,7 +65,7 @@ public class LocalTaskList extends LocalCollection<Task> {
public static Uri create(Account account, ContentResolver resolver, ServerInfo.ResourceInfo info) throws LocalStorageException {
final ContentProviderClient client = resolver.acquireContentProviderClient(TASKS_AUTHORITY);
@Cleanup("release") final ContentProviderClient client = resolver.acquireContentProviderClient(TASKS_AUTHORITY);
if (client == null)
throw new LocalStorageException("No tasks provider found");
@ -101,7 +99,7 @@ public class LocalTaskList extends LocalCollection<Task> {
return taskList.toArray(new LocalTaskList[taskList.size()]);
}
public LocalTaskList(Account account, ContentProviderClient providerClient, long id, String url) throws RemoteException {
public LocalTaskList(Account account, ContentProviderClient providerClient, long id, String url) {
super(account, providerClient);
this.id = id;
this.url = url;

@ -16,11 +16,7 @@ public class RecordNotFoundException extends LocalStorageException {
private static final String detailMessage = "Record not found in local content provider";
RecordNotFoundException(Throwable ex) {
super(detailMessage, ex);
}
RecordNotFoundException() {
super(detailMessage);
}

@ -7,11 +7,8 @@
*/
package at.bitfire.davdroid.resource;
import android.accounts.Account;
import android.util.Log;
import net.fortuna.ical4j.model.ValidationException;
import org.apache.commons.io.IOUtils;
import org.apache.http.impl.client.CloseableHttpClient;
@ -25,10 +22,7 @@ import java.util.LinkedList;
import java.util.List;
import at.bitfire.davdroid.URIUtils;
import at.bitfire.davdroid.syncadapter.AccountSettings;
import at.bitfire.davdroid.webdav.DavCalendarQuery;
import at.bitfire.davdroid.webdav.DavException;
import at.bitfire.davdroid.webdav.DavFilter;
import at.bitfire.davdroid.webdav.DavMultiget;
import at.bitfire.davdroid.webdav.DavNoContentException;
import at.bitfire.davdroid.webdav.HttpException;
@ -38,7 +32,6 @@ import at.bitfire.davdroid.webdav.WebDavResource.PutMode;
import ezvcard.io.text.VCardParseException;
import lombok.Cleanup;
import lombok.Getter;
import lombok.Setter;
/**
* Represents a remotely stored synchronizable collection (collection as in
@ -49,7 +42,6 @@ import lombok.Setter;
public abstract class RemoteCollection<T extends Resource> {
private static final String TAG = "davdroid.resource";
CloseableHttpClient httpClient;
URI baseURI;
@Getter WebDavResource collection;
@ -59,8 +51,6 @@ public abstract class RemoteCollection<T extends Resource> {
abstract protected T newResourceSkeleton(String name, String ETag);
public RemoteCollection(CloseableHttpClient httpClient, String baseURL, String user, String password, boolean preemptiveAuth) throws URISyntaxException {
this.httpClient = httpClient;
baseURI = URIUtils.parseURI(baseURL, false);
collection = new WebDavResource(httpClient, baseURI, user, password, preemptiveAuth);
}
@ -101,12 +91,12 @@ public abstract class RemoteCollection<T extends Resource> {
else
collection.propfind(HttpPropfind.Mode.MEMBERS_ETAG);
List<T> resources = new LinkedList<T>();
List<T> resources = new LinkedList<>();
if (collection.getMembers() != null)
for (WebDavResource member : collection.getMembers())
resources.add(newResourceSkeleton(member.getName(), member.getETag()));
return resources.toArray(new Resource[0]);
return resources.toArray(new Resource[resources.size()]);
}
@ -115,16 +105,16 @@ public abstract class RemoteCollection<T extends Resource> {
public Resource[] multiGet(Resource[] resources) throws URISyntaxException, IOException, DavException, HttpException {
try {
if (resources.length == 1)
return (T[]) new Resource[] { get(resources[0]) };
return new Resource[] { get(resources[0]) };
Log.i(TAG, "Multi-getting " + resources.length + " remote resource(s)");
LinkedList<String> names = new LinkedList<String>();
LinkedList<String> names = new LinkedList<>();
for (Resource resource : resources)
names.add(resource.getName());
LinkedList<T> foundResources = new LinkedList<T>();
collection.multiGet(multiGetType(), names.toArray(new String[0]));
LinkedList<T> foundResources = new LinkedList<>();
collection.multiGet(multiGetType(), names.toArray(new String[names.size()]));
if (collection.getMembers() == null)
throw new DavNoContentException();
@ -142,7 +132,7 @@ public abstract class RemoteCollection<T extends Resource> {
}
}
return foundResources.toArray(new Resource[0]);
return foundResources.toArray(new Resource[foundResources.size()]);
} catch (InvalidResourceException e) {
Log.e(TAG, "Couldn't parse entity from GET", e);
}
@ -172,7 +162,7 @@ public abstract class RemoteCollection<T extends Resource> {
}
// returns ETag of the created resource, if returned by server
public String add(Resource res) throws URISyntaxException, IOException, HttpException, ValidationException {
public String add(Resource res) throws URISyntaxException, IOException, HttpException {
WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
member.setContentType(res.getMimeType());
@ -193,7 +183,7 @@ public abstract class RemoteCollection<T extends Resource> {
}
// returns ETag of the updated resource, if returned by server
public String update(Resource res) throws URISyntaxException, IOException, HttpException, ValidationException {
public String update(Resource res) throws URISyntaxException, IOException, HttpException {
WebDavResource member = new WebDavResource(collection, res.getName(), res.getETag());
member.setContentType(res.getMimeType());
@ -211,22 +201,22 @@ public abstract class RemoteCollection<T extends Resource> {
Resource.AssetDownloader getDownloader() {
return new Resource.AssetDownloader() {
@Override
public byte[] download(URI uri) throws URISyntaxException, IOException, HttpException, DavException {
if (!uri.isAbsolute())
throw new URISyntaxException(uri.toString(), "URI referenced from entity must be absolute");
if (uri.getScheme().equalsIgnoreCase(baseURI.getScheme()) &&
uri.getAuthority().equalsIgnoreCase(baseURI.getAuthority())) {
// resource is on same server, send Authorization
WebDavResource file = new WebDavResource(collection, uri);
file.get("image/*");
return file.getContent();
} else {
// resource is on an external server, don't send Authorization
return IOUtils.toByteArray(uri);
}
}
};
@Override
public byte[] download(URI uri) throws URISyntaxException, IOException, HttpException, DavException {
if (!uri.isAbsolute())
throw new URISyntaxException(uri.toString(), "URI referenced from entity must be absolute");
if (uri.getScheme().equalsIgnoreCase(baseURI.getScheme()) &&
uri.getAuthority().equalsIgnoreCase(baseURI.getAuthority())) {
// resource is on same server, send Authorization
WebDavResource file = new WebDavResource(collection, uri);
file.get("image/*");
return file.getContent();
} else {
// resource is on an external server, don't send Authorization
return IOUtils.toByteArray(uri);
}
}
};
}
}

@ -12,7 +12,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import at.bitfire.davdroid.webdav.DavException;
import at.bitfire.davdroid.webdav.HttpException;
@ -58,6 +57,6 @@ public abstract class Resource {
public interface AssetDownloader {
public byte[] download(URI url) throws URISyntaxException, IOException, HttpException, DavException;
byte[] download(URI url) throws URISyntaxException, IOException, HttpException, DavException;
}
}

@ -7,7 +7,6 @@
*/
package at.bitfire.davdroid.resource;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URI;
import java.util.LinkedList;
@ -20,10 +19,6 @@ import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor(suppressConstructorProperties=true)
@Data
public class ServerInfo {
enum Scheme {
HTTP, HTTPS, MAILTO
}
final private URI baseURI;
final private String userName, password;
final boolean authPreemptive;

@ -38,7 +38,7 @@ public class AccountAuthenticatorService extends Service {
private static class AccountAuthenticator extends AbstractAccountAuthenticator {
Context context;
final Context context;
public AccountAuthenticator(Context context) {
super(context);

@ -19,7 +19,6 @@ import android.net.Uri;
import android.os.Bundle;
import android.provider.CalendarContract;
import android.provider.CalendarContract.Calendars;
import android.provider.ContactsContract;
import android.util.Log;
import java.net.URI;
@ -46,9 +45,9 @@ public class AccountSettings {
public final static long SYNC_INTERVAL_MANUALLY = -1;
Context context;
AccountManager accountManager;
Account account;
final Context context;
final AccountManager accountManager;
final Account account;
public AccountSettings(Context context, Account account) {
@ -78,7 +77,7 @@ public class AccountSettings {
if (addressBook.isEnabled()) {
bundle.putString(KEY_ADDRESSBOOK_URL, addressBook.getURL());
bundle.putString(KEY_ADDRESSBOOK_VCARD_VERSION, addressBook.getVCardVersion().getVersion());
continue;
break;
}
return bundle;
}

@ -61,7 +61,7 @@ public class CalendarsSyncAdapterService extends Service {
boolean preemptive = settings.getPreemptiveAuth();
try {
Map<LocalCollection<?>, RemoteCollection<?>> map = new HashMap<LocalCollection<?>, RemoteCollection<?>>();
Map<LocalCollection<?>, RemoteCollection<?>> map = new HashMap<>();
for (LocalCalendar calendar : LocalCalendar.findAll(account, provider)) {
RemoteCollection<?> dav = new CalDavCalendar(httpClient, calendar.getUrl(), userName, password, preemptive);

@ -67,7 +67,7 @@ public class ContactsSyncAdapterService extends Service {
LocalCollection<?> database = new LocalAddressBook(account, provider, settings);
RemoteCollection<?> dav = new CardDavAddressBook(settings, httpClient, addressBookURL, userName, password, preemptive);
Map<LocalCollection<?>, RemoteCollection<?>> map = new HashMap<LocalCollection<?>, RemoteCollection<?>>();
Map<LocalCollection<?>, RemoteCollection<?>> map = new HashMap<>();
map.put(database, dav);
return map;

@ -8,8 +8,6 @@
package at.bitfire.davdroid.syncadapter;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationManager;
@ -19,17 +17,15 @@ import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SyncResult;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.provider.Settings;
import android.util.Log;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.http.HttpStatus;
import org.apache.http.impl.client.CloseableHttpClient;
@ -57,7 +53,7 @@ public abstract class DavSyncAdapter extends AbstractThreadedSyncAdapter impleme
@Getter private static String androidID;
protected Context context;
final protected Context context;
/* We use one static httpClient for
* - all sync adapters (CalendarsSyncAdapter, ContactsSyncAdapter)
@ -120,7 +116,6 @@ public abstract class DavSyncAdapter extends AbstractThreadedSyncAdapter impleme
httpClientLock.writeLock().lock();
if (httpClient == null) {
Log.d(TAG, "Creating new DavHttpClient");
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(getContext());
httpClient = DavHttpClient.create();
}
@ -129,10 +124,6 @@ public abstract class DavSyncAdapter extends AbstractThreadedSyncAdapter impleme
httpClientLock.readLock().lock();
httpClientLock.writeLock().unlock();
// TODO use VCard 4.0 if possible
AccountSettings accountSettings = new AccountSettings(getContext(), account);
Log.d(TAG, "Server supports VCard version " + accountSettings.getAddressBookVCardVersion());
Exception exceptionToShow = null; // exception to show notification for
Intent exceptionIntent = null; // what shall happen when clicking on the exception notification
try {
@ -199,7 +190,7 @@ public abstract class DavSyncAdapter extends AbstractThreadedSyncAdapter impleme
.setContentTitle(context.getString(R.string.sync_error_title))
.setContentText(exceptionToShow.getLocalizedMessage())
.setContentInfo(account.name)
.setStyle(new Notification.BigTextStyle().bigText(account.name + ":\n" + ExceptionUtils.getFullStackTrace(exceptionToShow)))
.setStyle(new Notification.BigTextStyle().bigText(account.name + ":\n" + ExceptionUtils.getStackTrace(exceptionToShow)))
.setContentIntent(contentIntent);
NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);

@ -10,8 +10,6 @@ package at.bitfire.davdroid.syncadapter;
import android.content.SyncResult;
import android.util.Log;
import net.fortuna.ical4j.model.ValidationException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.HashSet;
@ -33,8 +31,8 @@ public class SyncManager {
private static final int MAX_MULTIGET_RESOURCES = 35;
protected LocalCollection<? extends Resource> local;
protected RemoteCollection<? extends Resource> remote;
final protected LocalCollection<? extends Resource> local;
final protected RemoteCollection<? extends Resource> remote;
public SyncManager(LocalCollection<? extends Resource> local, RemoteCollection<? extends Resource> remote) {
@ -49,10 +47,8 @@ public class SyncManager {
addedRemotely = pushNew(),
updatedRemotely = pushDirty();
syncResult.stats.numEntries = deletedRemotely + addedRemotely + updatedRemotely;
// PHASE 2A: check if there's a reason to do a sync with remote (= forced sync or remote CTag changed)
boolean fetchCollection = syncResult.stats.numEntries > 0;
boolean fetchCollection = (deletedRemotely + addedRemotely + updatedRemotely) > 0;
if (manualSync) {
Log.i(TAG, "Synchronization forced");
fetchCollection = true;
@ -72,8 +68,8 @@ public class SyncManager {
// PHASE 2B: detect details of remote changes
Log.i(TAG, "Fetching remote resource list");
Set<Resource> remotelyAdded = new HashSet<Resource>(),
remotelyUpdated = new HashSet<Resource>();
Set<Resource> remotelyAdded = new HashSet<>(),
remotelyUpdated = new HashSet<>();
Resource[] remoteResources = remote.getMemberETags();
for (Resource remoteResource : remoteResources) {
@ -87,13 +83,13 @@ public class SyncManager {
}
// PHASE 3: pull remote changes from server
syncResult.stats.numInserts = pullNew(remotelyAdded.toArray(new Resource[0]));
syncResult.stats.numUpdates = pullChanged(remotelyUpdated.toArray(new Resource[0]));
syncResult.stats.numEntries += syncResult.stats.numInserts + syncResult.stats.numUpdates;
syncResult.stats.numInserts = pullNew(remotelyAdded.toArray(new Resource[remotelyAdded.size()]));
syncResult.stats.numUpdates = pullChanged(remotelyUpdated.toArray(new Resource[remotelyUpdated.size()]));
Log.i(TAG, "Removing non-dirty resources that are not present remotely anymore");
local.deleteAllExceptRemoteNames(remoteResources);
local.commit();
syncResult.stats.numDeletes = local.deleteAllExceptRemoteNames(remoteResources);
syncResult.stats.numEntries = syncResult.stats.numInserts + syncResult.stats.numUpdates + syncResult.stats.numDeletes;
// update collection CTag
Log.i(TAG, "Sync complete, fetching new CTag");
@ -145,10 +141,8 @@ public class SyncManager {
local.updateETag(res, eTag);
local.clearDirty(res);
count++;
} catch(PreconditionFailedException e) {
Log.i(TAG, "Didn't overwrite existing resource with other content");
} catch (ValidationException e) {
Log.e(TAG, "Couldn't create entity for adding: " + e.toString());
} catch (PreconditionFailedException e) {
Log.i(TAG, "Didn't overwrite existing resource with other content");
} catch (RecordNotFoundException e) {
Log.wtf(TAG, "Couldn't read new record", e);
}
@ -171,10 +165,8 @@ public class SyncManager {
local.updateETag(res, eTag);
local.clearDirty(res);
count++;
} catch(PreconditionFailedException e) {
Log.i(TAG, "Locally changed resource has been changed on the server in the meanwhile");
} catch (ValidationException e) {
Log.e(TAG, "Couldn't create entity for updating: " + e.toString());
} catch (PreconditionFailedException e) {
Log.i(TAG, "Locally changed resource has been changed on the server in the meanwhile");
} catch (RecordNotFoundException e) {
Log.e(TAG, "Couldn't read dirty record", e);
}
@ -193,7 +185,6 @@ public class SyncManager {
for (Resource res : remote.multiGet(resources)) {
Log.d(TAG, "Adding " + res.getName());
local.add(res);
local.commit();
count++;
}
return count;
@ -207,7 +198,6 @@ public class SyncManager {
for (Resource res : remote.multiGet(resources)) {
Log.i(TAG, "Updating " + res.getName());
local.updateByRemoteName(res);
local.commit();
count++;
}
return count;

@ -61,7 +61,7 @@ public class TasksSyncAdapterService extends Service {
boolean preemptive = settings.getPreemptiveAuth();
try {
Map<LocalCollection<?>, RemoteCollection<?>> map = new HashMap<LocalCollection<?>, RemoteCollection<?>>();
Map<LocalCollection<?>, RemoteCollection<?>> map = new HashMap<>();
for (LocalTaskList calendar : LocalTaskList.findAll(account, provider)) {
RemoteCollection<?> dav = new CalDavTaskList(httpClient, calendar.getUrl(), userName, password, preemptive);

@ -22,8 +22,8 @@ import android.widget.TextView;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.ui.setup.AddAccountActivity;
import at.bitfire.davdroid.ui.settings.SettingsActivity;
import at.bitfire.davdroid.ui.setup.AddAccountActivity;
public class MainActivity extends Activity {

@ -12,7 +12,6 @@ import android.accounts.Account;
import android.app.Activity;
import android.app.FragmentManager;
import android.os.Bundle;
import android.util.Log;
import at.bitfire.davdroid.R;

@ -9,7 +9,6 @@
package at.bitfire.davdroid.ui.settings;
import android.accounts.Account;
import android.os.Build;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
@ -20,13 +19,10 @@ import android.preference.SwitchPreference;
import android.provider.CalendarContract;
import android.provider.ContactsContract;
import org.dmfs.provider.tasks.TaskContract;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.resource.LocalTaskList;
import at.bitfire.davdroid.syncadapter.AccountSettings;
import ezvcard.VCardVersion;
import lombok.Setter;
public class AccountFragment extends PreferenceFragment {
final static String ARG_ACCOUNT = "account";

@ -11,10 +11,8 @@ package at.bitfire.davdroid.ui.settings;
import android.accounts.Account;
import android.app.Activity;
import android.app.FragmentTransaction;
import android.app.ListFragment;
import android.content.Intent;
import android.os.Bundle;
import android.widget.ListView;
import at.bitfire.davdroid.R;

@ -32,7 +32,7 @@ public class SettingsScopeFragment extends ListFragment {
final String[] accountNames = new String[accounts.length];
for (int i = 0; i < accounts.length; i++)
accountNames[i] = accounts[i].name;
setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_activated_1, accountNames));
setListAdapter(new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_activated_1, accountNames));
return super.onCreateView(inflater, container, savedInstanceState);
}

@ -142,8 +142,7 @@ public class AccountDetailsFragment extends Fragment implements TextWatcher {
@Override
public void onPrepareOptionsMenu(Menu menu) {
boolean ok = false;
ok = editAccountName.getText().length() > 0;
boolean ok = editAccountName.getText().length() > 0;
MenuItem item = menu.findItem(R.id.add_account);
item.setEnabled(ok);
}

@ -14,7 +14,6 @@ import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R;
@ -45,11 +44,6 @@ public class AddAccountActivity extends Activity {
return true;
}
public void installTasksApp(View view) {
final Intent intent = new Intent(Intent.ACTION_VIEW).setData(Uri.parse("market://details?id=org.dmfs.tasks"));
startActivity(intent);
}
public void showHelp(MenuItem item) {
startActivityForResult(new Intent(Intent.ACTION_VIEW, Uri.parse(Constants.WEB_URL_HELP)), 0);
}

@ -8,20 +8,11 @@
package at.bitfire.davdroid.ui.setup;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@ -30,13 +21,9 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.resource.LocalTaskList;
public class InstallAppsFragment extends Fragment {
private static final String TAG = "davdroid.setup";
// https://code.google.com/p/android/issues/detail?id=25906
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@ -68,8 +55,6 @@ public class InstallAppsFragment extends Fragment {
}
protected void skip() {
FragmentManager fm = getFragmentManager();
getFragmentManager().beginTransaction()
.replace(R.id.right_pane, new SelectCollectionsFragment())
.addToBackStack(null)

@ -21,7 +21,7 @@ import at.bitfire.davdroid.R;
public class LoginTypeFragment extends Fragment {
protected RadioButton btnTypeEmail, btnTypeURL;
protected RadioButton btnTypeEmail;
@Override
@ -29,8 +29,7 @@ public class LoginTypeFragment extends Fragment {
View v = inflater.inflate(R.layout.setup_login_type, container, false);
btnTypeEmail = (RadioButton)v.findViewById(R.id.login_type_email);
btnTypeURL = (RadioButton)v.findViewById(R.id.login_type_url);
setHasOptionsMenu(true);
return v;

@ -21,13 +21,12 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import java.net.URI;
import java.net.URISyntaxException;
@ -40,8 +39,7 @@ public class LoginURLFragment extends Fragment implements TextWatcher {
protected TextView textHttpWarning;
protected EditText editBaseURI, editUserName, editPassword;
protected CheckBox checkboxPreemptive;
protected Button btnNext;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

@ -18,10 +18,9 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.Toast;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.http.HttpException;
import java.io.IOException;
@ -43,9 +42,7 @@ public class QueryServerDialogFragment extends DialogFragment implements LoaderC
EXTRA_USER_NAME = "user_name",
EXTRA_PASSWORD = "password",
EXTRA_AUTH_PREEMPTIVE = "auth_preemptive";
ProgressBar progressBar;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

@ -44,8 +44,8 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
TYPE_TASK_LISTS_HEADING = 4,
TYPE_TASK_LISTS_ROW = 5;
protected Context context;
protected ServerInfo serverInfo;
final protected Context context;
final protected ServerInfo serverInfo;
@Getter protected int
nAddressBooks, nAddressBookHeadings,
nCalendars, nCalendarHeadings,
@ -160,7 +160,6 @@ public class SelectCollectionsAdapter extends BaseAdapter implements ListAdapter
}
// step 2: fill view with content
int collectionIcon;
switch (viewType) {
case TYPE_ADDRESS_BOOKS_ROW:
setContent((CheckedTextView)v, R.drawable.addressbook, (ServerInfo.ResourceInfo)getItem(position));

@ -1,63 +0,0 @@
/*
* 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.ui.setup;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.List;
import at.bitfire.davdroid.webdav.WebDavResource;
public class WebDavResourceAdapter extends BaseAdapter {
protected int viewId;
protected LayoutInflater inflater;
WebDavResource[] items;
public WebDavResourceAdapter(Context context, int textViewResourceId, List<WebDavResource> objects) {
viewId = textViewResourceId;
inflater = LayoutInflater.from(context);
items = objects.toArray(new WebDavResource[0]);
}
@Override
public View getView(int position, View view, ViewGroup parent) {
WebDavResource item = items[position];
View itemView = (View)inflater.inflate(viewId, null);
TextView textName = (TextView) itemView.findViewById(android.R.id.text1);
textName.setText(item.getDisplayName());
TextView textDescription = (TextView) itemView.findViewById(android.R.id.text2);
String description = item.getDescription();
if (description == null)
description = item.getLocation().getPath();
textDescription.setText(description);
return itemView;
}
@Override
public int getCount() {
return items.length;
}
@Override
public Object getItem(int position) {
return items[position];
}
@Override
public long getItemId(int position) {
return position;
}
}

@ -11,7 +11,6 @@ package at.bitfire.davdroid.webdav;
import org.simpleframework.xml.Attribute;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Namespace;
import org.simpleframework.xml.Root;
import lombok.Getter;
import lombok.Setter;
@ -23,7 +22,7 @@ public class DavCompFilter {
}
@Attribute(required=false)
String name;
final String name;
@Element(required=false,name="comp-filter")
@Getter @Setter DavCompFilter compFilter;

@ -7,7 +7,7 @@
*/
package at.bitfire.davdroid.webdav;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.http.HttpRequest;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandlerHC4;

@ -49,7 +49,7 @@ public class DavMultiget {
multiget.prop.calendarData = new DavProp.CalendarData();
}
multiget.hrefs = new ArrayList<DavHref>(names.length);
multiget.hrefs = new ArrayList<>(names.length);
for (String name : names)
multiget.hrefs.add(new DavHref(name));

@ -13,8 +13,6 @@ import org.simpleframework.xml.Root;
import java.util.List;
import lombok.Getter;
@Root(strict=false)
public class DavResponse {
@Element

@ -56,19 +56,19 @@ public class HttpPropfind extends HttpEntityEnclosingRequestBaseHC4 {
depth = 1;
propfind.prop.displayname = new DavProp.DisplayName();
propfind.prop.resourcetype = new DavProp.ResourceType();
propfind.prop.currentUserPrivilegeSet = new LinkedList<DavProp.Privilege>();
propfind.prop.currentUserPrivilegeSet = new LinkedList<>();
propfind.prop.addressbookDescription = new DavProp.AddressbookDescription();
propfind.prop.supportedAddressData = new LinkedList<DavProp.AddressDataType>();
propfind.prop.supportedAddressData = new LinkedList<>();
break;
case CALDAV_COLLECTIONS:
depth = 1;
propfind.prop.displayname = new DavProp.DisplayName();
propfind.prop.resourcetype = new DavProp.ResourceType();
propfind.prop.currentUserPrivilegeSet = new LinkedList<DavProp.Privilege>();
propfind.prop.currentUserPrivilegeSet = new LinkedList<>();
propfind.prop.calendarDescription = new DavProp.CalendarDescription();
propfind.prop.calendarColor = new DavProp.CalendarColor();
propfind.prop.calendarTimezone = new DavProp.CalendarTimezone();
propfind.prop.supportedCalendarComponentSet = new LinkedList<DavProp.Comp>();
propfind.prop.supportedCalendarComponentSet = new LinkedList<>();
break;
case COLLECTION_CTAG:
propfind.prop.getctag = new DavProp.GetCTag();

@ -11,7 +11,7 @@ package at.bitfire.davdroid.webdav;
import android.os.Build;
import android.util.Log;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.conn.ssl.BrowserCompatHostnameVerifierHC4;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.X509HostnameVerifier;
@ -28,7 +28,7 @@ import javax.net.ssl.SSLSocketFactory;
import lombok.Cleanup;
public class TlsSniSocketFactory extends SSLConnectionSocketFactory {
private static final String TAG = "davdroid.TlsSniSocketFactory";
private static final String TAG = "davdroid.TLS_SNI";
public static TlsSniSocketFactory getSocketFactory() {
return new TlsSniSocketFactory(
@ -48,41 +48,39 @@ public class TlsSniSocketFactory extends SSLConnectionSocketFactory {
/* set reasonable protocol versions */
// - enable all supported protocols (enables TLSv1.1 and TLSv1.2 on Android <5.0)
// - remove all SSL versions (especially SSLv3) because they're insecure now
List<String> protocols = new LinkedList<String>();
List<String> protocols = new LinkedList<>();
for (String protocol : socket.getSupportedProtocols())
if (!protocol.toUpperCase().contains("SSL"))
protocols.add(protocol);
Log.v(TAG, "Setting allowed TLS protocols: " + StringUtils.join(protocols, ", "));
TlsSniSocketFactory.protocols = protocols.toArray(new String[0]);
TlsSniSocketFactory.protocols = protocols.toArray(new String[protocols.size()]);
/* set reasonable cipher suites */
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
// choose secure cipher suites
List<String> allowedCiphers = Arrays.asList(new String[]{
// allowed secure ciphers according to NIST.SP.800-52r1.pdf Section 3.3.1 (see docs directory)
// TLS 1.2
"TLS_RSA_WITH_AES_256_GCM_SHA384",
"TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECHDE_RSA_WITH_AES_128_GCM_SHA256",
// maximum interoperability
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_RSA_WITH_AES_128_CBC_SHA",
// additionally
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
});
List<String> allowedCiphers = Arrays.asList(
// TLS 1.2
"TLS_RSA_WITH_AES_256_GCM_SHA384",
"TLS_RSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
"TLS_ECHDE_RSA_WITH_AES_128_GCM_SHA256",
// maximum interoperability
"TLS_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_RSA_WITH_AES_128_CBC_SHA",
// additionally
"TLS_RSA_WITH_AES_256_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
List<String> availableCiphers = Arrays.asList(socket.getSupportedCipherSuites());
// preferred ciphers = allowed Ciphers \ availableCiphers
HashSet<String> preferredCiphers = new HashSet<String>(allowedCiphers);
HashSet<String> preferredCiphers = new HashSet<>(allowedCiphers);
preferredCiphers.retainAll(availableCiphers);
// add preferred ciphers to enabled ciphers
@ -90,10 +88,10 @@ public class TlsSniSocketFactory extends SSLConnectionSocketFactory {
// but I guess for the security level of DAVdroid, disabling of insecure
// ciphers should be a server-side task
HashSet<String> enabledCiphers = preferredCiphers;
enabledCiphers.addAll(new HashSet<String>(Arrays.asList(socket.getEnabledCipherSuites())));
enabledCiphers.addAll(new HashSet<>(Arrays.asList(socket.getEnabledCipherSuites())));
Log.v(TAG, "Setting allowed TLS ciphers: " + StringUtils.join(enabledCiphers, ", "));
TlsSniSocketFactory.cipherSuites = enabledCiphers.toArray(new String[0]);
TlsSniSocketFactory.cipherSuites = enabledCiphers.toArray(new String[enabledCiphers.size()]);
}
} catch (IOException e) {
}

@ -9,7 +9,7 @@ package at.bitfire.davdroid.webdav;
import android.util.Log;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
@ -25,7 +25,6 @@ import org.apache.http.client.methods.HttpGetHC4;
import org.apache.http.client.methods.HttpOptionsHC4;
import org.apache.http.client.methods.HttpPutHC4;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.client.utils.URIUtilsHC4;
import org.apache.http.entity.ByteArrayEntityHC4;
import org.apache.http.impl.auth.BasicSchemeHC4;
import org.apache.http.impl.client.BasicAuthCache;
@ -83,11 +82,11 @@ public class WebDavResource {
@Getter protected URI location;
// DAV capabilities (DAV: header) and allowed DAV methods (set for OPTIONS request)
protected Set<String> capabilities = new HashSet<String>(),
methods = new HashSet<String>();
protected Set<String> capabilities = new HashSet<>(),
methods = new HashSet<>();
// DAV properties
protected HashMap<Property, String> properties = new HashMap<Property, String>();
protected HashMap<Property, String> properties = new HashMap<>();
@Getter protected List<String> supportedComponents;
// list of members (only for collections)
@ -294,13 +293,13 @@ public class WebDavResource {
// so we have to handle redirections manually and create a new request for the new location
for (int i = context.getRequestConfig().getMaxRedirects(); i > 0; i--) {
// build multi-get XML request
List<String> hrefs = new LinkedList<String>();
List<String> hrefs = new LinkedList<>();
for (String name : names)
// name may contain "%" which have to be encoded → use non-quoting URI constructor and getRawPath()
// name may also contain ":", so prepend "./" because even the non-quoting URI constructor parses after constructing
// DAVdroid ensures that collections always have a trailing slash, so "./" won't go down in directory hierarchy
hrefs.add(location.resolve(new URI(null, null, "./" + name, null)).getRawPath());
DavMultiget multiget = DavMultiget.newRequest(type, hrefs.toArray(new String[0]));
DavMultiget multiget = DavMultiget.newRequest(type, hrefs.toArray(new String[hrefs.size()]));
StringWriter writer = new StringWriter();
try {
@ -456,7 +455,7 @@ public class WebDavResource {
return;
// member list will be built from response
List<WebDavResource> members = new LinkedList<WebDavResource>();
List<WebDavResource> members = new LinkedList<>();
// iterate through all resources (either ourselves or member)
for (DavResponse singleResponse : multiStatus.response) {
@ -470,7 +469,7 @@ public class WebDavResource {
Log.d(TAG, "Processing multi-status element: " + href);
// process known properties
HashMap<Property, String> properties = new HashMap<Property, String>();
HashMap<Property, String> properties = new HashMap<>();
List<String> supportedComponents = null;
byte[] data = null;
@ -551,7 +550,7 @@ public class WebDavResource {
}
if (prop.supportedCalendarComponentSet != null) {
supportedComponents = new LinkedList<String>();
supportedComponents = new LinkedList<>();
for (Comp component : prop.supportedCalendarComponentSet)
supportedComponents.add(component.getName());
}

Loading…
Cancel
Save