Use java.util.logging instead of sl4fj

pull/2/head
Ricki Hirner 8 years ago
parent 552f6b6936
commit a735564bc1

@ -74,5 +74,4 @@ dependencies {
androidTestCompile 'com.squareup.okhttp3:mockwebserver:3.1.2'
compile 'dnsjava:dnsjava:2.1.7'
compile 'org.apache.commons:commons-lang3:3.4'
compile 'org.slf4j:slf4j-android:1.7.14'
}

@ -32,6 +32,7 @@ import org.apache.commons.lang3.math.NumberUtils;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.logging.Level;
import at.bitfire.davdroid.resource.LocalAddressBook;
import at.bitfire.vcard4android.ContactsStorageException;
@ -63,9 +64,9 @@ public class AccountSettings {
int version = 0;
try {
version = Integer.parseInt(accountManager.getUserData(account, KEY_SETTINGS_VERSION));
} catch(NumberFormatException e) {
} catch(NumberFormatException ignored) {
}
Constants.log.info("AccountSettings version: v" + version + ", should be: " + version);
App.log.info("AccountSettings version: v" + version + ", should be: " + version);
if (version < CURRENT_VERSION) {
showNotification(Constants.NOTIFICATION_ACCOUNT_SETTINGS_UPDATED,
@ -89,6 +90,7 @@ public class AccountSettings {
@TargetApi(21)
protected void showNotification(int id, String title, String message) {
NotificationManager nm = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification.Builder n = new Notification.Builder(context);
if (Build.VERSION.SDK_INT >= 16) {
n.setPriority(Notification.PRIORITY_HIGH);
@ -160,7 +162,7 @@ public class AccountSettings {
private void updateTo(int toVersion) {
final int fromVersion = toVersion - 1;
Constants.log.info("Updating account settings from v" + fromVersion + " to " + toVersion);
App.log.info("Updating account settings from v" + fromVersion + " to " + toVersion);
try {
switch (toVersion) {
case 1:
@ -170,10 +172,10 @@ public class AccountSettings {
update_1_2();
break;
default:
Constants.log.error("Don't know how to update settings from v" + fromVersion + " to v" + toVersion);
App.log.severe("Don't know how to update settings from v" + fromVersion + " to v" + toVersion);
}
} catch(Exception e) {
Constants.log.error("Couldn't update account settings (DAVdroid will probably crash)!", e);
App.log.log(Level.SEVERE, "Couldn't update account settings (DAVdroid will probably crash)!", e);
}
}
@ -181,15 +183,15 @@ public class AccountSettings {
private void update_0_1() throws URISyntaxException {
String v0_principalURL = accountManager.getUserData(account, "principal_url"),
v0_addressBookPath = accountManager.getUserData(account, "addressbook_path");
Constants.log.debug("Old principal URL = " + v0_principalURL);
Constants.log.debug("Old address book path = " + v0_addressBookPath);
App.log.fine("Old principal URL = " + v0_principalURL);
App.log.fine("Old address book path = " + v0_addressBookPath);
URI principalURI = new URI(v0_principalURL);
// update address book
if (v0_addressBookPath != null) {
String addressBookURL = principalURI.resolve(v0_addressBookPath).toASCIIString();
Constants.log.debug("New address book URL = " + addressBookURL);
App.log.fine("New address book URL = " + addressBookURL);
accountManager.setUserData(account, "addressbook_url", addressBookURL);
}
@ -204,7 +206,7 @@ public class AccountSettings {
int id = cursor.getInt(0);
String v0_path = cursor.getString(1),
v1_url = principalURI.resolve(v0_path).toASCIIString();
Constants.log.debug("Updating calendar #" + id + " name: " + v0_path + " -> " + v1_url);
App.log.fine("Updating calendar #" + id + " name: " + v0_path + " -> " + v1_url);
Uri calendar = ContentUris.appendId(Calendars.CONTENT_URI.buildUpon()
.appendQueryParameter(Calendars.ACCOUNT_NAME, account.name)
.appendQueryParameter(Calendars.ACCOUNT_TYPE, account.type)
@ -212,7 +214,7 @@ public class AccountSettings {
ContentValues newValues = new ContentValues(1);
newValues.put(Calendars.NAME, v1_url);
if (resolver.update(calendar, newValues, null, null) != 1)
Constants.log.debug("Number of modified calendars != 1");
App.log.fine("Number of modified calendars != 1");
}
accountManager.setUserData(account, "principal_url", null);

@ -8,8 +8,6 @@
package at.bitfire.davdroid;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.OnAccountsUpdateListener;
import android.content.BroadcastReceiver;
import android.content.Context;

@ -10,20 +10,21 @@ package at.bitfire.davdroid;
import android.app.Application;
import android.content.SharedPreferences;
import android.util.Log;
import org.apache.commons.lang3.time.DateFormatUtils;
import java.io.File;
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import javax.net.ssl.HostnameVerifier;
import at.bitfire.dav4android.*;
import at.bitfire.davdroid.log.LogcatHandler;
import at.bitfire.davdroid.log.PlainTextFormatter;
import de.duenndns.ssl.MemorizingTrustManager;
import lombok.Getter;
import okhttp3.internal.tls.OkHostnameVerifier;
@ -31,8 +32,7 @@ import okhttp3.internal.tls.OkHostnameVerifier;
public class App extends Application implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final String
PREF_FILE = "global",
PREF_LOG_TO_FILE = "log_to_file",
PREF_VERBOSE_LOGGING = "verbose_logging";
PREF_LOG_TO_FILE = "log_to_file";
@Getter
private static SSLSocketFactoryCompat sslSocketFactoryCompat;
@ -78,8 +78,11 @@ public class App extends Application implements SharedPreferences.OnSharedPrefer
// don't use Android default logging, we have our own handlers
log.setUseParentHandlers(false);
boolean logToFile = preferences.getBoolean(PREF_LOG_TO_FILE, false),
logVerbose = logToFile || Log.isLoggable(log.getName(), Log.DEBUG);
// set logging level according to preferences
log.setLevel(preferences.getBoolean(PREF_VERBOSE_LOGGING, false) ? Level.ALL : Level.INFO);
log.setLevel(logVerbose ? Level.ALL : Level.INFO);
// remove all handlers
for (Handler handler : log.getHandlers())
@ -89,15 +92,15 @@ public class App extends Application implements SharedPreferences.OnSharedPrefer
log.addHandler(LogcatHandler.INSTANCE);
// log to external file according to preferences
if (preferences.getBoolean(PREF_LOG_TO_FILE, false)) {
if (logToFile) {
File dir = getExternalFilesDir(null);
if (dir != null)
try {
String pattern = new File(dir, "davdroid-%u.txt").toString();
String pattern = new File(dir, "davdroid%u-" + DateFormatUtils.format(System.currentTimeMillis(), "yyyyMMdd-HHmmss") + ".txt").toString();
log.info("Logging to external file: " + pattern);
FileHandler fileHandler = new FileHandler(pattern);
fileHandler.setFormatter(new SimpleFormatter());
fileHandler.setFormatter(PlainTextFormatter.DEFAULT);
log.addHandler(fileHandler);
} catch (IOException e) {
log.log(Level.SEVERE, "Can't create external log file", e);

@ -9,16 +9,11 @@ package at.bitfire.davdroid;
import android.net.Uri;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Constants {
public static final String
ACCOUNT_TYPE = "bitfire.at.davdroid";
public static final Logger log = LoggerFactory.getLogger("davdroid");
// notification IDs
public final static int
NOTIFICATION_ANDROID_VERSION_UPDATED = 0,

@ -29,6 +29,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import at.bitfire.dav4android.DavResource;
import at.bitfire.dav4android.UrlUtils;
@ -39,7 +40,10 @@ import at.bitfire.dav4android.property.AddressbookHomeSet;
import at.bitfire.dav4android.property.CalendarHomeSet;
import at.bitfire.dav4android.property.GroupMembership;
import at.bitfire.davdroid.model.CollectionInfo;
import at.bitfire.davdroid.model.ServiceDB.*;
import at.bitfire.davdroid.model.ServiceDB.Collections;
import at.bitfire.davdroid.model.ServiceDB.HomeSets;
import at.bitfire.davdroid.model.ServiceDB.OpenHelper;
import at.bitfire.davdroid.model.ServiceDB.Services;
import lombok.Cleanup;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
@ -115,7 +119,7 @@ public class DavService extends Service {
*/
void cleanupAccounts() {
Constants.log.info("[DavService] Cleaning up orphaned accounts");
App.log.info("Cleaning up orphaned accounts");
final OpenHelper dbHelper = new OpenHelper(this);
try {
@ -151,7 +155,7 @@ public class DavService extends Service {
db = dbHelper.getWritableDatabase();
String serviceType = serviceType();
Constants.log.info("[DavService {}] Refreshing {} collections", service, serviceType);
App.log.info("Refreshing " + serviceType + " collections of service #" + service);
// create authenticating OkHttpClient (credentials taken from account settings)
OkHttpClient httpClient = httpClient();
@ -160,15 +164,15 @@ public class DavService extends Service {
Set<HttpUrl> homeSets = readHomeSets();
HttpUrl principal = readPrincipal();
if (principal != null) {
Constants.log.debug("[DavService {}] Querying principal for home sets", service);
DavResource dav = new DavResource(null, httpClient, principal);
App.log.fine("Querying principal for home sets");
DavResource dav = new DavResource(httpClient, principal);
queryHomeSets(serviceType, httpClient, principal, homeSets);
// refresh home sets: direct group memberships
GroupMembership groupMembership = (GroupMembership)dav.properties.get(GroupMembership.NAME);
if (groupMembership != null)
for (String href : groupMembership.hrefs) {
Constants.log.debug("[DavService {}] Querying member group {} for home sets", href);
App.log.fine("Querying member group " + href + " for home sets");
queryHomeSets(serviceType, httpClient, dav.location.resolve(href), homeSets);
}
}
@ -184,15 +188,15 @@ public class DavService extends Service {
for (Iterator<HttpUrl> iterator = homeSets.iterator(); iterator.hasNext();) {
HttpUrl homeSet = iterator.next();
Constants.log.debug("[DavService {}] Listing home set {}", service, homeSet);
App.log.fine("Listing home set " + homeSet);
DavResource dav = new DavResource(null, httpClient, homeSet);
DavResource dav = new DavResource(httpClient, homeSet);
try {
dav.propfind(1, CollectionInfo.DAV_PROPERTIES);
for (DavResource member : dav.members) {
CollectionInfo info = CollectionInfo.fromDavResource(member);
info.confirmed = true;
Constants.log.debug("[DavService {}] Found collection {}", service, info);
App.log.fine("Found collection" + info);
if ((serviceType.equals(Services.SERVICE_CARDDAV) && info.type == CollectionInfo.Type.ADDRESS_BOOK) ||
(serviceType.equals(Services.SERVICE_CALDAV) && info.type == CollectionInfo.Type.CALENDAR))
@ -212,7 +216,7 @@ public class DavService extends Service {
if (!info.confirmed)
try {
DavResource dav = new DavResource(null, httpClient, url);
DavResource dav = new DavResource(httpClient, url);
dav.propfind(0, CollectionInfo.DAV_PROPERTIES);
info = CollectionInfo.fromDavResource(dav);
info.confirmed = true;
@ -244,7 +248,7 @@ public class DavService extends Service {
}
} catch (IOException|HttpException|DavException e) {
Constants.log.error("Couldn't refresh collection list", e);
App.log.log(Level.SEVERE, "Couldn't refresh collection list", e);
} finally {
dbHelper.close();
@ -255,7 +259,7 @@ public class DavService extends Service {
}
private void queryHomeSets(String serviceType, OkHttpClient client, HttpUrl url, Set<HttpUrl> homeSets) throws IOException, HttpException, DavException {
DavResource dav = new DavResource(null, client, url);
DavResource dav = new DavResource(client, url);
if (Services.SERVICE_CARDDAV.equals(serviceType)) {
dav.propfind(0, AddressbookHomeSet.NAME, GroupMembership.NAME);
AddressbookHomeSet addressbookHomeSet = (AddressbookHomeSet)dav.properties.get(AddressbookHomeSet.NAME);
@ -332,7 +336,7 @@ public class DavService extends Service {
db.delete(Collections._TABLE, HomeSets.SERVICE_ID + "=?", new String[]{String.valueOf(service)});
for (CollectionInfo collection : collections) {
ContentValues values = collection.toDB();
Constants.log.debug("Saving collection: {}", values);
App.log.log(Level.FINE, "Saving collection", values);
values.put(Collections.SERVICE_ID, service);
db.insertWithOnConflict(Collections._TABLE, null, values, SQLiteDatabase.CONFLICT_REPLACE);
}

@ -24,7 +24,7 @@ public class DavUtils {
int color_alpha = m.group(2) != null ? (Integer.parseInt(m.group(2), 16) & 0xFF) : 0xFF;
color = (color_alpha << 24) | color_rgb;
} else
Constants.log.warn("Couldn't parse color " + davColor + ", using DAVdroid green");
App.log.warning("Couldn't parse color " + davColor + ", using DAVdroid green");
}
return color;
}

@ -11,10 +11,9 @@ package at.bitfire.davdroid;
import android.accounts.Account;
import android.content.Context;
import android.os.Build;
import android.support.annotation.NonNull;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
@ -23,11 +22,6 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import at.bitfire.dav4android.BasicDigestAuthenticator;
import at.bitfire.davdroid.log.ExternalFileLogger;
import android.support.annotation.NonNull;
import android.text.format.DateFormat;
import lombok.RequiredArgsConstructor;
import okhttp3.Credentials;
import okhttp3.Interceptor;
@ -49,7 +43,7 @@ public class HttpClient {
private HttpClient() {
}
public static OkHttpClient create(Context context, Account account) {
public static OkHttpClient create(Context context, Account account, @NonNull final Logger logger) {
OkHttpClient.Builder builder = client.newBuilder();
// use MemorizingTrustManager to manage self-signed certificates
@ -80,15 +74,26 @@ public class HttpClient {
builder.addNetworkInterceptor(new PreemptiveAuthenticationInterceptor(settings.username(), settings.password()));
else
builder.authenticator(new BasicDigestAuthenticator(null, settings.username(), settings.password()));
}
if (App.log.isLoggable(Level.FINEST))
addLogger(builder, App.log);
if (logger.isLoggable(Level.FINEST)) {
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
logger.finest(message);
}
});
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addInterceptor(loggingInterceptor);
}
return builder.build();
}
public static OkHttpClient create(Context context, Account account) {
return create(context, account, App.log);
}
private static OkHttpClient.Builder addAuthentication(@NonNull OkHttpClient.Builder builder, @NonNull String username, @NonNull String password, boolean preemptive) {
if (preemptive)
builder.addNetworkInterceptor(new PreemptiveAuthenticationInterceptor(username, password));
@ -109,22 +114,6 @@ public class HttpClient {
.build();
}
private static OkHttpClient.Builder addLogger(@NonNull OkHttpClient.Builder builder, @NonNull final Logger logger) {
// trace-level logging → add network traffic interceptor
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
logger.finest(message);
}
});
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
return builder.addInterceptor(loggingInterceptor);
}
public static OkHttpClient addLogger(@NonNull OkHttpClient client, @NonNull final Logger logger) {
return addLogger(client.newBuilder(), logger).build();
}
static class UserAgentInterceptor implements Interceptor {
@Override

@ -50,7 +50,7 @@ public class SSLSocketFactoryCompat extends SSLSocketFactory {
for (String protocol : socket.getSupportedProtocols())
if (!protocol.toUpperCase(Locale.US).contains("SSL"))
protocols.add(protocol);
Constants.log.info("Setting allowed TLS protocols: " + TextUtils.join(", ", protocols));
App.log.info("Setting allowed TLS protocols: " + TextUtils.join(", ", protocols));
SSLSocketFactoryCompat.protocols = protocols.toArray(new String[protocols.size()]);
/* set up reasonable cipher suites */
@ -75,8 +75,8 @@ public class SSLSocketFactoryCompat extends SSLSocketFactory {
"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA");
List<String> availableCiphers = Arrays.asList(socket.getSupportedCipherSuites());
Constants.log.debug("Available cipher suites: " + TextUtils.join(", ", availableCiphers));
Constants.log.debug("Cipher suites enabled by default: " + TextUtils.join(", ", socket.getEnabledCipherSuites()));
App.log.fine("Available cipher suites: " + TextUtils.join(", ", availableCiphers));
App.log.fine("Cipher suites enabled by default: " + TextUtils.join(", ", socket.getEnabledCipherSuites()));
// take all allowed ciphers that are available and put them into preferredCiphers
HashSet<String> preferredCiphers = new HashSet<>(allowedCiphers);
@ -91,12 +91,12 @@ public class SSLSocketFactoryCompat extends SSLSocketFactory {
HashSet<String> enabledCiphers = preferredCiphers;
enabledCiphers.addAll(new HashSet<>(Arrays.asList(socket.getEnabledCipherSuites())));
Constants.log.info("Enabling (only) those TLS ciphers: " + TextUtils.join(", ", enabledCiphers));
App.log.info("Enabling (only) those TLS ciphers: " + TextUtils.join(", ", enabledCiphers));
SSLSocketFactoryCompat.cipherSuites = enabledCiphers.toArray(new String[enabledCiphers.size()]);
}
}
} catch (IOException e) {
Constants.log.error("Couldn't determine default TLS settings");
App.log.severe("Couldn't determine default TLS settings");
}
}

@ -1,374 +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.log;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.Marker;
import java.io.PrintWriter;
import lombok.Getter;
/**
* 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] ";
@Getter
protected String name;
protected PrintWriter writer;
// CUSTOM LOGGING METHODS
protected void log(String prefix, String msg) {
writer.write(prefix + msg + "\n");
}
protected void log(String prefix, String format, Object arg) {
writer.write(prefix + format.replace("{}", arg.toString()) + "\n");
}
protected void log(String prefix, String format, Object arg1, Object arg2) {
writer.write(prefix + format.replaceFirst("\\{\\}", arg1.toString()).replaceFirst("\\{\\}", arg2.toString()) + "\n");
}
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");
}
protected void log(String prefix, String msg, Throwable t) {
writer.write(prefix + msg + " - EXCEPTION:\n");
t.printStackTrace(writer);
ExceptionUtils.printRootCauseStackTrace(t, writer);
}
// STANDARD CALLS
@Override
public boolean isTraceEnabled() {
return true;
}
@Override
public void trace(String msg) {
log(PREFIX_TRACE, msg);
}
@Override
public void trace(String format, Object arg) {
log(PREFIX_TRACE, format, arg);
}
@Override
public void trace(String format, Object arg1, Object arg2) {
log(PREFIX_TRACE, format, arg1, arg2);
}
@Override
public void trace(String format, Object... arguments) {
log(PREFIX_TRACE, format, arguments);
}
@Override
public void trace(String msg, Throwable t) {
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();
}
}

@ -1,42 +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.log;
import android.content.Context;
import org.apache.commons.lang3.StringUtils;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
public class ExternalFileLogger extends CustomLogger implements Closeable {
public ExternalFileLogger(Context context, String fileName) throws IOException {
File dir = getDirectory(context);
if (dir == null)
throw new IOException("External media not available for log creation");
name = StringUtils.remove(StringUtils.remove(fileName, File.pathSeparatorChar), File.separatorChar);
File log = new File(dir, name);
writer = new PrintWriter(log);
}
public static File getDirectory(Context context) {
return context.getExternalFilesDir(null);
}
@Override
public void close() throws IOException {
writer.close();
}
}

@ -10,7 +10,6 @@ package at.bitfire.davdroid.log;
import android.util.Log;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
@ -22,25 +21,25 @@ public class LogcatHandler extends Handler {
private LogcatHandler() {
super();
setFormatter(AdbFormatter.INSTANCE);
setFormatter(PlainTextFormatter.LOGCAT);
setLevel(Level.ALL);
}
@Override
public void publish(LogRecord record) {
String line = getFormatter().format(record);
int level = record.getLevel().intValue();
public void publish(LogRecord r) {
String line = getFormatter().format(r);
int level = r.getLevel().intValue();
if (level >= Level.SEVERE.intValue())
Log.e(TAG, line);
Log.e(r.getLoggerName(), line);
else if (level >= Level.WARNING.intValue())
Log.w(TAG, line);
Log.w(r.getLoggerName(), line);
else if (level >= Level.CONFIG.intValue())
Log.i(TAG, line);
Log.i(r.getLoggerName(), line);
else if (level >= Level.FINER.intValue())
Log.d(TAG, line);
Log.d(r.getLoggerName(), line);
else
Log.v(TAG, line);
Log.v(r.getLoggerName(), line);
}
@Override
@ -51,17 +50,4 @@ public class LogcatHandler extends Handler {
public void close() {
}
private static class AdbFormatter extends Formatter {
public static AdbFormatter INSTANCE = new AdbFormatter();
private AdbFormatter() {
}
@Override
public String format(LogRecord r) {
return String.format("[%s] %s", r.getSourceClassName(), r.getMessage());
}
}
}

@ -0,0 +1,61 @@
/*
* Copyright © 2013 2016 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.util.Log;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import java.util.logging.Formatter;
import java.util.logging.LogRecord;
public class PlainTextFormatter extends Formatter {
public final static PlainTextFormatter
LOGCAT = new PlainTextFormatter(true),
DEFAULT = new PlainTextFormatter(false);
private final boolean logcat;
private PlainTextFormatter(boolean onLogcat) {
this.logcat = onLogcat;
}
@Override
public String format(LogRecord r) {
StringBuilder builder = new StringBuilder();
if (!logcat) {
builder.append(DateFormatUtils.format(r.getMillis(), "yyyy-MM-dd HH:mm:ss"));
builder.append(String.format(" %d ", r.getThreadID()));
}
builder.append(String.format("[%s] %s", shortClassName(r.getSourceClassName()), r.getMessage()));
if (r.getThrown() != null) {
builder.append("\nEXCEPTION ");
builder.append(Log.getStackTraceString(r.getThrown()));
}
if (r.getParameters() != null)
for (Object param : r.getParameters())
builder.append("\nPARAMETER " + param);
if (!logcat)
builder.append("\n");
return builder.toString();
}
private String shortClassName(String className) {
String s = StringUtils.replace(className, "at.bitfire.davdroid.", "");
return StringUtils.replace(s, "at.bitfire.", "");
}
}

@ -0,0 +1,40 @@
/*
* Copyright © 2013 2016 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 java.util.logging.Handler;
import java.util.logging.LogRecord;
public class StringHandler extends Handler {
StringBuilder builder = new StringBuilder();
public StringHandler() {
super();
setFormatter(PlainTextFormatter.DEFAULT);
}
@Override
public void publish(LogRecord record) {
builder.append(getFormatter().format(record));
}
@Override
public void flush() {
}
@Override
public void close() {
}
@Override
public String toString() {
return builder.toString();
}
}

@ -1,30 +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.log;
import java.io.PrintWriter;
import java.io.StringWriter;
import lombok.Getter;
public class StringLogger extends CustomLogger {
@Getter protected final String name;
protected final StringWriter stringWriter = new StringWriter();
public StringLogger(String name) {
this.name = name;
writer = new PrintWriter(stringWriter);
}
public String toString() {
return stringWriter.toString();
}
}

@ -23,8 +23,8 @@ import at.bitfire.dav4android.property.DisplayName;
import at.bitfire.dav4android.property.ResourceType;
import at.bitfire.dav4android.property.SupportedAddressData;
import at.bitfire.dav4android.property.SupportedCalendarComponentSet;
import at.bitfire.davdroid.model.ServiceDB.Collections;
import lombok.ToString;
import at.bitfire.davdroid.model.ServiceDB.*;
@ToString
public class CollectionInfo implements Serializable {

@ -13,7 +13,7 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Build;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.App;
public class ServiceDB {
@ -81,7 +81,7 @@ public class ServiceDB {
@Override
public void onCreate(SQLiteDatabase db) {
Constants.log.info("Creating services database");
App.log.info("Creating services database");
db.execSQL("CREATE TABLE " + Services._TABLE + "(" +
Services.ID + " INTEGER PRIMARY KEY AUTOINCREMENT," +

@ -24,7 +24,7 @@ import android.provider.ContactsContract.RawContacts;
import java.util.LinkedList;
import java.util.List;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.App;
import at.bitfire.vcard4android.AndroidAddressBook;
import at.bitfire.vcard4android.AndroidContact;
import at.bitfire.vcard4android.AndroidGroupFactory;
@ -150,7 +150,7 @@ public class LocalAddressBook extends AndroidAddressBook implements LocalCollect
new String[] { GroupMembership.CONTENT_ITEM_TYPE, String.valueOf(groupId) }, null);
while (cursor != null && cursor.moveToNext()) {
long id = cursor.getLong(0);
Constants.log.debug("Marking raw contact #" + id + " as dirty");
App.log.fine("Marking raw contact #" + id + " as dirty");
provider.update(syncAdapterURI(ContentUris.withAppendedId(RawContacts.CONTENT_URI, id)), dirty, null, null);
}
} catch (RemoteException e) {

@ -32,7 +32,7 @@ import java.io.FileNotFoundException;
import java.util.LinkedList;
import java.util.List;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.model.CollectionInfo;
import at.bitfire.ical4android.AndroidCalendar;
import at.bitfire.ical4android.AndroidCalendarFactory;
@ -171,14 +171,14 @@ public class LocalCalendar extends AndroidCalendar implements LocalCollection {
@SuppressWarnings("Recycle")
public void processDirtyExceptions() throws CalendarStorageException {
// process deleted exceptions
Constants.log.info("Processing deleted exceptions");
App.log.info("Processing deleted exceptions");
try {
@Cleanup Cursor cursor = provider.query(
syncAdapterURI(Events.CONTENT_URI),
new String[] { Events._ID, Events.ORIGINAL_ID, LocalEvent.COLUMN_SEQUENCE },
Events.DELETED + "!=0 AND " + Events.ORIGINAL_ID + " IS NOT NULL", null, null);
while (cursor != null && cursor.moveToNext()) {
Constants.log.debug("Found deleted exception, removing; then re-schuling original event");
App.log.fine("Found deleted exception, removing; then re-schuling original event");
long id = cursor.getLong(0), // can't be null (by definition)
originalID = cursor.getLong(1); // can't be null (by query)
int sequence = cursor.isNull(2) ? 0 : cursor.getInt(2);
@ -207,14 +207,14 @@ public class LocalCalendar extends AndroidCalendar implements LocalCollection {
}
// process dirty exceptions
Constants.log.info("Processing dirty exceptions");
App.log.info("Processing dirty exceptions");
try {
@Cleanup Cursor cursor = provider.query(
syncAdapterURI(Events.CONTENT_URI),
new String[] { Events._ID, Events.ORIGINAL_ID, LocalEvent.COLUMN_SEQUENCE },
Events.DIRTY + "!=0 AND " + Events.ORIGINAL_ID + " IS NOT NULL", null, null);
while (cursor != null && cursor.moveToNext()) {
Constants.log.debug("Found dirty exception, increasing SEQUENCE to re-schedule");
App.log.fine("Found dirty exception, increasing SEQUENCE to re-schedule");
long id = cursor.getLong(0), // can't be null (by definition)
originalID = cursor.getLong(1); // can't be null (by query)
int sequence = cursor.isNull(2) ? 0 : cursor.getInt(2);

@ -15,9 +15,10 @@ import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import java.io.FileNotFoundException;
import java.util.logging.Level;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.BuildConfig;
import at.bitfire.davdroid.Constants;
import at.bitfire.vcard4android.AndroidAddressBook;
import at.bitfire.vcard4android.AndroidContact;
import at.bitfire.vcard4android.AndroidContactFactory;
@ -83,7 +84,7 @@ public class LocalContact extends AndroidContact implements LocalResource {
// add to CATEGORIES
contact.getCategories().add(groupInfo.displayName);
} catch (FileNotFoundException|ContactsStorageException e) {
Constants.log.warn("Couldn't find assigned group #" + groupId + ", ignoring membership", e);
App.log.log(Level.WARNING, "Couldn't find assigned group #" + groupId + ", ignoring membership", e);
}
}
}

@ -15,6 +15,7 @@ import android.os.Build;
import android.os.RemoteException;
import android.provider.CalendarContract;
import android.provider.CalendarContract.Events;
import android.support.annotation.NonNull;
import net.fortuna.ical4j.model.property.ProdId;
@ -25,7 +26,6 @@ import at.bitfire.ical4android.AndroidEventFactory;
import at.bitfire.ical4android.CalendarStorageException;
import at.bitfire.ical4android.Event;
import lombok.Getter;
import android.support.annotation.NonNull;
import lombok.Setter;
@TargetApi(17)

@ -12,6 +12,7 @@ import android.content.ContentProviderOperation;
import android.content.ContentValues;
import android.os.RemoteException;
import android.provider.CalendarContract.Events;
import android.support.annotation.NonNull;
import net.fortuna.ical4j.model.property.ProdId;
@ -27,7 +28,6 @@ import at.bitfire.ical4android.AndroidTaskList;
import at.bitfire.ical4android.CalendarStorageException;
import at.bitfire.ical4android.Task;
import lombok.Getter;
import android.support.annotation.NonNull;
import lombok.Setter;
public class LocalTask extends AndroidTask implements LocalResource {

@ -14,6 +14,7 @@ import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.RemoteException;
import android.support.annotation.NonNull;
import org.dmfs.provider.tasks.TaskContract.TaskLists;
import org.dmfs.provider.tasks.TaskContract.Tasks;
@ -26,7 +27,6 @@ import at.bitfire.ical4android.AndroidTaskListFactory;
import at.bitfire.ical4android.CalendarStorageException;
import at.bitfire.ical4android.TaskProvider;
import lombok.Cleanup;
import android.support.annotation.NonNull;
public class LocalTaskList extends AndroidTaskList implements LocalCollection {

@ -16,11 +16,6 @@ import android.os.Bundle;
import android.provider.CalendarContract.Calendars;
import android.text.TextUtils;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import org.apache.commons.codec.Charsets;
import org.apache.commons.lang3.StringUtils;
@ -31,6 +26,7 @@ import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import at.bitfire.dav4android.DavCalendar;
import at.bitfire.dav4android.DavResource;
@ -42,6 +38,7 @@ import at.bitfire.dav4android.property.DisplayName;
import at.bitfire.dav4android.property.GetCTag;
import at.bitfire.dav4android.property.GetContentType;
import at.bitfire.dav4android.property.GetETag;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.ArrayUtils;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R;
@ -53,6 +50,10 @@ import at.bitfire.ical4android.Event;
import at.bitfire.ical4android.InvalidCalendarException;
import at.bitfire.vcard4android.ContactsStorageException;
import lombok.Cleanup;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
public class CalendarSyncManager extends SyncManager {
@ -72,7 +73,7 @@ public class CalendarSyncManager extends SyncManager {
@Override
protected void prepare() {
collectionURL = HttpUrl.parse(localCalendar().getName());
davCollection = new DavCalendar(log, httpClient, collectionURL);
davCollection = new DavCalendar(httpClient, collectionURL);
}
@Override
@ -80,7 +81,7 @@ public class CalendarSyncManager extends SyncManager {
davCollection.propfind(0, DisplayName.NAME, CalendarColor.NAME, GetCTag.NAME);
// update name and color
log.info("Setting calendar name and color (if available)");
App.log.info("Setting calendar name and color (if available)");
ContentValues values = new ContentValues(2);
DisplayName pDisplayName = (DisplayName)davCollection.properties.get(DisplayName.NAME);
@ -118,20 +119,20 @@ public class CalendarSyncManager extends SyncManager {
remoteResources = new HashMap<>(davCollection.members.size());
for (DavResource iCal : davCollection.members) {
String fileName = iCal.fileName();
log.debug("Found remote VEVENT: " + fileName);
App.log.fine("Found remote VEVENT: " + fileName);
remoteResources.put(fileName, iCal);
}
}
@Override
protected void downloadRemote() throws IOException, HttpException, DavException, CalendarStorageException {
log.info("Downloading " + toDownload.size() + " events (" + MAX_MULTIGET + " at once)");
App.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;
log.info("Downloading " + StringUtils.join(bunch, ", "));
App.log.info("Downloading " + StringUtils.join(bunch, ", "));
if (bunch.length == 1) {
// only one contact, use GET
@ -194,28 +195,28 @@ public class CalendarSyncManager extends SyncManager {
try {
events = Event.fromStream(stream, charset);
} catch (InvalidCalendarException e) {
log.error("Received invalid iCalendar, ignoring");
App.log.log(Level.SEVERE, "Received invalid iCalendar, ignoring", e);
return;
}
if (events != null && events.length == 1) {
if (events.length == 1) {
Event newData = events[0];
// delete local event, if it exists
LocalEvent localEvent = (LocalEvent)localResources.get(fileName);
if (localEvent != null) {
log.info("Updating " + fileName + " in local calendar");
App.log.info("Updating " + fileName + " in local calendar");
localEvent.setETag(eTag);
localEvent.update(newData);
syncResult.stats.numUpdates++;
} else {
log.info("Adding " + fileName + " to local calendar");
App.log.info("Adding " + fileName + " to local calendar");
localEvent = new LocalEvent(localCalendar(), newData, fileName, eTag);
localEvent.add();
syncResult.stats.numInserts++;
}
} else
log.error("Received VCALENDAR with not exactly one VEVENT with UID, but without RECURRENCE-ID; ignoring " + fileName);
App.log.severe("Received VCALENDAR with not exactly one VEVENT with UID, but without RECURRENCE-ID; ignoring " + fileName);
}
}

@ -24,8 +24,9 @@ import android.provider.CalendarContract;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Level;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.model.CollectionInfo;
import at.bitfire.davdroid.model.ServiceDB;
import at.bitfire.davdroid.model.ServiceDB.OpenHelper;
@ -64,7 +65,7 @@ public class CalendarsSyncAdapterService extends Service {
@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
Constants.log.info("Starting calendar sync (" + authority + ")");
App.log.info("Starting calendar sync (" + authority + ")");
// required for ical4j and dav4android (ServiceLoader)
Thread.currentThread().setContextClassLoader(getContext().getClassLoader());
@ -73,15 +74,15 @@ public class CalendarsSyncAdapterService extends Service {
updateLocalCalendars(provider, account);
for (LocalCalendar calendar : (LocalCalendar[])LocalCalendar.find(account, provider, LocalCalendar.Factory.INSTANCE, CalendarContract.Calendars.SYNC_EVENTS + "!=0", null)) {
Constants.log.info("Synchronizing calendar #" + calendar.getId() + ", URL: " + calendar.getName());
App.log.info("Synchronizing calendar #" + calendar.getId() + ", URL: " + calendar.getName());
CalendarSyncManager syncManager = new CalendarSyncManager(getContext(), account, extras, authority, syncResult, calendar);
syncManager.performSync();
}
} catch (CalendarStorageException e) {
Constants.log.error("Couldn't enumerate local calendars", e);
App.log.log(Level.SEVERE, "Couldn't enumerate local calendars", e);
}
Constants.log.info("Calendar sync complete");
App.log.info("Calendar sync complete");
}
private void updateLocalCalendars(ContentProviderClient provider, Account account) throws CalendarStorageException {
@ -95,12 +96,12 @@ public class CalendarsSyncAdapterService extends Service {
for (LocalCalendar calendar : local) {
String url = calendar.getName();
if (!remote.containsKey(url)) {
Constants.log.debug("Deleting obsolete local calendar {}", url);
App.log.fine("Deleting obsolete local calendar " + url);
calendar.delete();
} else {
// remote CollectionInfo found for this local collection, update data
CollectionInfo info = remote.get(url);
Constants.log.debug("Updating local calendar {} with {}", url, info);
App.log.fine("Updating local calendar " + url + " with " + info);
calendar.update(info);
// we already have a local calendar for this remote collection, don't take into consideration anymore
remote.remove(url);
@ -110,7 +111,7 @@ public class CalendarsSyncAdapterService extends Service {
// create new local calendars
for (String url : remote.keySet()) {
CollectionInfo info = remote.get(url);
Constants.log.info("Adding local calendar list {}", info);
App.log.info("Adding local calendar list " + info);
LocalCalendar.create(account, provider, info);
}
}

@ -22,7 +22,7 @@ import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.NonNull;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.model.CollectionInfo;
import at.bitfire.davdroid.model.ServiceDB;
import at.bitfire.davdroid.model.ServiceDB.Collections;
@ -60,7 +60,7 @@ public class ContactsSyncAdapterService extends Service {
@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
Constants.log.info("Starting address book sync (" + authority + ")");
App.log.info("Starting address book sync (" + authority + ")");
// required for dav4android (ServiceLoader)
Thread.currentThread().setContextClassLoader(getContext().getClassLoader());
@ -72,9 +72,9 @@ public class ContactsSyncAdapterService extends Service {
ContactsSyncManager syncManager = new ContactsSyncManager(getContext(), account, extras, authority, provider, syncResult, remote);
syncManager.performSync();
} else
Constants.log.info("No address book collection selected for synchronization");
App.log.info("No address book collection selected for synchronization");
Constants.log.info("Address book sync complete");
App.log.info("Address book sync complete");
}
private long getService(@NonNull Account account) {

@ -14,16 +14,6 @@ import android.content.Context;
import android.content.SyncResult;
import android.os.Bundle;
import at.bitfire.davdroid.model.CollectionInfo;
import at.bitfire.ical4android.CalendarStorageException;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import org.apache.commons.codec.Charsets;
import org.apache.commons.lang3.StringUtils;
@ -34,6 +24,7 @@ import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import at.bitfire.dav4android.DavAddressBook;
import at.bitfire.dav4android.DavResource;
@ -44,20 +35,30 @@ import at.bitfire.dav4android.property.GetCTag;
import at.bitfire.dav4android.property.GetContentType;
import at.bitfire.dav4android.property.GetETag;
import at.bitfire.dav4android.property.SupportedAddressData;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.ArrayUtils;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.HttpClient;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.model.CollectionInfo;
import at.bitfire.davdroid.resource.LocalAddressBook;
import at.bitfire.davdroid.resource.LocalContact;
import at.bitfire.davdroid.resource.LocalGroup;
import at.bitfire.davdroid.resource.LocalResource;
import at.bitfire.ical4android.CalendarStorageException;
import at.bitfire.vcard4android.Contact;
import at.bitfire.vcard4android.ContactsStorageException;
import ezvcard.VCardVersion;
import ezvcard.util.IOUtils;
import lombok.Cleanup;
import lombok.RequiredArgsConstructor;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
public class ContactsSyncManager extends SyncManager {
protected static final int MAX_MULTIGET = 10;
@ -87,12 +88,12 @@ public class ContactsSyncManager extends SyncManager {
String url = remote.url;
String lastUrl = localAddressBook().getURL();
if (!url.equals(lastUrl)) {
Constants.log.info("Selected address book has changed from {} to {}, deleting all local contacts", lastUrl, url);
App.log.info("Selected address book has changed from " + lastUrl + " to " + url + ", deleting all local contacts");
((LocalAddressBook)localCollection).deleteAll();
}
collectionURL = HttpUrl.parse(url);
davCollection = new DavAddressBook(log, httpClient, collectionURL);
davCollection = new DavAddressBook(httpClient, collectionURL);
processChangedGroups();
}
@ -107,7 +108,7 @@ public class ContactsSyncManager extends SyncManager {
for (MediaType type : supportedAddressData.types)
if ("text/vcard; version=4.0".equalsIgnoreCase(type.toString()))
hasVCard4 = true;
log.info("Server advertises VCard/4 support: " + hasVCard4);
App.log.info("Server advertises VCard/4 support: " + hasVCard4);
}
@Override
@ -133,7 +134,7 @@ public class ContactsSyncManager extends SyncManager {
* Zimbra ZCS 500 Server Error https://bugzilla.zimbra.com/show_bug.cgi?id=101902
*/
if (e.status == 400 || e.status == 403 || e.status == 500 || e.status == 501) {
log.warn("Server error on REPORT addressbook-query, falling back to PROPFIND", e);
App.log.log(Level.WARNING, "Server error on REPORT addressbook-query, falling back to PROPFIND", e);
davAddressBook().propfind(1, GetETag.NAME);
} else
// no defined fallback, pass through exception
@ -143,14 +144,14 @@ public class ContactsSyncManager extends SyncManager {
remoteResources = new HashMap<>(davCollection.members.size());
for (DavResource vCard : davCollection.members) {
String fileName = vCard.fileName();
log.debug("Found remote VCard: " + fileName);
App.log.fine("Found remote VCard: " + fileName);
remoteResources.put(fileName, vCard);
}
}
@Override
protected void downloadRemote() throws IOException, HttpException, DavException, ContactsStorageException {
log.info("Downloading " + toDownload.size() + " contacts (" + MAX_MULTIGET + " at once)");
App.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(collectionURL);
@ -160,7 +161,7 @@ public class ContactsSyncManager extends SyncManager {
if (Thread.interrupted())
return;
log.info("Downloading " + StringUtils.join(bunch, ", "));
App.log.info("Downloading " + StringUtils.join(bunch, ", "));
if (bunch.length == 1) {
// only one contact, use GET
@ -230,7 +231,7 @@ public class ContactsSyncManager extends SyncManager {
// groups with DELETED=1: remove group finally
for (LocalGroup group : addressBook.getDeletedGroups()) {
long groupId = group.getId();
log.debug("Finally removing group #" + groupId);
App.log.fine("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);
@ -240,32 +241,32 @@ 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();
log.debug("Marking members of modified group #" + groupId + " as dirty");
App.log.fine("Marking members of modified group #" + groupId + " as dirty");
addressBook.markMembersDirty(groupId);
group.clearDirty();
}
}
private void processVCard(String fileName, String eTag, InputStream stream, Charset charset, Contact.Downloader downloader) throws IOException, ContactsStorageException {
Contact contacts[] = Contact.fromStream(stream, charset, downloader);
if (contacts != null && contacts.length == 1) {
Contact[] contacts = Contact.fromStream(stream, charset, downloader);
if (contacts.length == 1) {
Contact newData = contacts[0];
// update local contact, if it exists
LocalContact localContact = (LocalContact)localResources.get(fileName);
if (localContact != null) {
log.info("Updating " + fileName + " in local address book");
App.log.info("Updating " + fileName + " in local address book");
localContact.eTag = eTag;
localContact.update(newData);
syncResult.stats.numUpdates++;
} else {
log.info("Adding " + fileName + " to local address book");
App.log.info("Adding " + fileName + " to local address book");
localContact = new LocalContact(localAddressBook(), newData, fileName, eTag);
localContact.add();
syncResult.stats.numInserts++;
}
} else
log.error("Received VCard with not exactly one VCARD, ignoring " + fileName);
App.log.severe("Received VCard with not exactly one VCARD, ignoring " + fileName);
}
@ -280,13 +281,13 @@ public class ContactsSyncManager extends SyncManager {
HttpUrl httpUrl = HttpUrl.parse(url);
if (httpUrl == null) {
log.error("Invalid external resource URL");
App.log.log(Level.SEVERE, "Invalid external resource URL", url);
return null;
}
String host = httpUrl.host();
if (host == null) {
log.error("External resource URL doesn't specify a host name");
App.log.log(Level.SEVERE, "External resource URL doesn't specify a host name", url);
return null;
}
@ -312,10 +313,10 @@ public class ContactsSyncManager extends SyncManager {
if (response.isSuccessful() && stream != null) {
return IOUtils.toByteArray(stream);
} else
log.error("Couldn't download external resource");
App.log.severe("Couldn't download external resource");
}
} catch(IOException e) {
log.error("Couldn't download external resource", e);
App.log.log(Level.SEVERE, "Couldn't download external resource", e);
}
return null;
}

@ -20,8 +20,6 @@ package at.bitfire.davdroid.syncadapter;
import android.os.Bundle;
import android.text.TextUtils;
import org.slf4j.Logger;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
@ -29,6 +27,7 @@ package at.bitfire.davdroid.syncadapter;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import at.bitfire.dav4android.DavResource;
import at.bitfire.dav4android.exception.ConflictException;
@ -40,10 +39,9 @@ package at.bitfire.davdroid.syncadapter;
import at.bitfire.dav4android.property.GetCTag;
import at.bitfire.dav4android.property.GetETag;
import at.bitfire.davdroid.AccountSettings;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.App;
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.AccountActivity;
@ -80,8 +78,6 @@ abstract public class SyncManager {
protected final AccountSettings settings;
protected LocalCollection localCollection;
protected Logger log;
protected OkHttpClient httpClient;
protected HttpUrl collectionURL;
protected DavResource davCollection;
@ -125,60 +121,60 @@ abstract public class SyncManager {
public void performSync() {
int syncPhase = SYNC_PHASE_PREPARE;
try {
log.info("Preparing synchronization");
App.log.info("Preparing synchronization");
prepare();
if (Thread.interrupted())
return;
syncPhase = SYNC_PHASE_QUERY_CAPABILITIES;
log.info("Querying capabilities");
App.log.info("Querying capabilities");
queryCapabilities();
syncPhase = SYNC_PHASE_PROCESS_LOCALLY_DELETED;
log.info("Processing locally deleted entries");
App.log.info("Processing locally deleted entries");
processLocallyDeleted();
if (Thread.interrupted())
return;
syncPhase = SYNC_PHASE_PREPARE_DIRTY;
log.info("Locally preparing dirty entries");
App.log.info("Locally preparing dirty entries");
prepareDirty();
syncPhase = SYNC_PHASE_UPLOAD_DIRTY;
log.info("Uploading dirty entries");
App.log.info("Uploading dirty entries");
uploadDirty();
syncPhase = SYNC_PHASE_CHECK_SYNC_STATE;
log.info("Checking sync state");
App.log.info("Checking sync state");
if (checkSyncState()) {
syncPhase = SYNC_PHASE_LIST_LOCAL;
log.info("Listing local entries");
App.log.info("Listing local entries");
listLocal();
if (Thread.interrupted())
return;
syncPhase = SYNC_PHASE_LIST_REMOTE;
log.info("Listing remote entries");
App.log.info("Listing remote entries");
listRemote();
if (Thread.interrupted())
return;
syncPhase = SYNC_PHASE_COMPARE_LOCAL_REMOTE;
log.info("Comparing local/remote entries");
App.log.info("Comparing local/remote entries");
compareLocalRemote();
syncPhase = SYNC_PHASE_DOWNLOAD_REMOTE;
log.info("Downloading remote entries");
App.log.info("Downloading remote entries");
downloadRemote();
syncPhase = SYNC_PHASE_SAVE_SYNC_STATE;
log.info("Saving sync state");
App.log.info("Saving sync state");
saveSyncState();
} else
log.info("Remote collection didn't change, skipping remote sync");
App.log.info("Remote collection didn't change, skipping remote sync");
} catch (IOException|ServiceUnavailableException e) {
log.error("I/O exception during sync, trying again later", e);
App.log.log(Level.WARNING, "I/O exception during sync, trying again later", e);
syncResult.stats.numIoExceptions++;
if (e instanceof ServiceUnavailableException) {
@ -193,19 +189,19 @@ abstract public class SyncManager {
final int messageString;
if (e instanceof UnauthorizedException) {
log.error("Not authorized anymore", e);
App.log.log(Level.SEVERE, "Not authorized anymore", e);
messageString = R.string.sync_error_unauthorized;
syncResult.stats.numAuthExceptions++;
} else if (e instanceof HttpException || e instanceof DavException) {
log.error("HTTP/DAV Exception during sync", e);
App.log.log(Level.SEVERE, "HTTP/DAV Exception during sync", e);
messageString = R.string.sync_error_http_dav;
syncResult.stats.numParseExceptions++;
} else if (e instanceof CalendarStorageException || e instanceof ContactsStorageException) {
log.error("Couldn't access local storage", e);
App.log.log(Level.SEVERE, "Couldn't access local storage", e);
messageString = R.string.sync_error_local_storage;
syncResult.databaseError = true;
} else {
log.error("Unknown sync error", e);
App.log.log(Level.SEVERE, "Unknown sync error", e);
messageString = R.string.sync_error;
syncResult.stats.numParseExceptions++;
}
@ -224,7 +220,7 @@ abstract public class SyncManager {
Notification.Builder builder = new Notification.Builder(context);
Notification notification;
builder .setSmallIcon(R.drawable.ic_launcher)
builder.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle(getSyncErrorTitle())
.setContentIntent(PendingIntent.getActivity(context, notificationId, detailsIntent, PendingIntent.FLAG_UPDATE_CURRENT));
@ -247,13 +243,6 @@ 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);
}
}
}
@ -276,15 +265,15 @@ abstract public class SyncManager {
final String fileName = local.getFileName();
if (!TextUtils.isEmpty(fileName)) {
log.info(fileName + " has been deleted locally -> deleting from server");
App.log.info(fileName + " has been deleted locally -> deleting from server");
try {
new DavResource(log, httpClient, collectionURL.newBuilder().addPathSegment(fileName).build())
new DavResource(httpClient, collectionURL.newBuilder().addPathSegment(fileName).build())
.delete(local.getETag());
} catch (IOException|HttpException e) {
log.warn("Couldn't delete " + fileName + " from server; ignoring (may be downloaded again)");
App.log.warning("Couldn't delete " + fileName + " from server; ignoring (may be downloaded again)");
}
} else
log.info("Removing local record #" + local.getId() + " which has been deleted locally and was never uploaded");
App.log.info("Removing local record #" + local.getId() + " which has been deleted locally and was never uploaded");
local.delete();
syncResult.stats.numDeletes++;
}
@ -294,7 +283,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();
log.info("Found local record #" + local.getId() + " without file name; assigning file name/UID based on " + uuid);
App.log.info("Found local record #" + local.getId() + " without file name; assigning file name/UID based on " + uuid);
local.updateFileNameAndUID(uuid);
}
}
@ -313,7 +302,7 @@ abstract public class SyncManager {
final String fileName = local.getFileName();
DavResource remote = new DavResource(log, httpClient, collectionURL.newBuilder().addPathSegment(fileName).build());
DavResource remote = new DavResource(httpClient, collectionURL.newBuilder().addPathSegment(fileName).build());
// generate entity to upload (VCard, iCal, whatever)
RequestBody body = prepareUpload(local);
@ -321,25 +310,25 @@ abstract public class SyncManager {
try {
if (local.getETag() == null) {
log.info("Uploading new record " + fileName);
App.log.info("Uploading new record " + fileName);
remote.put(body, null, true);
} else {
log.info("Uploading locally modified record " + fileName);
App.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
log.info("Resource has been modified on the server before upload, ignoring", e);
App.log.log(Level.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;
log.debug("Received new ETag=" + eTag + " after uploading");
App.log.fine("Received new ETag=" + eTag + " after uploading");
} else
log.debug("Didn't receive new ETag after uploading, setting to null");
App.log.fine("Didn't receive new ETag after uploading, setting to null");
local.clearDirty(eTag);
}
@ -360,12 +349,12 @@ abstract public class SyncManager {
String localCTag = null;
if (extras.containsKey(ContentResolver.SYNC_EXTRAS_MANUAL))
log.info("Manual sync, ignoring CTag");
App.log.info("Manual sync, ignoring CTag");
else
localCTag = localCollection.getCTag();
if (remoteCTag != null && remoteCTag.equals(localCTag)) {
log.info("Remote collection didn't change (CTag=" + remoteCTag + "), no need to query children");
App.log.info("Remote collection didn't change (CTag=" + remoteCTag + "), no need to query children");
return false;
} else
return true;
@ -379,7 +368,7 @@ abstract public class SyncManager {
LocalResource[] localList = localCollection.getAll();
localResources = new HashMap<>(localList.length);
for (LocalResource resource : localList) {
log.debug("Found local resource: " + resource.getFileName());
App.log.fine("Found local resource: " + resource.getFileName());
localResources.put(resource.getFileName(), resource);
}
}
@ -406,7 +395,7 @@ abstract public class SyncManager {
for (String localName : localResources.keySet()) {
DavResource remote = remoteResources.get(localName);
if (remote == null) {
log.info(localName + " is not on server anymore, deleting");
App.log.info(localName + " is not on server anymore, deleting");
localResources.get(localName).delete();
syncResult.stats.numDeletes++;
} else {
@ -419,7 +408,7 @@ abstract public class SyncManager {
if (remoteETag.equals(localETag))
syncResult.stats.numSkippedEntries++;
else {
log.info(localName + " has been changed on server (current ETag=" + remoteETag + ", last known ETag=" + localETag + ")");
App.log.info(localName + " has been changed on server (current ETag=" + remoteETag + ", last known ETag=" + localETag + ")");
toDownload.add(remote);
}
@ -430,7 +419,7 @@ abstract public class SyncManager {
// add all unseen (= remotely added) remote contacts
if (!remoteResources.isEmpty()) {
log.info("New resources have been found on the server: " + TextUtils.join(", ", remoteResources.keySet()));
App.log.info("New resources have been found on the server: " + TextUtils.join(", ", remoteResources.keySet()));
toDownload.addAll(remoteResources.values());
}
}
@ -445,7 +434,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. */
log.info("Saving CTag=" + remoteCTag);
App.log.info("Saving CTag=" + remoteCTag);
localCollection.setCTag(remoteCTag);
}

@ -21,12 +21,15 @@ import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.os.IBinder;
import java.util.*;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.logging.Level;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.model.CollectionInfo;
import at.bitfire.davdroid.model.ServiceDB.*;
import at.bitfire.davdroid.model.ServiceDB.Collections;
import at.bitfire.davdroid.model.ServiceDB.OpenHelper;
import at.bitfire.davdroid.model.ServiceDB.Services;
import at.bitfire.davdroid.resource.LocalTaskList;
import at.bitfire.ical4android.CalendarStorageException;
import at.bitfire.ical4android.TaskProvider;
@ -63,7 +66,7 @@ public class TasksSyncAdapterService extends Service {
@Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient providerClient, SyncResult syncResult) {
Constants.log.info("Starting task sync (" + authority + ")");
App.log.info("Starting task sync (" + authority + ")");
// required for ical4j and dav4android (ServiceLoader)
Thread.currentThread().setContextClassLoader(getContext().getClassLoader());
@ -76,17 +79,17 @@ public class TasksSyncAdapterService extends Service {
updateLocalTaskLists(provider, account);
for (LocalTaskList taskList : (LocalTaskList[])LocalTaskList.find(account, provider, LocalTaskList.Factory.INSTANCE, null, null)) {
Constants.log.info("Synchronizing task list #" + taskList.getId() + ", URL: " + taskList.getSyncId());
App.log.info("Synchronizing task list #" + taskList.getId() + ", URL: " + taskList.getSyncId());
TasksSyncManager syncManager = new TasksSyncManager(getContext(), account, extras, authority, provider, syncResult, taskList);
syncManager.performSync();
}
} catch (CalendarStorageException e) {
Constants.log.error("Couldn't enumerate local task lists", e);
App.log.log(Level.SEVERE, "Couldn't enumerate local task lists", e);
} finally {
db.close();
}
Constants.log.info("Task sync complete");
App.log.info("Task sync complete");
}
private void updateLocalTaskLists(TaskProvider provider, Account account) throws CalendarStorageException {
@ -100,12 +103,12 @@ public class TasksSyncAdapterService extends Service {
for (LocalTaskList list : local) {
String url = list.getSyncId();
if (!remote.containsKey(url)) {
Constants.log.debug("Deleting obsolete local task list {}", url);
App.log.fine("Deleting obsolete local task list" + url);
list.delete();
} else {
// remote CollectionInfo found for this local collection, update data
CollectionInfo info = remote.get(url);
Constants.log.debug("Updating local task list {} with {}", url, info);
App.log.fine("Updating local task list " + url + " with " + info);
list.update(info);
// we already have a local task list for this remote collection, don't take into consideration anymore
remote.remove(url);
@ -115,7 +118,7 @@ public class TasksSyncAdapterService extends Service {
// create new local task lists
for (String url : remote.keySet()) {
CollectionInfo info = remote.get(url);
Constants.log.info("Adding local task list {}", info);
App.log.info("Adding local task list " + info);
LocalTaskList.create(account, provider, info);
}
}

@ -15,11 +15,6 @@ import android.content.SyncResult;
import android.os.Bundle;
import android.text.TextUtils;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import org.apache.commons.codec.Charsets;
import org.apache.commons.lang3.StringUtils;
import org.dmfs.provider.tasks.TaskContract.TaskLists;
@ -31,6 +26,7 @@ import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import at.bitfire.dav4android.DavCalendar;
import at.bitfire.dav4android.DavResource;
@ -42,10 +38,10 @@ import at.bitfire.dav4android.property.DisplayName;
import at.bitfire.dav4android.property.GetCTag;
import at.bitfire.dav4android.property.GetContentType;
import at.bitfire.dav4android.property.GetETag;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.ArrayUtils;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.resource.LocalCalendar;
import at.bitfire.davdroid.resource.LocalResource;
import at.bitfire.davdroid.resource.LocalTask;
import at.bitfire.davdroid.resource.LocalTaskList;
@ -54,6 +50,10 @@ import at.bitfire.ical4android.InvalidCalendarException;
import at.bitfire.ical4android.Task;
import at.bitfire.ical4android.TaskProvider;
import lombok.Cleanup;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
public class TasksSyncManager extends SyncManager {
@ -77,7 +77,7 @@ public class TasksSyncManager extends SyncManager {
@Override
protected void prepare() {
collectionURL = HttpUrl.parse(localTaskList().getSyncId());
davCollection = new DavCalendar(log, httpClient, collectionURL);
davCollection = new DavCalendar(httpClient, collectionURL);
}
@Override
@ -85,7 +85,7 @@ public class TasksSyncManager extends SyncManager {
davCollection.propfind(0, DisplayName.NAME, CalendarColor.NAME, GetCTag.NAME);
// update name and color
log.info("Setting task list name and color (if available)");
App.log.info("Setting task list name and color (if available)");
ContentValues values = new ContentValues(2);
DisplayName pDisplayName = (DisplayName)davCollection.properties.get(DisplayName.NAME);
@ -116,21 +116,21 @@ public class TasksSyncManager extends SyncManager {
remoteResources = new HashMap<>(davCollection.members.size());
for (DavResource vCard : davCollection.members) {
String fileName = vCard.fileName();
log.debug("Found remote VTODO: " + fileName);
App.log.fine("Found remote VTODO: " + fileName);
remoteResources.put(fileName, vCard);
}
}
@Override
protected void downloadRemote() throws IOException, HttpException, DavException, CalendarStorageException {
log.info("Downloading " + toDownload.size() + " tasks (" + MAX_MULTIGET + " at once)");
App.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;
log.info("Downloading " + StringUtils.join(bunch, ", "));
App.log.info("Downloading " + StringUtils.join(bunch, ", "));
if (bunch.length == 1) {
// only one contact, use GET
@ -189,32 +189,32 @@ public class TasksSyncManager extends SyncManager {
private DavCalendar davCalendar() { return (DavCalendar)davCollection; }
private void processVTodo(String fileName, String eTag, InputStream stream, Charset charset) throws IOException, CalendarStorageException {
Task[] tasks = null;
Task[] tasks;
try {
tasks = Task.fromStream(stream, charset);
} catch (InvalidCalendarException e) {
log.error("Received invalid iCalendar, ignoring", e);
App.log.log(Level.SEVERE, "Received invalid iCalendar, ignoring", e);
return;
}
if (tasks != null && tasks.length == 1) {
if (tasks.length == 1) {
Task newData = tasks[0];
// update local task, if it exists
LocalTask localTask = (LocalTask)localResources.get(fileName);
if (localTask != null) {
log.info("Updating " + fileName + " in local tasklist");
App.log.info("Updating " + fileName + " in local tasklist");
localTask.setETag(eTag);
localTask.update(newData);
syncResult.stats.numUpdates++;
} else {
log.info("Adding " + fileName + " to local task list");
App.log.info("Adding " + fileName + " to local task list");
localTask = new LocalTask(localTaskList(), newData, fileName, eTag);
localTask.add();
syncResult.stats.numInserts++;
}
} else
log.error("Received VCALENDAR with not exactly one VTODO; ignoring " + fileName);
App.log.severe("Received VCALENDAR with not exactly one VTODO; ignoring " + fileName);
}
}

@ -59,8 +59,9 @@ import org.apache.commons.lang3.BooleanUtils;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.DavService;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.model.CollectionInfo;
@ -494,7 +495,7 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
if (future.getResult().getBoolean(AccountManager.KEY_BOOLEAN_RESULT))
finish();
} catch(OperationCanceledException|IOException|AuthenticatorException e) {
Constants.log.error("Couldn't remove account", e);
App.log.log(Level.SEVERE, "Couldn't remove account", e);
}
}
}, null);
@ -506,7 +507,7 @@ public class AccountActivity extends AppCompatActivity implements Toolbar.OnMenu
if (future.getResult())
finish();
} catch (OperationCanceledException|IOException|AuthenticatorException e) {
Constants.log.error("Couldn't remove account", e);
App.log.log(Level.SEVERE, "Couldn't remove account", e);
}
}
}, null);

@ -18,8 +18,8 @@ import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.support.v7.preference.SwitchPreferenceCompat;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.AccountSettings;
import at.bitfire.davdroid.R;
import at.bitfire.ical4android.TaskProvider;
public class AccountSettingsFragment extends PreferenceFragmentCompat {

@ -20,7 +20,6 @@ import android.support.v7.widget.Toolbar;
import android.view.MenuItem;
import android.view.View;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.ui.setup.LoginActivity;

@ -43,12 +43,11 @@ import java.util.List;
import at.bitfire.dav4android.DavResource;
import at.bitfire.dav4android.XmlUtils;
import at.bitfire.dav4android.exception.HttpException;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.HttpClient;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.model.CollectionInfo;
import at.bitfire.davdroid.model.Service;
import at.bitfire.davdroid.model.HomeSet;
import at.bitfire.davdroid.model.Service;
import at.bitfire.davdroid.model.ServiceDB;
import lombok.Cleanup;
import okhttp3.HttpUrl;
@ -228,10 +227,10 @@ public class CreateAddressBookActivity extends AppCompatActivity implements Load
serializer.endTag(XmlUtils.NS_WEBDAV, "mkcol");
serializer.endDocument();
} catch (IOException e) {
Constants.log.error("Couldn't assemble MKCOL request", e);
//App.log.severe("Couldn't assemble MKCOL request", e);
}
DavResource addressBook = new DavResource(null, client, HttpUrl.parse(info.url));
DavResource addressBook = new DavResource(client, HttpUrl.parse(info.url));
try {
addressBook.mkCol(writer.toString());

@ -20,26 +20,16 @@ import android.support.v4.app.NavUtils;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.AppCompatCheckBox;
import android.support.v7.widget.AppCompatRadioButton;
import android.support.v7.widget.AppCompatSpinner;
import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.webkit.URLUtil;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Toast;
import net.fortuna.ical4j.data.CalendarBuilder;
import net.fortuna.ical4j.model.Calendar;
import net.fortuna.ical4j.model.TimeZoneRegistry;
import net.fortuna.ical4j.model.TimeZoneRegistryFactory;
import net.fortuna.ical4j.util.TimeZones;
import org.apache.commons.lang3.StringUtils;
@ -48,7 +38,6 @@ import java.util.List;
import java.util.TimeZone;
import java.util.UUID;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.model.CollectionInfo;
import at.bitfire.davdroid.model.ServiceDB;
@ -56,7 +45,6 @@ import at.bitfire.ical4android.DateUtils;
import lombok.Cleanup;
import okhttp3.HttpUrl;
import yuku.ambilwarna.AmbilWarnaDialog;
import yuku.ambilwarna.AmbilWarnaSquare;
public class CreateCalendarActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<CreateCalendarActivity.AccountInfo> {
public static final String EXTRA_ACCOUNT = "account";

@ -28,11 +28,12 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.StringWriter;
import java.util.logging.Level;
import at.bitfire.dav4android.DavResource;
import at.bitfire.dav4android.XmlUtils;
import at.bitfire.dav4android.exception.HttpException;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.DavUtils;
import at.bitfire.davdroid.HttpClient;
import at.bitfire.davdroid.R;
@ -201,13 +202,13 @@ public class CreateCollectionFragment extends DialogFragment implements LoaderMa
serializer.endTag(XmlUtils.NS_WEBDAV, "mkcol");
serializer.endDocument();
} catch (IOException e) {
Constants.log.error("Couldn't assemble Extended MKCOL request", e);
App.log.log(Level.SEVERE, "Couldn't assemble Extended MKCOL request", e);
}
ServiceDB.OpenHelper dbHelper = new ServiceDB.OpenHelper(getContext());
OkHttpClient client = HttpClient.create(getContext(), account);
DavResource collection = new DavResource(null, client, HttpUrl.parse(info.url));
DavResource collection = new DavResource(client, HttpUrl.parse(info.url));
try {
// create collection on remote server
collection.mkCol(writer.toString());

@ -29,19 +29,20 @@ import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.commons.lang3.text.WordUtils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;
import java.util.logging.Level;
import at.bitfire.dav4android.exception.HttpException;
import at.bitfire.davdroid.AccountSettings;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.BuildConfig;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.AccountSettings;
public class DebugInfoActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<String> {
public static final String
@ -86,7 +87,7 @@ public class DebugInfoActivity extends AppCompatActivity implements LoaderManage
// report is too long for inline text, send it as an attachment
try {
File reportFile = File.createTempFile("davdroid-debug", ".txt", getExternalCacheDir());
Constants.log.debug("Writing debug info to " + reportFile.getAbsolutePath());
App.log.fine("Writing debug info to " + reportFile.getAbsolutePath());
FileWriter writer = new FileWriter(reportFile);
writer.write(report);
writer.close();
@ -197,7 +198,7 @@ public class DebugInfoActivity extends AppCompatActivity implements LoaderManage
"JB Workaround installed: " + (workaroundInstalled ? "yes" : "no") + "\n\n"
);
} catch(Exception ex) {
Constants.log.error("Couldn't get software information", ex);
App.log.log(Level.SEVERE, "Couldn't get software information", ex);
}
report.append(
@ -223,7 +224,7 @@ public class DebugInfoActivity extends AppCompatActivity implements LoaderManage
"Device: " + WordUtils.capitalize(Build.MANUFACTURER) + " " + Build.MODEL + " (" + Build.DEVICE + ")\n\n"
);
} catch (Exception ex) {
Constants.log.error("Couldn't get system details", ex);
App.log.log(Level.SEVERE, "Couldn't get system details", ex);
}
return report.toString();

@ -110,7 +110,7 @@ public class DeleteCollectionFragment extends DialogFragment implements LoaderMa
public Exception loadInBackground() {
OkHttpClient httpClient = HttpClient.create(getContext(), account);
DavResource collection = new DavResource(null, httpClient, HttpUrl.parse(collectionInfo.url));
DavResource collection = new DavResource(httpClient, HttpUrl.parse(collectionInfo.url));
try {
// delete collection from server
collection.delete(null);

@ -52,7 +52,7 @@ public class ExceptionInfoFragment extends DialogFragment {
.setIcon(R.drawable.ic_error_dark)
.setTitle(title)
.setMessage(exception.getClass().getCanonicalName() + "\n" + exception.getLocalizedMessage())
.setPositiveButton(R.string.exception_show_details, new DialogInterface.OnClickListener() {
.setNegativeButton(R.string.exception_show_details, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(getContext(), DebugInfoActivity.class);
@ -62,7 +62,7 @@ public class ExceptionInfoFragment extends DialogFragment {
startActivity(intent);
}
})
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
}

@ -26,7 +26,10 @@ import android.widget.Button;
import android.widget.EditText;
import java.net.URI;
import java.util.logging.Level;
import at.bitfire.davdroid.AccountSettings;
import at.bitfire.davdroid.App;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.DavService;
import at.bitfire.davdroid.R;
@ -35,7 +38,6 @@ import at.bitfire.davdroid.model.ServiceDB.Collections;
import at.bitfire.davdroid.model.ServiceDB.HomeSets;
import at.bitfire.davdroid.model.ServiceDB.OpenHelper;
import at.bitfire.davdroid.model.ServiceDB.Services;
import at.bitfire.davdroid.AccountSettings;
import at.bitfire.ical4android.TaskProvider;
import lombok.Cleanup;
@ -90,7 +92,7 @@ public class AccountDetailsFragment extends Fragment {
protected boolean createAccount(String accountName, DavResourceFinder.Configuration config) {
Account account = new Account(accountName, Constants.ACCOUNT_TYPE);
Constants.log.info("Creating account {}, initial config: {}", accountName, config);
App.log.log(Level.INFO, "Creating account " + accountName + " with initial config", config);
// create Android account
AccountManager accountManager = AccountManager.get(getContext());

@ -10,7 +10,6 @@ package at.bitfire.davdroid.ui.setup;
import android.content.Context;
import android.support.annotation.NonNull;
import org.slf4j.Logger;
import org.xbill.DNS.Lookup;
import org.xbill.DNS.Record;
import org.xbill.DNS.SRVRecord;
@ -26,6 +25,8 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import at.bitfire.dav4android.DavResource;
import at.bitfire.dav4android.UrlUtils;
@ -44,7 +45,7 @@ import at.bitfire.dav4android.property.DisplayName;
import at.bitfire.dav4android.property.ResourceType;
import at.bitfire.dav4android.property.SupportedCalendarComponentSet;
import at.bitfire.davdroid.HttpClient;
import at.bitfire.davdroid.log.StringLogger;
import at.bitfire.davdroid.log.StringHandler;
import at.bitfire.davdroid.model.CollectionInfo;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
@ -64,16 +65,19 @@ public class DavResourceFinder {
protected final Context context;
protected final LoginCredentials credentials;
protected final Logger log = new StringLogger("DavResourceFinder");
protected final Logger log;
protected final StringHandler logBuffer = new StringHandler();
protected OkHttpClient httpClient;
public DavResourceFinder(@NonNull Context context, @NonNull LoginCredentials credentials) {
this.context = context;
this.credentials = credentials;
httpClient = HttpClient.create(context, null);
log = Logger.getLogger("davdroid.DavResourceFinder");
log.addHandler(logBuffer);
httpClient = HttpClient.create(context, null, log);
httpClient = HttpClient.addAuthentication(httpClient, credentials.userName, credentials.password, credentials.authPreemptive);
//httpClient = HttpClient.addLogger(httpClient, log);
}
@ -85,7 +89,7 @@ public class DavResourceFinder {
return new Configuration(
credentials.userName, credentials.password, credentials.authPreemptive,
cardDavConfig, calDavConfig,
log.toString()
logBuffer.toString()
);
}
@ -98,7 +102,7 @@ public class DavResourceFinder {
// put discovered information here
final Configuration.ServiceInfo config = new Configuration.ServiceInfo();
log.info("Finding initial {} service configuration", service.name);
log.info("Finding initial " + service.name + " service configuration");
if ("http".equalsIgnoreCase(baseURI.getScheme()) || "https".equalsIgnoreCase(baseURI.getScheme())) {
final HttpUrl baseURL = HttpUrl.get(baseURI);
@ -114,7 +118,7 @@ public class DavResourceFinder {
try {
config.principal = getCurrentUserPrincipal(baseURL.resolve("/.well-known/" + service.name), service);
} catch (IOException|HttpException|DavException e) {
log.debug("Well-known URL detection failed", e);
log.log(Level.FINE, "Well-known URL detection failed", e);
}
} else if ("mailto".equalsIgnoreCase(baseURI.getScheme())) {
@ -131,7 +135,7 @@ public class DavResourceFinder {
try {
config.principal = discoverPrincipalUrl(discoveryFQDN, service);
} catch (IOException|HttpException|DavException e) {
log.debug(service.name + " service discovery failed", e);
log.log(Level.FINE, service.name + " service discovery failed", e);
}
}
@ -145,7 +149,7 @@ public class DavResourceFinder {
HttpUrl principal = null;
try {
DavResource davBase = new DavResource(log, httpClient, baseURL);
DavResource davBase = new DavResource(httpClient, baseURL, log);
if (service == Service.CARDDAV) {
davBase.propfind(0,
@ -181,7 +185,7 @@ public class DavResourceFinder {
config.principal = principal.uri();
} catch (IOException|HttpException|DavException e) {
log.debug("PROPFIND/OPTIONS on user-given URL failed", e);
log.log(Level.FINE, "PROPFIND/OPTIONS on user-given URL failed", e);
}
}
@ -219,7 +223,7 @@ public class DavResourceFinder {
boolean providesService(HttpUrl url, Service service) throws IOException {
DavResource davPrincipal = new DavResource(log, httpClient, url);
DavResource davPrincipal = new DavResource(httpClient, url, log);
try {
davPrincipal.options();
@ -228,7 +232,7 @@ public class DavResourceFinder {
return true;
} catch (HttpException|DavException e) {
log.error("Couldn't detect services on {}", url);
log.log(Level.SEVERE, "Couldn't detect services on " + url, e);
}
return false;
}
@ -248,7 +252,7 @@ public class DavResourceFinder {
List<String> paths = new LinkedList<>(); // there may be multiple paths to try
final String query = "_" + service.name + "s._tcp." + domain;
log.debug("Looking up SRV records for " + query);
log.fine("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)
@ -298,7 +302,7 @@ public class DavResourceFinder {
if (principal != null)
return principal;
} catch(NotFoundException e) {
log.warn("No resource found", e);
log.log(Level.WARNING, "No resource found", e);
}
return null;
}
@ -310,8 +314,9 @@ public class DavResourceFinder {
* @return current-user-principal URL that provides required service, or null if none
*/
public URI getCurrentUserPrincipal(HttpUrl url, Service service) throws IOException, HttpException, DavException {
DavResource dav = new DavResource(log, httpClient, url);
DavResource dav = new DavResource(httpClient, url, log);
dav.propfind(0, CurrentUserPrincipal.NAME);
CurrentUserPrincipal currentUserPrincipal = (CurrentUserPrincipal) dav.properties.get(CurrentUserPrincipal.NAME);
if (currentUserPrincipal != null && currentUserPrincipal.href != null) {
HttpUrl principal = dav.location.resolve(currentUserPrincipal.href);
@ -320,7 +325,7 @@ public class DavResourceFinder {
// service check
if (service != null && !providesService(principal, service)) {
log.info("{} doesn't provide required {} service, dismissing", principal, service);
log.info(principal + " doesn't provide required " + service + " service");
principal = null;
}
@ -335,7 +340,7 @@ public class DavResourceFinder {
private SRVRecord selectSRVRecord(Record[] records) {
if (records.length > 1)
log.warn("Multiple SRV records not supported yet; using first one");
log.warning("Multiple SRV records not supported yet; using first one");
return (SRVRecord)records[0];
}

@ -21,15 +21,9 @@ import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
import android.support.v7.app.AlertDialog;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import at.bitfire.davdroid.Constants;
import at.bitfire.davdroid.R;
import at.bitfire.davdroid.ui.setup.DavResourceFinder.Configuration;
import at.bitfire.davdroid.ui.DebugInfoActivity;
import lombok.Cleanup;
import at.bitfire.davdroid.ui.setup.DavResourceFinder.Configuration;
public class DetectConfigurationFragment extends DialogFragment implements LoaderManager.LoaderCallbacks<Configuration> {
protected static final String ARG_LOGIN_CREDENTIALS = "credentials";
@ -142,19 +136,7 @@ public class DetectConfigurationFragment extends DialogFragment implements Loade
@Override
public Configuration loadInBackground() {
DavResourceFinder finder = new DavResourceFinder(context, credentials);
Configuration configuration = finder.findInitialConfiguration();
try {
@Cleanup BufferedReader logStream = new BufferedReader(new StringReader(configuration.logs));
Constants.log.info("Resource detection finished:");
String line;
while ((line = logStream.readLine()) != null)
Constants.log.info(line);
} catch (IOException e) {
Constants.log.error("Couldn't read resource detection logs", e);
}
return configuration;
return finder.findInitialConfiguration();
}
}
}

@ -1 +1 @@
Subproject commit fd19c6531ad5c1cbe210a7e70f5781cbfd5744a7
Subproject commit 80f3b060a7d5d240848fc89c5008b91a84046299
Loading…
Cancel
Save