mirror of
https://github.com/etesync/android
synced 2024-11-25 17:38:13 +00:00
Synchronization logging to external file
* use ExternalFileLogger to log synchronization, if enabled in Settings * new settings: log to external file / log verbose * DavResource: check for well-known even if service type of user-given URL can't be determined * remove oblsete testing assets
This commit is contained in:
parent
dd50f10c58
commit
58f05986c9
@ -1,11 +0,0 @@
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//hacksw/handcal//NONSGML v1.0//EN
|
||||
BEGIN:VEVENT
|
||||
UID:all-day-0sec@example.com
|
||||
DTSTAMP:20140101T000000Z
|
||||
DTSTART;VALUE=DATE:19970714
|
||||
DTEND;VALUE=DATE:19970714
|
||||
SUMMARY:0 Sec Event
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
@ -1,11 +0,0 @@
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//hacksw/handcal//NONSGML v1.0//EN
|
||||
BEGIN:VEVENT
|
||||
UID:all-day-10days@example.com
|
||||
DTSTAMP:20140101T000000Z
|
||||
DTSTART;VALUE=DATE:19970714
|
||||
DTEND;VALUE=DATE:19970724
|
||||
SUMMARY:All-Day 10 Days
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
@ -1,11 +0,0 @@
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//hacksw/handcal//NONSGML v1.0//EN
|
||||
BEGIN:VEVENT
|
||||
UID:all-day-1day@example.com
|
||||
DTSTAMP:20140101T000000Z
|
||||
DTSTART;VALUE=DATE:19970714
|
||||
DTEND;VALUE=DATE:19970714
|
||||
SUMMARY:All-Day 1 Day
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
Binary file not shown.
Before Width: | Height: | Size: 23 KiB |
@ -1,11 +0,0 @@
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:-//hacksw/handcal//NONSGML v1.0//EN
|
||||
BEGIN:VEVENT
|
||||
UID:event-on-that-day@example.com
|
||||
DTSTAMP:19970714T170000Z
|
||||
ORGANIZER;CN=John Doe:MAILTO:john.doe@example.com
|
||||
DTSTART;VALUE=DATE:19970714
|
||||
SUMMARY:Bastille Day Party
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
@ -1,9 +0,0 @@
|
||||
BEGIN:VCARD
|
||||
VERSION:3.0
|
||||
UID:2de59c6cc9
|
||||
PRODID:-//ownCloud//NONSGML Contacts 0.2.5//EN
|
||||
REV:2013-12-08T00:04:30+00:00
|
||||
FN:test mctest
|
||||
N:mctest;test;;;
|
||||
IMPP;TYPE=WORK;X-SERVICE-TYPE=jabber:test-without-valid-scheme@test.tld
|
||||
END:VCARD
|
@ -1,5 +0,0 @@
|
||||
BEGIN:VCARD
|
||||
VERSION:3.0
|
||||
FN:VCard with invalid unknown properties
|
||||
X-UNKNOWN@PROPERTY:MUST-NOT_CONTAIN?OTHER*LETTERS;
|
||||
END:VCARD
|
@ -1,5 +0,0 @@
|
||||
BEGIN:VCARD
|
||||
VERSION:3.0
|
||||
N:Äuçek;Özkan
|
||||
FN:Özkan Äuçek
|
||||
END:VCARD
|
@ -1,17 +0,0 @@
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
BEGIN:VEVENT
|
||||
UID:fcb42e4d-bc6e-4499-97f0-6616a02da7bc
|
||||
SUMMARY:Recurring event with one exception
|
||||
RRULE:FREQ=DAILY;COUNT=5
|
||||
DTSTART;VALUE=DATE:20150501
|
||||
DTEND;VALUE=DATE:20150502
|
||||
END:VEVENT
|
||||
BEGIN:VEVENT
|
||||
UID:fcb42e4d-bc6e-4499-97f0-6616a02da7bc
|
||||
RECURRENCE-ID;VALUE=DATE:20150503
|
||||
DTSTART;VALUE=DATE:20150503
|
||||
DTEND;VALUE=DATE:20150504
|
||||
SUMMARY:Another summary for the third day
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
@ -1,16 +0,0 @@
|
||||
BEGIN:VCARD
|
||||
VERSION:3.0
|
||||
N:Gümp;Förrest;Mr.
|
||||
FN:Förrest Gümp
|
||||
ORG:Bubba Gump Shrimpß Co.
|
||||
TITLE:Shrimp Man
|
||||
PHOTO;VALUE=URL;TYPE=PNG:http://192.168.0.11:3000/assets/davdroid-logo-192.png
|
||||
TEL;TYPE=WORK,VOICE:(111) 555-1212
|
||||
TEL;TYPE=HOME,VOICE:(404) 555-1212
|
||||
ADR;TYPE=WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America
|
||||
LABEL;TYPE=WORK:100 Waters Edge\nBaytown, LA 30314\nUnited States of America
|
||||
ADR;TYPE=HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America
|
||||
LABEL;TYPE=HOME:42 Plantation St.\nBaytown, LA 30314\nUnited States of America
|
||||
EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com
|
||||
REV:2008-04-24T19:52:43Z
|
||||
END:VCARD
|
Binary file not shown.
@ -1,14 +0,0 @@
|
||||
BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
PRODID:Blabla
|
||||
BEGIN:VEVENT
|
||||
CLASS:PUBLIC
|
||||
CREATED;VALUE=DATE-TIME:20131008T205713
|
||||
LAST-MODIFIED;VALUE=DATE-TIME:20131008T205740
|
||||
SUMMARY:online Anmeldung
|
||||
DESCRIPTION:http://www.tgbornheim.de/index.php?sessionid=&page=&id=&sportce
|
||||
ntergroup=&day=6
|
||||
UID:b99c41704b
|
||||
DTSTART;VALUE=DATE-TIME;TZID=Europe/Berlin:20131019T060000
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
@ -1,16 +0,0 @@
|
||||
BEGIN:VCARD
|
||||
VERSION:3.0
|
||||
N:Gump;Forrest
|
||||
FN:Forrest Gump
|
||||
ORG:Bubba Gump Shrimp Co.
|
||||
TITLE:Shrimp Man
|
||||
PHOTO;VALUE=URL;TYPE=GIF:http://www.example.com/dir_photos/my_photo.gif
|
||||
TEL;TYPE=WORK,VOICE:(111) 555-1212
|
||||
TEL;TYPE=HOME,VOICE:(404) 555-1212
|
||||
ADR;TYPE=WORK:;;100 Waters Edge;Baytown;LA;30314;United States of America
|
||||
LABEL;TYPE=WORK:100 Waters Edge\nBaytown, LA 30314\nUnited States of America
|
||||
ADR;TYPE=HOME:;;42 Plantation St.;Baytown;LA;30314;United States of America
|
||||
LABEL;TYPE=HOME:42 Plantation St.\nBaytown, LA 30314\nUnited States of America
|
||||
EMAIL;TYPE=PREF,INTERNET:forrestgump@example.com
|
||||
REV:2008-04-24T19:52:43Z
|
||||
END:VCARD
|
@ -1,33 +0,0 @@
|
||||
BEGIN:VCALENDAR
|
||||
PRODID:-//Ximian//NONSGML Evolution Calendar//EN
|
||||
VERSION:2.0
|
||||
METHOD:PUBLISH
|
||||
BEGIN:VTIMEZONE
|
||||
TZID:/freeassociation.sourceforge.net/Tzfile/Europe/Vienna
|
||||
X-LIC-LOCATION:Europe/Vienna
|
||||
BEGIN:STANDARD
|
||||
TZNAME:CET
|
||||
DTSTART:19701027T030000
|
||||
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
|
||||
TZOFFSETFROM:+0200
|
||||
TZOFFSETTO:+0100
|
||||
END:STANDARD
|
||||
BEGIN:DAYLIGHT
|
||||
TZNAME:CEST
|
||||
DTSTART:19700331T020000
|
||||
RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=3
|
||||
TZOFFSETFROM:+0100
|
||||
TZOFFSETTO:+0200
|
||||
END:DAYLIGHT
|
||||
END:VTIMEZONE
|
||||
BEGIN:VEVENT
|
||||
UID:c252087c-7354-4722-aea9-0e7d86c01a25
|
||||
DTSTAMP:20130926T151211Z
|
||||
SUMMARY:Test-Ereignis im schönen Wien
|
||||
DTSTART;TZID=/freeassociation.sourceforge.net/Tzfile/Europe/Vienna:20131009T170000
|
||||
DTEND;TZID=/freeassociation.sourceforge.net/Tzfile/Europe/Vienna:20131009T180000
|
||||
X-RADICALE-NAME:97929342-291a-434e-bf1a-fa1749bf99d0.ics
|
||||
X-EVOLUTION-CALDAV-HREF:/radicale/rfc2822/default.ics/97929342-291a-434e-bf1a-fa1749bf99d0.ics
|
||||
X-EVOLUTION-CALDAV-ETAG:\"-3264224243575339985\"
|
||||
END:VEVENT
|
||||
END:VCALENDAR
|
@ -20,6 +20,7 @@
|
||||
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
|
||||
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="18" />
|
||||
|
||||
<uses-permission android:name="org.dmfs.permission.READ_TASKS" />
|
||||
<uses-permission android:name="org.dmfs.permission.WRITE_TASKS" />
|
||||
|
@ -21,6 +21,8 @@ import com.squareup.okhttp.Response;
|
||||
import com.squareup.okhttp.internal.tls.OkHostnameVerifier;
|
||||
import com.squareup.okhttp.logging.HttpLoggingInterceptor;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.Proxy;
|
||||
import java.security.KeyManagementException;
|
||||
@ -39,18 +41,7 @@ import de.duenndns.ssl.MemorizingTrustManager;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
public class HttpClient extends OkHttpClient {
|
||||
protected static final String HEADER_AUTHORIZATION = "Authorization";
|
||||
|
||||
final static UserAgentInterceptor userAgentInterceptor = new UserAgentInterceptor();
|
||||
final static HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
|
||||
@Override
|
||||
public void log(String message) {
|
||||
Constants.log.trace(message);
|
||||
}
|
||||
});
|
||||
static {
|
||||
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
|
||||
}
|
||||
|
||||
static final String userAgent;
|
||||
static {
|
||||
@ -58,12 +49,14 @@ public class HttpClient extends OkHttpClient {
|
||||
userAgent = "DAVdroid/" + BuildConfig.VERSION_NAME + " (" + date + "; dav4android) Android/" + Build.VERSION.RELEASE;
|
||||
}
|
||||
|
||||
final Logger log;
|
||||
final Context context;
|
||||
protected String username, password;
|
||||
|
||||
|
||||
protected HttpClient(Context context) {
|
||||
protected HttpClient(final Logger log, Context context) {
|
||||
super();
|
||||
this.log = (log != null) ? log : Constants.log;
|
||||
this.context = context;
|
||||
|
||||
if (context != null) {
|
||||
@ -90,12 +83,20 @@ public class HttpClient extends OkHttpClient {
|
||||
networkInterceptors().add(userAgentInterceptor);
|
||||
|
||||
// enable verbose logs, if requested
|
||||
if (Constants.log.isTraceEnabled())
|
||||
enableLogs();
|
||||
if (log.isTraceEnabled()) {
|
||||
HttpLoggingInterceptor logger = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
|
||||
@Override
|
||||
public void log(String message) {
|
||||
log.trace(message);
|
||||
}
|
||||
});
|
||||
logger.setLevel(HttpLoggingInterceptor.Level.BODY);
|
||||
interceptors().add(logger);
|
||||
}
|
||||
}
|
||||
|
||||
public HttpClient(Context context, String username, String password, boolean preemptive) {
|
||||
this(context);
|
||||
public HttpClient(Logger log, Context context, String username, String password, boolean preemptive) {
|
||||
this(log, context);
|
||||
|
||||
// authentication
|
||||
this.username = username;
|
||||
@ -113,8 +114,8 @@ public class HttpClient extends OkHttpClient {
|
||||
* @param client user name and password from this client will be used
|
||||
* @param host authentication will be restricted to this host
|
||||
*/
|
||||
public HttpClient(HttpClient client, String host) {
|
||||
this(client.context);
|
||||
public HttpClient(Logger log, HttpClient client, String host) {
|
||||
this(log, client.context);
|
||||
|
||||
username = client.username;
|
||||
password = client.password;
|
||||
@ -123,12 +124,7 @@ public class HttpClient extends OkHttpClient {
|
||||
|
||||
// for testing (mock server doesn't need auth)
|
||||
protected HttpClient() {
|
||||
this(null, null, null, false);
|
||||
}
|
||||
|
||||
|
||||
protected void enableLogs() {
|
||||
interceptors().add(loggingInterceptor);
|
||||
this(null, null, null, null, false);
|
||||
}
|
||||
|
||||
|
||||
@ -142,7 +138,6 @@ public class HttpClient extends OkHttpClient {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@RequiredArgsConstructor
|
||||
static class PreemptiveAuthenticationInterceptor implements Interceptor {
|
||||
final String username, password;
|
||||
|
348
app/src/main/java/at/bitfire/davdroid/log/CustomLogger.java
Normal file
348
app/src/main/java/at/bitfire/davdroid/log/CustomLogger.java
Normal file
@ -0,0 +1,348 @@
|
||||
/*
|
||||
* 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.log;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.Marker;
|
||||
|
||||
/**
|
||||
* A DAVdroid logger base class that wraps all calls around some standard log() calls.
|
||||
* @throws UnsupportedOperationException for all methods with Marker
|
||||
* arguments (as Markers are not used by DAVdroid logging).
|
||||
*/
|
||||
public abstract class CustomLogger implements Logger {
|
||||
|
||||
private static final String
|
||||
PREFIX_ERROR = "[error] ",
|
||||
PREFIX_WARN = "[warn ] ",
|
||||
PREFIX_INFO = "[info ] ",
|
||||
PREFIX_DEBUG = "[debug] ",
|
||||
PREFIX_TRACE = "[trace] ";
|
||||
|
||||
protected boolean verbose;
|
||||
|
||||
protected abstract void log(String prefix, String msg);
|
||||
protected abstract void log(String prefix, String format, Object arg);
|
||||
protected abstract void log(String prefix, String format, Object arg1, Object arg2);
|
||||
protected abstract void log(String prefix, String format, Object... args);
|
||||
protected abstract void log(String prefix, String msg, Throwable t);
|
||||
|
||||
|
||||
// STANDARD CALLS
|
||||
|
||||
@Override
|
||||
public boolean isTraceEnabled() {
|
||||
return verbose;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String msg) {
|
||||
if (verbose)
|
||||
log(PREFIX_TRACE, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String format, Object arg) {
|
||||
if (verbose)
|
||||
log(PREFIX_TRACE, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String format, Object arg1, Object arg2) {
|
||||
if (verbose) log(PREFIX_TRACE, format, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String format, Object... arguments) {
|
||||
if (verbose)
|
||||
log(PREFIX_TRACE, format, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(String msg, Throwable t) {
|
||||
if (verbose)
|
||||
log(PREFIX_TRACE, msg, t);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isDebugEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String msg) {
|
||||
log(PREFIX_DEBUG, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String format, Object arg) {
|
||||
log(PREFIX_DEBUG, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String format, Object arg1, Object arg2) {
|
||||
log(PREFIX_DEBUG, format, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String format, Object... arguments) {
|
||||
log(PREFIX_DEBUG, format, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(String msg, Throwable t) {
|
||||
log(PREFIX_DEBUG, msg, t);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isInfoEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String msg) {
|
||||
log(PREFIX_INFO, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String format, Object arg) {
|
||||
log(PREFIX_INFO, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String format, Object arg1, Object arg2) {
|
||||
log(PREFIX_INFO, format, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String format, Object... arguments) {
|
||||
log(PREFIX_INFO, format, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(String msg, Throwable t) {
|
||||
log(PREFIX_INFO, msg, t);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isWarnEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String msg) {
|
||||
log(PREFIX_WARN, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String format, Object arg) {
|
||||
log(PREFIX_WARN, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String format, Object... arguments) {
|
||||
log(PREFIX_WARN, format, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String format, Object arg1, Object arg2) {
|
||||
log(PREFIX_WARN, format, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(String msg, Throwable t) {
|
||||
log(PREFIX_WARN, msg, t);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isErrorEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String msg) {
|
||||
log(PREFIX_ERROR, msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String format, Object arg) {
|
||||
log(PREFIX_ERROR, format, arg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String format, Object arg1, Object arg2) {
|
||||
log(PREFIX_ERROR, format, arg1, arg2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String format, Object... arguments) {
|
||||
log(PREFIX_ERROR, format, arguments);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(String msg, Throwable t) {
|
||||
log(PREFIX_ERROR, msg, t);
|
||||
}
|
||||
|
||||
|
||||
// CALLS WITH MARKER
|
||||
|
||||
@Override
|
||||
public boolean isTraceEnabled(Marker marker) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(Marker marker, String msg) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(Marker marker, String format, Object arg) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(Marker marker, String format, Object arg1, Object arg2) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(Marker marker, String format, Object... argArray) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trace(Marker marker, String msg, Throwable t) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDebugEnabled(Marker marker) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(Marker marker, String msg) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(Marker marker, String format, Object arg) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(Marker marker, String format, Object arg1, Object arg2) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(Marker marker, String format, Object... arguments) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debug(Marker marker, String msg, Throwable t) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInfoEnabled(Marker marker) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(Marker marker, String msg) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(Marker marker, String format, Object arg) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(Marker marker, String format, Object arg1, Object arg2) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(Marker marker, String format, Object... arguments) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void info(Marker marker, String msg, Throwable t) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isWarnEnabled(Marker marker) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(Marker marker, String msg) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(Marker marker, String format, Object arg) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(Marker marker, String format, Object arg1, Object arg2) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(Marker marker, String format, Object... arguments) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void warn(Marker marker, String msg, Throwable t) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isErrorEnabled(Marker marker) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(Marker marker, String msg) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(Marker marker, String format, Object arg) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(Marker marker, String format, Object arg1, Object arg2) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(Marker marker, String format, Object... arguments) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void error(Marker marker, String msg, Throwable t) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* 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.log;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
public class ExternalFileLogger extends CustomLogger implements Closeable {
|
||||
|
||||
@Getter protected final String name;
|
||||
|
||||
protected final PrintWriter writer;
|
||||
|
||||
public ExternalFileLogger(Context context, String fileName, boolean verbose) throws IOException {
|
||||
this.verbose = verbose;
|
||||
|
||||
File dir = context.getExternalFilesDir(null);
|
||||
if (dir == null)
|
||||
throw new IOException("External media not available for log creation");
|
||||
|
||||
File log = new File(dir, name = fileName);
|
||||
writer = new PrintWriter(log);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
writer.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void log(String prefix, String msg) {
|
||||
writer.write(prefix + msg + "\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void log(String prefix, String format, Object arg) {
|
||||
writer.write(prefix + format.replace("{}", arg.toString()) + "\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void log(String prefix, String format, Object arg1, Object arg2) {
|
||||
writer.write(prefix + format.replaceFirst("\\{\\}", arg1.toString()).replaceFirst("\\{\\}", arg2.toString()) + "\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void log(String prefix, String format, Object... args) {
|
||||
String message = prefix;
|
||||
for (Object arg : args)
|
||||
format.replaceFirst("\\{\\}", arg.toString());
|
||||
writer.write(prefix + format + "\n");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void log(String prefix, String msg, Throwable t) {
|
||||
writer.write(prefix + msg + " - EXCEPTION:");
|
||||
t.printStackTrace(writer);
|
||||
writer.write("CAUSED BY:\n");
|
||||
ExceptionUtils.printRootCauseStackTrace(t, writer);
|
||||
}
|
||||
|
||||
}
|
@ -12,6 +12,7 @@ import android.text.TextUtils;
|
||||
|
||||
import com.squareup.okhttp.HttpUrl;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.xbill.DNS.Lookup;
|
||||
import org.xbill.DNS.Record;
|
||||
import org.xbill.DNS.SRVRecord;
|
||||
@ -57,7 +58,8 @@ public class DavResourceFinder {
|
||||
@Override public String toString() { return name; }
|
||||
};
|
||||
|
||||
protected Context context;
|
||||
protected final Logger log;
|
||||
protected final Context context;
|
||||
protected final HttpClient httpClient;
|
||||
protected final ServerInfo serverInfo;
|
||||
|
||||
@ -67,11 +69,12 @@ public class DavResourceFinder {
|
||||
taskLists = new HashMap<>();
|
||||
|
||||
|
||||
public DavResourceFinder(Context context, ServerInfo serverInfo) {
|
||||
public DavResourceFinder(Logger log, Context context, ServerInfo serverInfo) {
|
||||
this.log = log;
|
||||
this.context = context;
|
||||
this.serverInfo = serverInfo;
|
||||
|
||||
httpClient = new HttpClient(context, serverInfo.getUserName(), serverInfo.getPassword(), serverInfo.authPreemptive);
|
||||
httpClient = new HttpClient(log, context, serverInfo.getUserName(), serverInfo.getPassword(), serverInfo.authPreemptive);
|
||||
}
|
||||
|
||||
public void findResources() throws URISyntaxException, IOException, HttpException, DavException {
|
||||
@ -101,7 +104,7 @@ public class DavResourceFinder {
|
||||
2. user-given URL has a calendar-home-set property (i.e. is a principal URL)
|
||||
*/
|
||||
Constants.log.info("Check whether user-given URL is a calendar collection and/or contains <calendar-home-set> and/or has <current-user-principal>");
|
||||
DavResource davBase = new DavResource(httpClient, userURL);
|
||||
DavResource davBase = new DavResource(log, httpClient, userURL);
|
||||
try {
|
||||
if (service == Service.CALDAV) {
|
||||
davBase.propfind(0,
|
||||
@ -158,18 +161,23 @@ public class DavResourceFinder {
|
||||
CurrentUserPrincipal currentUserPrincipal = (CurrentUserPrincipal)davBase.properties.get(CurrentUserPrincipal.NAME);
|
||||
if (currentUserPrincipal != null && currentUserPrincipal.href != null)
|
||||
principalUrl = davBase.location.resolve(currentUserPrincipal.href);
|
||||
|
||||
if (principalUrl == null) {
|
||||
Constants.log.info("User-given URL doesn't contain <current-user-principal>, trying /.well-known/" + service.name);
|
||||
principalUrl = getCurrentUserPrincipal(userURL.resolve("/.well-known/" + service.name));
|
||||
}
|
||||
}
|
||||
} catch(IOException|HttpException|DavException e) {
|
||||
Constants.log.debug("Couldn't find <current-user-principal>", e);
|
||||
Constants.log.debug("Couldn't find <current-user-principal> at user-given URL", e);
|
||||
}
|
||||
|
||||
// try service discovery with "domain" = user-given host name
|
||||
domain = baseURI.getHost();
|
||||
if (principalUrl == null)
|
||||
try {
|
||||
Constants.log.info("User-given URL doesn't contain <current-user-principal>, trying /.well-known/" + service.name);
|
||||
principalUrl = getCurrentUserPrincipal(userURL.resolve("/.well-known/" + service.name));
|
||||
} catch(IOException|HttpException e) {
|
||||
Constants.log.debug("Couldn't determine <current-user-principal> from well-known " + service + " path", e);
|
||||
}
|
||||
|
||||
if (principalUrl == null)
|
||||
// still no principal URL, try service discovery with "domain" = user-given host name
|
||||
domain = baseURI.getHost();
|
||||
|
||||
} else if ("mailto".equals(baseURI.getScheme())) {
|
||||
String mailbox = baseURI.getSchemeSpecificPart();
|
||||
|
||||
@ -190,7 +198,7 @@ public class DavResourceFinder {
|
||||
if (principalUrl != null) {
|
||||
Constants.log.info("Principal URL=" + principalUrl + ", getting <calendar-home-set>");
|
||||
try {
|
||||
DavResource principal = new DavResource(httpClient, principalUrl);
|
||||
DavResource principal = new DavResource(log, httpClient, principalUrl);
|
||||
|
||||
if (service == Service.CALDAV) {
|
||||
principal.propfind(0, CalendarHomeSet.NAME);
|
||||
@ -226,7 +234,7 @@ public class DavResourceFinder {
|
||||
if (service == Service.CALDAV)
|
||||
try {
|
||||
Constants.log.info("Listing calendar collections in home set " + url);
|
||||
DavResource homeSet = new DavResource(httpClient, url);
|
||||
DavResource homeSet = new DavResource(log, httpClient, url);
|
||||
homeSet.propfind(1, SupportedCalendarComponentSet.NAME, ResourceType.NAME, DisplayName.NAME, CurrentUserPrivilegeSet.NAME,
|
||||
CalendarColor.NAME, CalendarDescription.NAME, CalendarTimezone.NAME);
|
||||
|
||||
@ -242,7 +250,7 @@ public class DavResourceFinder {
|
||||
else if (service == Service.CARDDAV)
|
||||
try {
|
||||
Constants.log.info("Listing address books in home set " + url);
|
||||
DavResource homeSet = new DavResource(httpClient, url);
|
||||
DavResource homeSet = new DavResource(log, httpClient, url);
|
||||
homeSet.propfind(1, ResourceType.NAME, DisplayName.NAME, CurrentUserPrivilegeSet.NAME, AddressbookDescription.NAME);
|
||||
|
||||
// home set should not be an address book, but some servers have only one address book and it's the home set
|
||||
@ -361,45 +369,40 @@ public class DavResourceFinder {
|
||||
* @param service service to discover (CALDAV or CARDDAV)
|
||||
* @return principal URL, or null if none found
|
||||
*/
|
||||
protected HttpUrl discoverPrincipalUrl(String domain, Service service) {
|
||||
protected HttpUrl discoverPrincipalUrl(String domain, Service service) throws IOException, HttpException, DavException {
|
||||
String scheme = null;
|
||||
String fqdn = null;
|
||||
Integer port = null;
|
||||
List<String> paths = new LinkedList<>(); // there may be multiple paths to try
|
||||
|
||||
try {
|
||||
final String query = "_" + service.name + "s._tcp." + domain;
|
||||
Constants.log.debug("Looking up SRV records for " + query);
|
||||
Record[] records = new Lookup(query, Type.SRV).run();
|
||||
final String query = "_" + service.name + "s._tcp." + domain;
|
||||
Constants.log.debug("Looking up SRV records for " + query);
|
||||
Record[] records = new Lookup(query, Type.SRV).run();
|
||||
if (records != null && records.length >= 1) {
|
||||
// choose SRV record to use (query may return multiple SRV records)
|
||||
SRVRecord srv = selectSRVRecord(records);
|
||||
|
||||
scheme = "https";
|
||||
fqdn = srv.getTarget().toString(true);
|
||||
port = srv.getPort();
|
||||
Constants.log.info("Found " + service + " service: fqdn=" + fqdn + ", port=" + port);
|
||||
|
||||
// look for TXT record too (for initial context path)
|
||||
records = new Lookup(domain, Type.TXT).run();
|
||||
if (records != null && records.length >= 1) {
|
||||
// choose SRV record to use (query may return multiple SRV records)
|
||||
SRVRecord srv = selectSRVRecord(records);
|
||||
|
||||
scheme = "https";
|
||||
fqdn = srv.getTarget().toString(true);
|
||||
port = srv.getPort();
|
||||
Constants.log.info("Found " + service + " service: fqdn=" + fqdn + ", port=" + port);
|
||||
|
||||
// look for TXT record too (for initial context path)
|
||||
records = new Lookup(domain, Type.TXT).run();
|
||||
if (records != null && records.length >= 1) {
|
||||
TXTRecord txt = (TXTRecord)records[0];
|
||||
for (String segment : (String[])txt.getStrings().toArray(new String[0]))
|
||||
if (segment.startsWith("path=")) {
|
||||
paths.add(segment.substring(5));
|
||||
Constants.log.info("Found TXT record; initial context path=" + paths);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if there's TXT record if it it's wrong, try well-known
|
||||
paths.add("/.well-known/" + service.name);
|
||||
// if this fails, too, try "/"
|
||||
paths.add("/");
|
||||
TXTRecord txt = (TXTRecord)records[0];
|
||||
for (String segment : (String[])txt.getStrings().toArray(new String[0]))
|
||||
if (segment.startsWith("path=")) {
|
||||
paths.add(segment.substring(5));
|
||||
Constants.log.info("Found TXT record; initial context path=" + paths);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Constants.log.debug("SRV/TXT record discovery failed", e);
|
||||
return null;
|
||||
|
||||
// if there's TXT record if it it's wrong, try well-known
|
||||
paths.add("/.well-known/" + service.name);
|
||||
// if this fails, too, try "/"
|
||||
paths.add("/");
|
||||
}
|
||||
|
||||
for (String path : paths) {
|
||||
@ -424,20 +427,16 @@ public class DavResourceFinder {
|
||||
* @param url URL to query with PROPFIND (Depth: 0)
|
||||
* @return current-user-principal URL, or null if none
|
||||
*/
|
||||
protected HttpUrl getCurrentUserPrincipal(HttpUrl url) {
|
||||
try {
|
||||
DavResource dav = new DavResource(httpClient, url);
|
||||
dav.propfind(0, CurrentUserPrincipal.NAME);
|
||||
CurrentUserPrincipal currentUserPrincipal = (CurrentUserPrincipal) dav.properties.get(CurrentUserPrincipal.NAME);
|
||||
if (currentUserPrincipal != null && currentUserPrincipal.href != null) {
|
||||
HttpUrl principal = url.resolve(currentUserPrincipal.href);
|
||||
if (principal != null) {
|
||||
Constants.log.info("Found current-user-principal: " + principal);
|
||||
return principal;
|
||||
}
|
||||
protected HttpUrl getCurrentUserPrincipal(HttpUrl url) throws IOException, HttpException, DavException {
|
||||
DavResource dav = new DavResource(log, httpClient, url);
|
||||
dav.propfind(0, CurrentUserPrincipal.NAME);
|
||||
CurrentUserPrincipal currentUserPrincipal = (CurrentUserPrincipal) dav.properties.get(CurrentUserPrincipal.NAME);
|
||||
if (currentUserPrincipal != null && currentUserPrincipal.href != null) {
|
||||
HttpUrl principal = url.resolve(currentUserPrincipal.href);
|
||||
if (principal != null) {
|
||||
Constants.log.info("Found current-user-principal: " + principal);
|
||||
return principal;
|
||||
}
|
||||
} catch(IOException|HttpException|DavException e) {
|
||||
Constants.log.debug("PROPFIND for current-user-principal on " + url + " failed", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -47,6 +47,8 @@ public class AccountSettings {
|
||||
|
||||
KEY_USERNAME = "user_name",
|
||||
KEY_AUTH_PREEMPTIVE = "auth_preemptive",
|
||||
KEY_LOG_TO_EXTERNAL_FILE = "log_external_file",
|
||||
KEY_LOG_VERBOSE = "log_verbose",
|
||||
KEY_LAST_ANDROID_VERSION = "last_android_version";
|
||||
|
||||
public final static long SYNC_INTERVAL_MANUALLY = -1;
|
||||
@ -84,9 +86,8 @@ public class AccountSettings {
|
||||
showNotification(Constants.NOTIFICATION_ANDROID_VERSION_UPDATED,
|
||||
context.getString(R.string.settings_android_update_title),
|
||||
context.getString(R.string.settings_android_update_description));
|
||||
|
||||
accountManager.setUserData(account, KEY_LAST_ANDROID_VERSION, String.valueOf(Build.VERSION.SDK_INT));
|
||||
}
|
||||
accountManager.setUserData(account, KEY_LAST_ANDROID_VERSION, String.valueOf(Build.VERSION.SDK_INT));
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,18 +120,23 @@ public class AccountSettings {
|
||||
|
||||
// authentication settings
|
||||
|
||||
public String getUserName() {
|
||||
return accountManager.getUserData(account, KEY_USERNAME);
|
||||
}
|
||||
public void setUserName(String userName) { accountManager.setUserData(account, KEY_USERNAME, userName); }
|
||||
public String username() { return accountManager.getUserData(account, KEY_USERNAME); }
|
||||
public void username(String userName) { accountManager.setUserData(account, KEY_USERNAME, userName); }
|
||||
|
||||
public String getPassword() {
|
||||
return accountManager.getPassword(account);
|
||||
}
|
||||
public void setPassword(String password) { accountManager.setPassword(account, password); }
|
||||
public String password() { return accountManager.getPassword(account); }
|
||||
public void password(String password) { accountManager.setPassword(account, password); }
|
||||
|
||||
public boolean getPreemptiveAuth() { return Boolean.parseBoolean(accountManager.getUserData(account, KEY_AUTH_PREEMPTIVE)); }
|
||||
public void setPreemptiveAuth(boolean preemptive) { accountManager.setUserData(account, KEY_AUTH_PREEMPTIVE, Boolean.toString(preemptive)); }
|
||||
public boolean preemptiveAuth() { return Boolean.parseBoolean(accountManager.getUserData(account, KEY_AUTH_PREEMPTIVE)); }
|
||||
public void preemptiveAuth(boolean preemptive) { accountManager.setUserData(account, KEY_AUTH_PREEMPTIVE, Boolean.toString(preemptive)); }
|
||||
|
||||
|
||||
// logging settings
|
||||
|
||||
public boolean logToExternalFile() { return Boolean.parseBoolean(accountManager.getUserData(account, KEY_LOG_TO_EXTERNAL_FILE)); }
|
||||
public void logToExternalFile(boolean newValue) { accountManager.setUserData(account, KEY_LOG_TO_EXTERNAL_FILE, Boolean.toString(newValue)); }
|
||||
|
||||
public boolean logVerbose() { return Boolean.parseBoolean(accountManager.getUserData(account, KEY_LOG_VERBOSE)); }
|
||||
public void logVerbose(boolean newValue) { accountManager.setUserData(account, KEY_LOG_VERBOSE, Boolean.toString(newValue)); }
|
||||
|
||||
|
||||
// sync. settings
|
||||
@ -229,7 +235,7 @@ public class AccountSettings {
|
||||
}
|
||||
|
||||
private void update_1_2() throws ContactsStorageException {
|
||||
/* - KEY_ADDRESSBOOK_URL ("addressbook_url"),,
|
||||
/* - KEY_ADDRESSBOOK_URL ("addressbook_url"),
|
||||
- KEY_ADDRESSBOOK_CTAG ("addressbook_ctag"),
|
||||
- KEY_ADDRESSBOOK_VCARD_VERSION ("addressbook_vcard_version") are not used anymore (now stored in ContactsContract.SyncState)
|
||||
- KEY_LAST_ANDROID_VERSION ("last_android_version") has been added
|
||||
@ -237,22 +243,25 @@ public class AccountSettings {
|
||||
|
||||
// move previous address book info to ContactsContract.SyncState
|
||||
@Cleanup("release") ContentProviderClient provider = context.getContentResolver().acquireContentProviderClient(ContactsContract.AUTHORITY);
|
||||
if (provider != null) {
|
||||
LocalAddressBook addr = new LocalAddressBook(account, provider);
|
||||
if (provider == null)
|
||||
throw new ContactsStorageException("Couldn't access Contacts provider");
|
||||
|
||||
String url = accountManager.getUserData(account, "addressbook_url");
|
||||
if (!TextUtils.isEmpty(url))
|
||||
addr.setURL(url);
|
||||
accountManager.setUserData(account, "addressbook_url", null);
|
||||
LocalAddressBook addr = new LocalAddressBook(account, provider);
|
||||
|
||||
String cTag = accountManager.getUserData(account, "addressbook_ctag");
|
||||
if (!TextUtils.isEmpty(cTag))
|
||||
addr.setCTag(cTag);
|
||||
accountManager.setUserData(account, "addressbook_ctag", null);
|
||||
}
|
||||
// until now, ContactsContract.Settings.UNGROUPED_VISIBLE was not set explicitly
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(ContactsContract.Settings.UNGROUPED_VISIBLE, 1);
|
||||
addr.updateSettings(values);
|
||||
|
||||
// store current Android version
|
||||
accountManager.setUserData(account, KEY_LAST_ANDROID_VERSION, String.valueOf(Build.VERSION.SDK_INT));
|
||||
String url = accountManager.getUserData(account, "addressbook_url");
|
||||
if (!TextUtils.isEmpty(url))
|
||||
addr.setURL(url);
|
||||
accountManager.setUserData(account, "addressbook_url", null);
|
||||
|
||||
String cTag = accountManager.getUserData(account, "addressbook_ctag");
|
||||
if (!TextUtils.isEmpty(cTag))
|
||||
addr.setCTag(cTag);
|
||||
accountManager.setUserData(account, "addressbook_ctag", null);
|
||||
|
||||
accountManager.setUserData(account, KEY_SETTINGS_VERSION, "2");
|
||||
}
|
||||
|
@ -74,7 +74,7 @@ public class CalendarSyncManager extends SyncManager {
|
||||
Thread.currentThread().setContextClassLoader(context.getClassLoader()); // required for ical4j
|
||||
|
||||
collectionURL = HttpUrl.parse(localCalendar().getName());
|
||||
davCollection = new DavCalendar(httpClient, collectionURL);
|
||||
davCollection = new DavCalendar(log, httpClient, collectionURL);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -90,7 +90,7 @@ public class CalendarSyncManager extends SyncManager {
|
||||
int color = (pColor != null && pColor.color != null) ? pColor.color : LocalCalendar.defaultColor;
|
||||
|
||||
ContentValues values = new ContentValues(2);
|
||||
Constants.log.info("Setting new calendar name \"" + displayName + "\" and color 0x" + Integer.toHexString(color));
|
||||
log.info("Setting new calendar name \"" + displayName + "\" and color 0x" + Integer.toHexString(color));
|
||||
values.put(Calendars.CALENDAR_DISPLAY_NAME, displayName);
|
||||
values.put(Calendars.CALENDAR_COLOR, color);
|
||||
localCalendar().update(values);
|
||||
@ -119,20 +119,20 @@ public class CalendarSyncManager extends SyncManager {
|
||||
remoteResources = new HashMap<>(davCollection.members.size());
|
||||
for (DavResource vCard : davCollection.members) {
|
||||
String fileName = vCard.fileName();
|
||||
Constants.log.debug("Found remote VEVENT: " + fileName);
|
||||
log.debug("Found remote VEVENT: " + fileName);
|
||||
remoteResources.put(fileName, vCard);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void downloadRemote() throws IOException, HttpException, DavException, CalendarStorageException {
|
||||
Constants.log.info("Downloading " + toDownload.size() + " events (" + MAX_MULTIGET + " at once)");
|
||||
log.info("Downloading " + toDownload.size() + " events (" + MAX_MULTIGET + " at once)");
|
||||
|
||||
// download new/updated iCalendars from server
|
||||
for (DavResource[] bunch : ArrayUtils.partition(toDownload.toArray(new DavResource[toDownload.size()]), MAX_MULTIGET)) {
|
||||
if (Thread.interrupted())
|
||||
return;
|
||||
Constants.log.info("Downloading " + StringUtils.join(bunch, ", "));
|
||||
log.info("Downloading " + StringUtils.join(bunch, ", "));
|
||||
|
||||
if (bunch.length == 1) {
|
||||
// only one contact, use GET
|
||||
@ -190,7 +190,7 @@ public class CalendarSyncManager extends SyncManager {
|
||||
try {
|
||||
events = Event.fromStream(stream, charset);
|
||||
} catch (InvalidCalendarException e) {
|
||||
Constants.log.error("Received invalid iCalendar, ignoring");
|
||||
log.error("Received invalid iCalendar, ignoring");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -200,18 +200,18 @@ public class CalendarSyncManager extends SyncManager {
|
||||
// delete local event, if it exists
|
||||
LocalEvent localEvent = (LocalEvent)localResources.get(fileName);
|
||||
if (localEvent != null) {
|
||||
Constants.log.info("Updating " + fileName + " in local calendar");
|
||||
log.info("Updating " + fileName + " in local calendar");
|
||||
localEvent.setETag(eTag);
|
||||
localEvent.update(newData);
|
||||
syncResult.stats.numUpdates++;
|
||||
} else {
|
||||
Constants.log.info("Adding " + fileName + " to local calendar");
|
||||
log.info("Adding " + fileName + " to local calendar");
|
||||
localEvent = new LocalEvent(localCalendar(), newData, fileName, eTag);
|
||||
localEvent.add();
|
||||
syncResult.stats.numInserts++;
|
||||
}
|
||||
} else
|
||||
Constants.log.error("Received VCALENDAR with not exactly one VEVENT with UID, but without RECURRENCE-ID; ignoring " + fileName);
|
||||
log.error("Received VCALENDAR with not exactly one VEVENT with UID, but without RECURRENCE-ID; ignoring " + fileName);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ public class ContactsSyncManager extends SyncManager {
|
||||
if (url == null)
|
||||
throw new ContactsStorageException("Couldn't get address book URL");
|
||||
collectionURL = HttpUrl.parse(url);
|
||||
davCollection = new DavAddressBook(httpClient, collectionURL);
|
||||
davCollection = new DavAddressBook(log, httpClient, collectionURL);
|
||||
|
||||
processChangedGroups();
|
||||
}
|
||||
@ -98,7 +98,7 @@ public class ContactsSyncManager extends SyncManager {
|
||||
for (MediaType type : supportedAddressData.types)
|
||||
if ("text/vcard; version=4.0".equalsIgnoreCase(type.toString()))
|
||||
hasVCard4 = true;
|
||||
Constants.log.info("Server advertises VCard/4 support: " + hasVCard4);
|
||||
log.info("Server advertises VCard/4 support: " + hasVCard4);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -118,7 +118,7 @@ public class ContactsSyncManager extends SyncManager {
|
||||
davAddressBook().addressbookQuery();
|
||||
} catch(HttpException e) {
|
||||
if (e.status/100 == 4) {
|
||||
Constants.log.warn("Server error on REPORT addressbook query, falling back to PROPFIND", e);
|
||||
log.warn("Server error on REPORT addressbook query, falling back to PROPFIND", e);
|
||||
davAddressBook().propfind(1, GetETag.NAME);
|
||||
}
|
||||
}
|
||||
@ -126,14 +126,14 @@ public class ContactsSyncManager extends SyncManager {
|
||||
remoteResources = new HashMap<>(davCollection.members.size());
|
||||
for (DavResource vCard : davCollection.members) {
|
||||
String fileName = vCard.fileName();
|
||||
Constants.log.debug("Found remote VCard: " + fileName);
|
||||
log.debug("Found remote VCard: " + fileName);
|
||||
remoteResources.put(fileName, vCard);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void downloadRemote() throws IOException, HttpException, DavException, ContactsStorageException {
|
||||
Constants.log.info("Downloading " + toDownload.size() + " contacts (" + MAX_MULTIGET + " at once)");
|
||||
log.info("Downloading " + toDownload.size() + " contacts (" + MAX_MULTIGET + " at once)");
|
||||
|
||||
// prepare downloader which may be used to download external resource like contact photos
|
||||
Contact.Downloader downloader = new ResourceDownloader(httpClient, collectionURL);
|
||||
@ -143,7 +143,7 @@ public class ContactsSyncManager extends SyncManager {
|
||||
if (Thread.interrupted())
|
||||
return;
|
||||
|
||||
Constants.log.info("Downloading " + StringUtils.join(bunch, ", "));
|
||||
log.info("Downloading " + StringUtils.join(bunch, ", "));
|
||||
|
||||
if (bunch.length == 1) {
|
||||
// only one contact, use GET
|
||||
@ -202,7 +202,7 @@ public class ContactsSyncManager extends SyncManager {
|
||||
// groups with DELETED=1: remove group finally
|
||||
for (LocalGroup group : addressBook.getDeletedGroups()) {
|
||||
long groupId = group.getId();
|
||||
Constants.log.debug("Finally removing group #" + groupId);
|
||||
log.debug("Finally removing group #" + groupId);
|
||||
// remove group memberships, but not as sync adapter (should marks contacts as DIRTY)
|
||||
// NOTE: doesn't work that way because Contact Provider removes the group memberships even for DELETED groups
|
||||
// addressBook.removeGroupMemberships(groupId, false);
|
||||
@ -212,7 +212,7 @@ public class ContactsSyncManager extends SyncManager {
|
||||
// groups with DIRTY=1: mark all memberships as dirty, then clean DIRTY flag of group
|
||||
for (LocalGroup group : addressBook.getDirtyGroups()) {
|
||||
long groupId = group.getId();
|
||||
Constants.log.debug("Marking members of modified group #" + groupId + " as dirty");
|
||||
log.debug("Marking members of modified group #" + groupId + " as dirty");
|
||||
addressBook.markMembersDirty(groupId);
|
||||
group.clearDirty();
|
||||
}
|
||||
@ -226,32 +226,32 @@ public class ContactsSyncManager extends SyncManager {
|
||||
// update local contact, if it exists
|
||||
LocalContact localContact = (LocalContact)localResources.get(fileName);
|
||||
if (localContact != null) {
|
||||
Constants.log.info("Updating " + fileName + " in local address book");
|
||||
log.info("Updating " + fileName + " in local address book");
|
||||
localContact.eTag = eTag;
|
||||
localContact.update(newData);
|
||||
syncResult.stats.numUpdates++;
|
||||
} else {
|
||||
Constants.log.info("Adding " + fileName + " to local address book");
|
||||
log.info("Adding " + fileName + " to local address book");
|
||||
localContact = new LocalContact(localAddressBook(), newData, fileName, eTag);
|
||||
localContact.add();
|
||||
syncResult.stats.numInserts++;
|
||||
}
|
||||
} else
|
||||
Constants.log.error("Received VCard with not exactly one VCARD, ignoring " + fileName);
|
||||
log.error("Received VCard with not exactly one VCARD, ignoring " + fileName);
|
||||
}
|
||||
|
||||
|
||||
// downloader helper class
|
||||
|
||||
@RequiredArgsConstructor
|
||||
private static class ResourceDownloader implements Contact.Downloader {
|
||||
private class ResourceDownloader implements Contact.Downloader {
|
||||
final HttpClient httpClient;
|
||||
final HttpUrl baseUrl;
|
||||
|
||||
@Override
|
||||
public byte[] download(String url, String accepts) {
|
||||
HttpUrl httpUrl = HttpUrl.parse(url);
|
||||
HttpClient resourceClient = new HttpClient(httpClient, httpUrl.host());
|
||||
HttpClient resourceClient = new HttpClient(log, httpClient, httpUrl.host());
|
||||
try {
|
||||
Response response = resourceClient.newCall(new Request.Builder()
|
||||
.get()
|
||||
@ -264,10 +264,10 @@ public class ContactsSyncManager extends SyncManager {
|
||||
if (response.isSuccessful() && stream != null) {
|
||||
return IOUtils.toByteArray(stream);
|
||||
} else
|
||||
Constants.log.error("Couldn't download external resource");
|
||||
log.error("Couldn't download external resource");
|
||||
}
|
||||
} catch(IOException e) {
|
||||
Constants.log.error("Couldn't download external resource", e);
|
||||
log.error("Couldn't download external resource", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
* 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
|
||||
@ -22,6 +22,8 @@ import android.text.TextUtils;
|
||||
import com.squareup.okhttp.HttpUrl;
|
||||
import com.squareup.okhttp.RequestBody;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
@ -42,6 +44,7 @@ import at.bitfire.dav4android.property.GetETag;
|
||||
import at.bitfire.davdroid.Constants;
|
||||
import at.bitfire.davdroid.HttpClient;
|
||||
import at.bitfire.davdroid.R;
|
||||
import at.bitfire.davdroid.log.ExternalFileLogger;
|
||||
import at.bitfire.davdroid.resource.LocalCollection;
|
||||
import at.bitfire.davdroid.resource.LocalResource;
|
||||
import at.bitfire.davdroid.ui.DebugInfoActivity;
|
||||
@ -75,6 +78,8 @@ abstract public class SyncManager {
|
||||
protected final AccountSettings settings;
|
||||
protected LocalCollection localCollection;
|
||||
|
||||
protected Logger log;
|
||||
|
||||
protected final HttpClient httpClient;
|
||||
protected HttpUrl collectionURL;
|
||||
protected DavResource davCollection;
|
||||
@ -101,9 +106,19 @@ abstract public class SyncManager {
|
||||
this.authority = authority;
|
||||
this.syncResult = syncResult;
|
||||
|
||||
// get account settings and generate httpClient
|
||||
// get account settings and log to file (if requested)
|
||||
settings = new AccountSettings(context, account);
|
||||
httpClient = new HttpClient(context, settings.getUserName(), settings.getPassword(), settings.getPreemptiveAuth());
|
||||
try {
|
||||
if (settings.logToExternalFile())
|
||||
log = new ExternalFileLogger(context, "davdroid-SyncManager-" + account.name + "-" + authority + ".txt", settings.logVerbose());
|
||||
} catch(IOException e) {
|
||||
log.error("Couldn't log to external file", e);
|
||||
}
|
||||
if (log == null)
|
||||
log = Constants.log;
|
||||
|
||||
// create HttpClient with given logger
|
||||
httpClient = new HttpClient(log, context, settings.username(), settings.password(), settings.preemptiveAuth());
|
||||
|
||||
// dismiss previous error notifications
|
||||
notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
@ -115,60 +130,60 @@ abstract public class SyncManager {
|
||||
public void performSync() {
|
||||
int syncPhase = SYNC_PHASE_PREPARE;
|
||||
try {
|
||||
Constants.log.info("Preparing synchronization");
|
||||
log.info("Preparing synchronization");
|
||||
prepare();
|
||||
|
||||
if (Thread.interrupted())
|
||||
return;
|
||||
syncPhase = SYNC_PHASE_QUERY_CAPABILITIES;
|
||||
Constants.log.info("Querying capabilities");
|
||||
log.info("Querying capabilities");
|
||||
queryCapabilities();
|
||||
|
||||
syncPhase = SYNC_PHASE_PROCESS_LOCALLY_DELETED;
|
||||
Constants.log.info("Processing locally deleted entries");
|
||||
log.info("Processing locally deleted entries");
|
||||
processLocallyDeleted();
|
||||
|
||||
if (Thread.interrupted())
|
||||
return;
|
||||
syncPhase = SYNC_PHASE_PREPARE_DIRTY;
|
||||
Constants.log.info("Locally preparing dirty entries");
|
||||
log.info("Locally preparing dirty entries");
|
||||
prepareDirty();
|
||||
|
||||
syncPhase = SYNC_PHASE_UPLOAD_DIRTY;
|
||||
Constants.log.info("Uploading dirty entries");
|
||||
log.info("Uploading dirty entries");
|
||||
uploadDirty();
|
||||
|
||||
syncPhase = SYNC_PHASE_CHECK_SYNC_STATE;
|
||||
Constants.log.info("Checking sync state");
|
||||
log.info("Checking sync state");
|
||||
if (checkSyncState()) {
|
||||
syncPhase = SYNC_PHASE_LIST_LOCAL;
|
||||
Constants.log.info("Listing local entries");
|
||||
log.info("Listing local entries");
|
||||
listLocal();
|
||||
|
||||
if (Thread.interrupted())
|
||||
return;
|
||||
syncPhase = SYNC_PHASE_LIST_REMOTE;
|
||||
Constants.log.info("Listing remote entries");
|
||||
log.info("Listing remote entries");
|
||||
listRemote();
|
||||
|
||||
if (Thread.interrupted())
|
||||
return;
|
||||
syncPhase = SYNC_PHASE_COMPARE_LOCAL_REMOTE;
|
||||
Constants.log.info("Comparing local/remote entries");
|
||||
log.info("Comparing local/remote entries");
|
||||
compareLocalRemote();
|
||||
|
||||
syncPhase = SYNC_PHASE_DOWNLOAD_REMOTE;
|
||||
Constants.log.info("Downloading remote entries");
|
||||
log.info("Downloading remote entries");
|
||||
downloadRemote();
|
||||
|
||||
syncPhase = SYNC_PHASE_SAVE_SYNC_STATE;
|
||||
Constants.log.info("Saving sync state");
|
||||
log.info("Saving sync state");
|
||||
saveSyncState();
|
||||
} else
|
||||
Constants.log.info("Remote collection didn't change, skipping remote sync");
|
||||
log.info("Remote collection didn't change, skipping remote sync");
|
||||
|
||||
} catch (IOException|ServiceUnavailableException e) {
|
||||
Constants.log.error("I/O exception during sync, trying again later", e);
|
||||
log.error("I/O exception during sync, trying again later", e);
|
||||
syncResult.stats.numIoExceptions++;
|
||||
|
||||
if (e instanceof ServiceUnavailableException) {
|
||||
@ -183,19 +198,19 @@ abstract public class SyncManager {
|
||||
final int messageString;
|
||||
|
||||
if (e instanceof UnauthorizedException) {
|
||||
Constants.log.error("Not authorized anymore", e);
|
||||
log.error("Not authorized anymore", e);
|
||||
messageString = R.string.sync_error_unauthorized;
|
||||
syncResult.stats.numAuthExceptions++;
|
||||
} else if (e instanceof HttpException || e instanceof DavException) {
|
||||
Constants.log.error("HTTP/DAV Exception during sync", e);
|
||||
log.error("HTTP/DAV Exception during sync", e);
|
||||
messageString = R.string.sync_error_http_dav;
|
||||
syncResult.stats.numParseExceptions++;
|
||||
} else if (e instanceof CalendarStorageException || e instanceof ContactsStorageException) {
|
||||
Constants.log.error("Couldn't access local storage", e);
|
||||
log.error("Couldn't access local storage", e);
|
||||
messageString = R.string.sync_error_local_storage;
|
||||
syncResult.databaseError = true;
|
||||
} else {
|
||||
Constants.log.error("Unknown sync error", e);
|
||||
log.error("Unknown sync error", e);
|
||||
messageString = R.string.sync_error;
|
||||
syncResult.stats.numParseExceptions++;
|
||||
}
|
||||
@ -237,6 +252,13 @@ abstract public class SyncManager {
|
||||
notification = builder.getNotification();
|
||||
}
|
||||
notificationManager.notify(account.name, notificationId, notification);
|
||||
} finally {
|
||||
if (log instanceof ExternalFileLogger)
|
||||
try {
|
||||
((ExternalFileLogger)log).close();
|
||||
} catch (IOException e) {
|
||||
Constants.log.error("Couldn't close external log file", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,15 +281,15 @@ abstract public class SyncManager {
|
||||
|
||||
final String fileName = local.getFileName();
|
||||
if (!TextUtils.isEmpty(fileName)) {
|
||||
Constants.log.info(fileName + " has been deleted locally -> deleting from server");
|
||||
log.info(fileName + " has been deleted locally -> deleting from server");
|
||||
try {
|
||||
new DavResource(httpClient, collectionURL.newBuilder().addPathSegment(fileName).build())
|
||||
new DavResource(log, httpClient, collectionURL.newBuilder().addPathSegment(fileName).build())
|
||||
.delete(local.getETag());
|
||||
} catch (IOException|HttpException e) {
|
||||
Constants.log.warn("Couldn't delete " + fileName + " from server; ignoring (may be downloaded again)");
|
||||
log.warn("Couldn't delete " + fileName + " from server; ignoring (may be downloaded again)");
|
||||
}
|
||||
} else
|
||||
Constants.log.info("Removing local record #" + local.getId() + " which has been deleted locally and was never uploaded");
|
||||
log.info("Removing local record #" + local.getId() + " which has been deleted locally and was never uploaded");
|
||||
local.delete();
|
||||
syncResult.stats.numDeletes++;
|
||||
}
|
||||
@ -277,7 +299,7 @@ abstract public class SyncManager {
|
||||
// assign file names and UIDs to new contacts so that we can use the file name as an index
|
||||
for (LocalResource local : localCollection.getWithoutFileName()) {
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
Constants.log.info("Found local record #" + local.getId() + " without file name; assigning file name/UID based on " + uuid);
|
||||
log.info("Found local record #" + local.getId() + " without file name; assigning file name/UID based on " + uuid);
|
||||
local.updateFileNameAndUID(uuid);
|
||||
}
|
||||
}
|
||||
@ -296,7 +318,7 @@ abstract public class SyncManager {
|
||||
|
||||
final String fileName = local.getFileName();
|
||||
|
||||
DavResource remote = new DavResource(httpClient, collectionURL.newBuilder().addPathSegment(fileName).build());
|
||||
DavResource remote = new DavResource(log, httpClient, collectionURL.newBuilder().addPathSegment(fileName).build());
|
||||
|
||||
// generate entity to upload (VCard, iCal, whatever)
|
||||
RequestBody body = prepareUpload(local);
|
||||
@ -304,25 +326,25 @@ abstract public class SyncManager {
|
||||
try {
|
||||
|
||||
if (local.getETag() == null) {
|
||||
Constants.log.info("Uploading new record " + fileName);
|
||||
log.info("Uploading new record " + fileName);
|
||||
remote.put(body, null, true);
|
||||
} else {
|
||||
Constants.log.info("Uploading locally modified record " + fileName);
|
||||
log.info("Uploading locally modified record " + fileName);
|
||||
remote.put(body, local.getETag(), false);
|
||||
}
|
||||
|
||||
} catch (ConflictException|PreconditionFailedException e) {
|
||||
// we can't interact with the user to resolve the conflict, so we treat 409 like 412
|
||||
Constants.log.info("Resource has been modified on the server before upload, ignoring", e);
|
||||
log.info("Resource has been modified on the server before upload, ignoring", e);
|
||||
}
|
||||
|
||||
String eTag = null;
|
||||
GetETag newETag = (GetETag) remote.properties.get(GetETag.NAME);
|
||||
if (newETag != null) {
|
||||
eTag = newETag.eTag;
|
||||
Constants.log.debug("Received new ETag=" + eTag + " after uploading");
|
||||
log.debug("Received new ETag=" + eTag + " after uploading");
|
||||
} else
|
||||
Constants.log.debug("Didn't receive new ETag after uploading, setting to null");
|
||||
log.debug("Didn't receive new ETag after uploading, setting to null");
|
||||
|
||||
local.clearDirty(eTag);
|
||||
}
|
||||
@ -343,12 +365,12 @@ abstract public class SyncManager {
|
||||
|
||||
String localCTag = null;
|
||||
if (extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL))
|
||||
Constants.log.info("Manual sync, ignoring CTag");
|
||||
log.info("Manual sync, ignoring CTag");
|
||||
else
|
||||
localCTag = localCollection.getCTag();
|
||||
|
||||
if (remoteCTag != null && remoteCTag.equals(localCTag)) {
|
||||
Constants.log.info("Remote collection didn't change (CTag=" + remoteCTag + "), no need to query children");
|
||||
log.info("Remote collection didn't change (CTag=" + remoteCTag + "), no need to query children");
|
||||
return false;
|
||||
} else
|
||||
return true;
|
||||
@ -362,7 +384,7 @@ abstract public class SyncManager {
|
||||
LocalResource[] localList = localCollection.getAll();
|
||||
localResources = new HashMap<>(localList.length);
|
||||
for (LocalResource resource : localList) {
|
||||
Constants.log.debug("Found local resource: " + resource.getFileName());
|
||||
log.debug("Found local resource: " + resource.getFileName());
|
||||
localResources.put(resource.getFileName(), resource);
|
||||
}
|
||||
}
|
||||
@ -389,7 +411,7 @@ abstract public class SyncManager {
|
||||
for (String localName : localResources.keySet()) {
|
||||
DavResource remote = remoteResources.get(localName);
|
||||
if (remote == null) {
|
||||
Constants.log.info(localName + " is not on server anymore, deleting");
|
||||
log.info(localName + " is not on server anymore, deleting");
|
||||
localResources.get(localName).delete();
|
||||
syncResult.stats.numDeletes++;
|
||||
} else {
|
||||
@ -402,7 +424,7 @@ abstract public class SyncManager {
|
||||
if (remoteETag.equals(localETag))
|
||||
syncResult.stats.numSkippedEntries++;
|
||||
else {
|
||||
Constants.log.info(localName + " has been changed on server (current ETag=" + remoteETag + ", last known ETag=" + localETag + ")");
|
||||
log.info(localName + " has been changed on server (current ETag=" + remoteETag + ", last known ETag=" + localETag + ")");
|
||||
toDownload.add(remote);
|
||||
}
|
||||
|
||||
@ -413,7 +435,7 @@ abstract public class SyncManager {
|
||||
|
||||
// add all unseen (= remotely added) remote contacts
|
||||
if (!remoteResources.isEmpty()) {
|
||||
Constants.log.info("New VCards have been found on the server: " + TextUtils.join(", ", remoteResources.keySet()));
|
||||
log.info("New VCards have been found on the server: " + TextUtils.join(", ", remoteResources.keySet()));
|
||||
toDownload.addAll(remoteResources.values());
|
||||
}
|
||||
}
|
||||
@ -428,7 +450,7 @@ abstract public class SyncManager {
|
||||
/* Save sync state (CTag). It doesn't matter if it has changed during the sync process
|
||||
(for instance, because another client has uploaded changes), because this will simply
|
||||
cause all remote entries to be listed at the next sync. */
|
||||
Constants.log.info("Saving CTag=" + remoteCTag);
|
||||
log.info("Saving CTag=" + remoteCTag);
|
||||
localCollection.setCTag(remoteCTag);
|
||||
}
|
||||
|
||||
|
@ -79,7 +79,7 @@ public class TasksSyncManager extends SyncManager {
|
||||
Thread.currentThread().setContextClassLoader(context.getClassLoader()); // required for ical4j
|
||||
|
||||
collectionURL = HttpUrl.parse(localTaskList().getSyncId());
|
||||
davCollection = new DavCalendar(httpClient, collectionURL);
|
||||
davCollection = new DavCalendar(log, httpClient, collectionURL);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -95,7 +95,7 @@ public class TasksSyncManager extends SyncManager {
|
||||
int color = (pColor != null && pColor.color != null) ? pColor.color : LocalCalendar.defaultColor;
|
||||
|
||||
ContentValues values = new ContentValues(2);
|
||||
Constants.log.info("Setting new task list name \"" + displayName + "\" and color 0x" + Integer.toHexString(color));
|
||||
log.info("Setting new task list name \"" + displayName + "\" and color 0x" + Integer.toHexString(color));
|
||||
values.put(TaskLists.LIST_NAME, displayName);
|
||||
values.put(TaskLists.LIST_COLOR, color);
|
||||
localTaskList().update(values);
|
||||
@ -117,21 +117,21 @@ public class TasksSyncManager extends SyncManager {
|
||||
remoteResources = new HashMap<>(davCollection.members.size());
|
||||
for (DavResource vCard : davCollection.members) {
|
||||
String fileName = vCard.fileName();
|
||||
Constants.log.debug("Found remote VTODO: " + fileName);
|
||||
log.debug("Found remote VTODO: " + fileName);
|
||||
remoteResources.put(fileName, vCard);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void downloadRemote() throws IOException, HttpException, DavException, CalendarStorageException {
|
||||
Constants.log.info("Downloading " + toDownload.size() + " tasks (" + MAX_MULTIGET + " at once)");
|
||||
log.info("Downloading " + toDownload.size() + " tasks (" + MAX_MULTIGET + " at once)");
|
||||
|
||||
// download new/updated iCalendars from server
|
||||
for (DavResource[] bunch : ArrayUtils.partition(toDownload.toArray(new DavResource[toDownload.size()]), MAX_MULTIGET)) {
|
||||
if (Thread.interrupted())
|
||||
return;
|
||||
|
||||
Constants.log.info("Downloading " + StringUtils.join(bunch, ", "));
|
||||
log.info("Downloading " + StringUtils.join(bunch, ", "));
|
||||
|
||||
if (bunch.length == 1) {
|
||||
// only one contact, use GET
|
||||
@ -189,7 +189,7 @@ public class TasksSyncManager extends SyncManager {
|
||||
try {
|
||||
tasks = Task.fromStream(stream, charset);
|
||||
} catch (InvalidCalendarException e) {
|
||||
Constants.log.error("Received invalid iCalendar, ignoring", e);
|
||||
log.error("Received invalid iCalendar, ignoring", e);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -199,18 +199,18 @@ public class TasksSyncManager extends SyncManager {
|
||||
// update local task, if it exists
|
||||
LocalTask localTask = (LocalTask)localResources.get(fileName);
|
||||
if (localTask != null) {
|
||||
Constants.log.info("Updating " + fileName + " in local tasklist");
|
||||
log.info("Updating " + fileName + " in local tasklist");
|
||||
localTask.setETag(eTag);
|
||||
localTask.update(newData);
|
||||
syncResult.stats.numUpdates++;
|
||||
} else {
|
||||
Constants.log.info("Adding " + fileName + " to local task list");
|
||||
log.info("Adding " + fileName + " to local task list");
|
||||
localTask = new LocalTask(localTaskList(), newData, fileName, eTag);
|
||||
localTask.add();
|
||||
syncResult.stats.numInserts++;
|
||||
}
|
||||
} else
|
||||
Constants.log.error("Received VCALENDAR with not exactly one VTODO; ignoring " + fileName);
|
||||
log.error("Received VCALENDAR with not exactly one VTODO; ignoring " + fileName);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ public class AccountActivity extends Activity {
|
||||
getActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
final FragmentManager fm = getFragmentManager();
|
||||
|
||||
AccountFragment fragment = (AccountFragment)fm.findFragmentById(R.id.account_fragment);
|
||||
if (fragment == null) {
|
||||
fragment = new AccountFragment();
|
||||
@ -36,8 +35,9 @@ public class AccountActivity extends Activity {
|
||||
fragment.setArguments(args);
|
||||
|
||||
getFragmentManager().beginTransaction()
|
||||
.add(R.id.account_fragment, fragment)
|
||||
.add(R.id.account_fragment, fragment, SettingsActivity.TAG_ACCOUNT_SETTINGS)
|
||||
.commit();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -9,8 +9,10 @@
|
||||
package at.bitfire.davdroid.ui.settings;
|
||||
|
||||
import android.accounts.Account;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.DialogFragment;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.preference.CheckBoxPreference;
|
||||
import android.preference.EditTextPreference;
|
||||
import android.preference.ListPreference;
|
||||
import android.preference.Preference;
|
||||
@ -22,7 +24,6 @@ import android.provider.ContactsContract;
|
||||
import at.bitfire.davdroid.R;
|
||||
import at.bitfire.davdroid.syncadapter.AccountSettings;
|
||||
import at.bitfire.ical4android.TaskProvider;
|
||||
import ezvcard.VCardVersion;
|
||||
|
||||
public class AccountFragment extends PreferenceFragment {
|
||||
final static String ARG_ACCOUNT = "account";
|
||||
@ -36,44 +37,41 @@ public class AccountFragment extends PreferenceFragment {
|
||||
addPreferencesFromResource(R.xml.settings_account_prefs);
|
||||
|
||||
account = getArguments().getParcelable(ARG_ACCOUNT);
|
||||
readFromAccount();
|
||||
refresh();
|
||||
}
|
||||
|
||||
public void readFromAccount() {
|
||||
public void refresh() {
|
||||
final AccountSettings settings = new AccountSettings(getActivity(), account);
|
||||
|
||||
// category: authentication
|
||||
final EditTextPreference prefUserName = (EditTextPreference)findPreference("username");
|
||||
prefUserName.setSummary(settings.getUserName());
|
||||
prefUserName.setText(settings.getUserName());
|
||||
prefUserName.setSummary(settings.username());
|
||||
prefUserName.setText(settings.username());
|
||||
prefUserName.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
settings.setUserName((String)newValue);
|
||||
readFromAccount();
|
||||
return true;
|
||||
settings.username((String) newValue);
|
||||
refresh(); return false;
|
||||
}
|
||||
});
|
||||
|
||||
final EditTextPreference prefPassword = (EditTextPreference)findPreference("password");
|
||||
prefPassword.setText(settings.getPassword());
|
||||
prefPassword.setText(settings.password());
|
||||
prefPassword.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
settings.setPassword((String)newValue);
|
||||
readFromAccount();
|
||||
return true;
|
||||
settings.password((String) newValue);
|
||||
refresh(); return false;
|
||||
}
|
||||
});
|
||||
|
||||
final SwitchPreference prefPreemptive = (SwitchPreference)findPreference("preemptive");
|
||||
prefPreemptive.setChecked(settings.getPreemptiveAuth());
|
||||
prefPreemptive.setChecked(settings.preemptiveAuth());
|
||||
prefPreemptive.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
settings.setPreemptiveAuth((Boolean)newValue);
|
||||
readFromAccount();
|
||||
return true;
|
||||
settings.preemptiveAuth((Boolean) newValue);
|
||||
refresh(); return false;
|
||||
}
|
||||
});
|
||||
|
||||
@ -90,8 +88,7 @@ public class AccountFragment extends PreferenceFragment {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
settings.setSyncInterval(ContactsContract.AUTHORITY, Long.parseLong((String) newValue));
|
||||
readFromAccount();
|
||||
return true;
|
||||
refresh(); return false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@ -111,8 +108,7 @@ public class AccountFragment extends PreferenceFragment {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
settings.setSyncInterval(CalendarContract.AUTHORITY, Long.parseLong((String) newValue));
|
||||
readFromAccount();
|
||||
return true;
|
||||
refresh(); return false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@ -132,8 +128,7 @@ public class AccountFragment extends PreferenceFragment {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
settings.setSyncInterval(TaskProvider.ProviderName.OpenTasks.authority, Long.parseLong((String) newValue));
|
||||
readFromAccount();
|
||||
return true;
|
||||
refresh(); return false;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@ -141,5 +136,80 @@ public class AccountFragment extends PreferenceFragment {
|
||||
prefSyncTasks.setSummary(R.string.settings_sync_summary_not_available);
|
||||
}
|
||||
|
||||
// category: debug info
|
||||
|
||||
final SwitchPreference prefLogExternalFile = (SwitchPreference)findPreference("log_external_file");
|
||||
prefLogExternalFile.setChecked(settings.logToExternalFile());
|
||||
prefLogExternalFile.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
Boolean external = (Boolean)newValue;
|
||||
if (external) {
|
||||
getFragmentManager().beginTransaction()
|
||||
.add(LogExternalFileDialogFragment.newInstance(account), null)
|
||||
.commit();
|
||||
return false;
|
||||
} else {
|
||||
settings.logToExternalFile(false);
|
||||
refresh(); return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
final SwitchPreference prefLogVerbose = (SwitchPreference)findPreference("log_verbose");
|
||||
prefLogVerbose.setChecked(settings.logVerbose());
|
||||
prefLogVerbose.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
settings.logVerbose((Boolean) newValue);
|
||||
refresh(); return false;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static class LogExternalFileDialogFragment extends DialogFragment {
|
||||
private static final String
|
||||
KEY_ACCOUNT = "account";
|
||||
|
||||
public static LogExternalFileDialogFragment newInstance(Account account) {
|
||||
Bundle args = new Bundle();
|
||||
args.putParcelable(KEY_ACCOUNT, account);
|
||||
LogExternalFileDialogFragment fragment = new LogExternalFileDialogFragment();
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog onCreateDialog(Bundle savedInstanceState) {
|
||||
final AccountSettings settings = new AccountSettings(getActivity(), (Account)getArguments().getParcelable(KEY_ACCOUNT));
|
||||
return new AlertDialog.Builder(getActivity())
|
||||
.setTitle(R.string.settings_security_warning)
|
||||
.setIcon(android.R.drawable.ic_dialog_alert)
|
||||
.setMessage(R.string.settings_log_to_external_file_confirmation)
|
||||
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
settings.logToExternalFile(false);
|
||||
refresh();
|
||||
}
|
||||
})
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
settings.logToExternalFile(true);
|
||||
refresh();
|
||||
}
|
||||
})
|
||||
.create();
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
AccountFragment fragment = (AccountFragment)getActivity().getFragmentManager().findFragmentByTag(SettingsActivity.TAG_ACCOUNT_SETTINGS);
|
||||
if (fragment != null)
|
||||
fragment.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import android.os.Bundle;
|
||||
import at.bitfire.davdroid.R;
|
||||
|
||||
public class SettingsActivity extends Activity {
|
||||
private final static String KEY_SELECTED_ACCOUNT = "selected_account";
|
||||
public final static String TAG_ACCOUNT_SETTINGS = "account_settings";
|
||||
|
||||
boolean tabletLayout;
|
||||
|
||||
@ -44,7 +44,7 @@ public class SettingsActivity extends Activity {
|
||||
|
||||
getFragmentManager().beginTransaction()
|
||||
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
|
||||
.replace(R.id.right_pane, fragment)
|
||||
.replace(R.id.right_pane, fragment, TAG_ACCOUNT_SETTINGS)
|
||||
.commit();
|
||||
} else { // phone layout
|
||||
Intent intent = new Intent(getApplicationContext(), AccountActivity.class);
|
||||
|
@ -110,7 +110,7 @@ public class QueryServerDialogFragment extends DialogFragment implements LoaderC
|
||||
);
|
||||
|
||||
try {
|
||||
DavResourceFinder finder = new DavResourceFinder(context, serverInfo);
|
||||
DavResourceFinder finder = new DavResourceFinder(null, context, serverInfo);
|
||||
finder.findResources();
|
||||
} catch (URISyntaxException e) {
|
||||
serverInfo.setErrorMessage(getContext().getString(R.string.exception_uri_syntax, e.getMessage()));
|
||||
|
@ -154,10 +154,15 @@
|
||||
<item>Every 4 hours</item>
|
||||
<item>Once a day</item>
|
||||
</string-array>
|
||||
<string name="settings_carddav">Address book</string>
|
||||
<string name="settings_carddav_vcard4_support">VCard 4.0 support</string>
|
||||
<string name="settings_carddav_vcard4_supported">Contacts are sent in VCard 4.0 format</string>
|
||||
<string name="settings_carddav_vcard4_not_supported">Contacts are sent in VCard 3.0 format</string>
|
||||
<string name="settings_debug">Debugging</string>
|
||||
<string name="settings_security_warning">Potential security risk!</string>
|
||||
<string name="settings_log_to_external_file">Log to external file</string>
|
||||
<string name="settings_log_to_external_file_confirmation">External log files will contain private data and be accessible by other apps. Turn off external logging and delete the log files after use.</string>
|
||||
<string name="settings_log_to_external_file_on">Logs are written to external files (if possible)</string>
|
||||
<string name="settings_log_to_external_file_off">Logs are written to ADB</string>
|
||||
<string name="settings_log_verbose">Verbose logging</string>
|
||||
<string name="settings_log_verbose_on">Log synchronization information and network traffic</string>
|
||||
<string name="settings_log_verbose_off">Log only synchronization information</string>
|
||||
|
||||
<string name="settings_android_update_title">Android version update</string>
|
||||
<string name="settings_android_update_description">Android version updates may have an impact on how DAVdroid works. If there are problems, please delete your DAVdroid accounts and add them again.</string>
|
||||
|
@ -59,10 +59,22 @@
|
||||
|
||||
</PreferenceCategory>
|
||||
|
||||
<!--
|
||||
<PreferenceCategory android:title="@string/settings_carddav">
|
||||
<PreferenceCategory android:title="@string/settings_debug">
|
||||
|
||||
<SwitchPreference
|
||||
android:key="log_external_file"
|
||||
android:persistent="false"
|
||||
android:title="@string/settings_log_to_external_file"
|
||||
android:summaryOn="@string/settings_log_to_external_file_on"
|
||||
android:summaryOff="@string/settings_log_to_external_file_off" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="log_verbose"
|
||||
android:persistent="false"
|
||||
android:title="@string/settings_log_verbose"
|
||||
android:summaryOn="@string/settings_log_verbose_on"
|
||||
android:summaryOff="@string/settings_log_verbose_off" />
|
||||
|
||||
</PreferenceCategory>
|
||||
-->
|
||||
|
||||
</PreferenceScreen>
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit e7218aeb8ad8a96620208140bc7b86f63bf05fad
|
||||
Subproject commit 47541c169b970a6991b0f2f6c589232009e258ad
|
Loading…
Reference in New Issue
Block a user