From 4b5cb307629617f0db6d25b88003aaf3223b0491 Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Sun, 18 Oct 2015 17:27:53 +0200 Subject: [PATCH] Log resource detection results to viewable string * new StringLogger * DavResourceFinder: log to StringLogger; if no collections are found, logs can be views * DebugInfoActivity: show passed logs * script to fetch translations from Transifex * increase version to 0.9-beta2 --- app/build.gradle | 4 +- .../at/bitfire/davdroid/log/CustomLogger.java | 41 ++++- .../davdroid/log/ExternalFileLogger.java | 38 ---- .../at/bitfire/davdroid/log/StringLogger.java | 35 ++++ .../davdroid/resource/DavResourceFinder.java | 73 ++++---- .../bitfire/davdroid/resource/ServerInfo.java | 8 +- .../davdroid/ui/DebugInfoActivity.java | 14 +- .../davdroid/ui/setup/LoginEmailFragment.java | 31 ++-- .../davdroid/ui/setup/LoginURLFragment.java | 31 ++-- .../ui/setup/QueryServerDialogFragment.java | 171 ++++++++++-------- app/src/main/res/values/strings.xml | 4 +- scripts/fetch-translations.sh | 11 ++ 12 files changed, 276 insertions(+), 185 deletions(-) create mode 100644 app/src/main/java/at/bitfire/davdroid/log/StringLogger.java create mode 100755 scripts/fetch-translations.sh diff --git a/app/build.gradle b/app/build.gradle index 799ae6a2..4e408eca 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,8 +17,8 @@ android { minSdkVersion 14 targetSdkVersion 23 - versionCode 76 - versionName "0.9-beta1" + versionCode 77 + versionName "0.9-beta2" buildConfigField "java.util.Date", "buildTime", "new java.util.Date()" } diff --git a/app/src/main/java/at/bitfire/davdroid/log/CustomLogger.java b/app/src/main/java/at/bitfire/davdroid/log/CustomLogger.java index 3e83fa23..86c58897 100644 --- a/app/src/main/java/at/bitfire/davdroid/log/CustomLogger.java +++ b/app/src/main/java/at/bitfire/davdroid/log/CustomLogger.java @@ -8,9 +8,14 @@ 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 @@ -25,13 +30,39 @@ public abstract class CustomLogger implements Logger { PREFIX_DEBUG = "[debug] ", PREFIX_TRACE = "[trace] "; + @Getter + protected String name; + + protected PrintWriter writer; 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); + + // 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 diff --git a/app/src/main/java/at/bitfire/davdroid/log/ExternalFileLogger.java b/app/src/main/java/at/bitfire/davdroid/log/ExternalFileLogger.java index 23ba45a7..0cbd28b2 100644 --- a/app/src/main/java/at/bitfire/davdroid/log/ExternalFileLogger.java +++ b/app/src/main/java/at/bitfire/davdroid/log/ExternalFileLogger.java @@ -10,21 +10,13 @@ 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; @@ -41,35 +33,5 @@ public class ExternalFileLogger extends CustomLogger implements Closeable { 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); - } } diff --git a/app/src/main/java/at/bitfire/davdroid/log/StringLogger.java b/app/src/main/java/at/bitfire/davdroid/log/StringLogger.java new file mode 100644 index 00000000..46ea3aca --- /dev/null +++ b/app/src/main/java/at/bitfire/davdroid/log/StringLogger.java @@ -0,0 +1,35 @@ +/* + * 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, boolean verbose) { + this.name = name; + this.verbose = verbose; + + writer = new PrintWriter(stringWriter); + } + + public String toString() { + return stringWriter.toString(); + } + + +} diff --git a/app/src/main/java/at/bitfire/davdroid/resource/DavResourceFinder.java b/app/src/main/java/at/bitfire/davdroid/resource/DavResourceFinder.java index 4c499189..29720bcf 100644 --- a/app/src/main/java/at/bitfire/davdroid/resource/DavResourceFinder.java +++ b/app/src/main/java/at/bitfire/davdroid/resource/DavResourceFinder.java @@ -44,7 +44,6 @@ import at.bitfire.dav4android.property.CurrentUserPrivilegeSet; import at.bitfire.dav4android.property.DisplayName; import at.bitfire.dav4android.property.ResourceType; import at.bitfire.dav4android.property.SupportedCalendarComponentSet; -import at.bitfire.davdroid.Constants; import at.bitfire.davdroid.HttpClient; import lombok.NonNull; @@ -77,12 +76,16 @@ public class DavResourceFinder { httpClient = new HttpClient(log, context, serverInfo.getUserName(), serverInfo.getPassword(), serverInfo.authPreemptive); } - public void findResources() throws URISyntaxException, IOException, HttpException, DavException { - findResources(Service.CARDDAV); - findResources(Service.CALDAV); + public void findResources() { + try { + findResources(Service.CARDDAV); + findResources(Service.CALDAV); + } catch(URISyntaxException e) { + log.warn("Invalid user-given URI", e); + } } - public void findResources(Service service) throws URISyntaxException, IOException, HttpException, DavException { + public void findResources(Service service) throws URISyntaxException { URI baseURI = serverInfo.getBaseURI(); String domain = null; @@ -95,7 +98,7 @@ public class DavResourceFinder { } else if (service == Service.CARDDAV) addressbooks.clear(); - Constants.log.info("*** STARTING COLLECTION DISCOVERY FOR SERVICE " + service.name.toUpperCase() + "***"); + log.info("*** STARTING COLLECTION DISCOVERY FOR SERVICE " + service.name.toUpperCase() + "***"); if ("http".equals(baseURI.getScheme()) || "https".equals(baseURI.getScheme())) { HttpUrl userURL = HttpUrl.get(baseURI); @@ -103,7 +106,7 @@ public class DavResourceFinder { 1. user-given URL is a calendar 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 and/or has "); + log.info("Check whether user-given URL is a calendar collection and/or contains and/or has "); DavResource davBase = new DavResource(log, httpClient, userURL); try { if (service == Service.CALDAV) { @@ -122,13 +125,13 @@ public class DavResourceFinder { addIfAddressBook(davBase); } } catch (IOException|HttpException|DavException e) { - Constants.log.debug("PROPFIND on user-given URL failed", e); + log.debug("PROPFIND on user-given URL failed", e); } if (service == Service.CALDAV) { CalendarHomeSet calendarHomeSet = (CalendarHomeSet)davBase.properties.get(CalendarHomeSet.NAME); if (calendarHomeSet != null) { - Constants.log.info("Found at user-given URL"); + log.info("Found at user-given URL"); for (String href : calendarHomeSet.hrefs) { HttpUrl url = userURL.resolve(href); if (url != null) @@ -138,7 +141,7 @@ public class DavResourceFinder { } else if (service == Service.CARDDAV) { AddressbookHomeSet addressbookHomeSet = (AddressbookHomeSet) davBase.properties.get(AddressbookHomeSet.NAME); if (addressbookHomeSet != null) { - Constants.log.info("Found at user-given URL"); + log.info("Found at user-given URL"); for (String href : addressbookHomeSet.hrefs) { HttpUrl url = userURL.resolve(href); if (url != null) @@ -153,7 +156,7 @@ public class DavResourceFinder { * Keep in mind that the CalDAV principal URL must not be the CardDAV principal URL! */ if (homeSets.isEmpty()) try { - Constants.log.info("No home sets found, looking for "); + log.info("No home sets found, looking for "); davBase.options(); if ((service == Service.CALDAV && davBase.capabilities.contains("calendar-access")) || @@ -163,15 +166,15 @@ public class DavResourceFinder { principalUrl = davBase.location.resolve(currentUserPrincipal.href); } } catch(IOException|HttpException|DavException e) { - Constants.log.debug("Couldn't find at user-given URL", e); + log.debug("Couldn't find at user-given URL", e); } if (principalUrl == null) try { - Constants.log.info("User-given URL doesn't contain , trying /.well-known/" + service.name); + log.info("User-given URL doesn't contain , trying /.well-known/" + service.name); principalUrl = getCurrentUserPrincipal(userURL.resolve("/.well-known/" + service.name)); - } catch(IOException|HttpException e) { - Constants.log.debug("Couldn't determine from well-known " + service + " path", e); + } catch(IOException|HttpException|DavException e) { + log.debug("Couldn't determine from well-known " + service + " path", e); } if (principalUrl == null) @@ -190,13 +193,17 @@ public class DavResourceFinder { } if (principalUrl == null && domain != null) { - Constants.log.info("No principal URL yet, trying SRV/TXT records with domain " + domain); - principalUrl = discoverPrincipalUrl(domain, service); + log.info("No principal URL yet, trying SRV/TXT records with domain " + domain); + try { + principalUrl = discoverPrincipalUrl(domain, service); + } catch (IOException|HttpException|DavException e) { + log.info("Couldn't find principal URL using service discovery"); + } } // principal URL has been found, get addressbook-home-set/calendar-home-set if (principalUrl != null) { - Constants.log.info("Principal URL=" + principalUrl + ", getting "); + log.info("Principal URL=" + principalUrl + ", getting "); try { DavResource principal = new DavResource(log, httpClient, principalUrl); @@ -204,7 +211,7 @@ public class DavResourceFinder { principal.propfind(0, CalendarHomeSet.NAME); CalendarHomeSet calendarHomeSet = (CalendarHomeSet) principal.properties.get(CalendarHomeSet.NAME); if (calendarHomeSet != null) { - Constants.log.info("Found at principal URL"); + log.info("Found at principal URL"); for (String href : calendarHomeSet.hrefs) { HttpUrl url = principal.location.resolve(href); if (url != null) @@ -215,7 +222,7 @@ public class DavResourceFinder { principal.propfind(0, AddressbookHomeSet.NAME); AddressbookHomeSet addressbookHomeSet = (AddressbookHomeSet) principal.properties.get(AddressbookHomeSet.NAME); if (addressbookHomeSet != null) { - Constants.log.info("Found at principal URL"); + log.info("Found at principal URL"); for (String href : addressbookHomeSet.hrefs) { HttpUrl url = principal.location.resolve(href); if (url != null) @@ -225,7 +232,7 @@ public class DavResourceFinder { } } catch (IOException|HttpException|DavException e) { - Constants.log.debug("PROPFIND on " + principalUrl + " failed", e); + log.debug("PROPFIND on " + principalUrl + " failed", e); } } @@ -233,7 +240,7 @@ public class DavResourceFinder { for (HttpUrl url : homeSets) if (service == Service.CALDAV) try { - Constants.log.info("Listing calendar collections in home set " + url); + log.info("Listing calendar collections in home set " + 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); @@ -245,11 +252,11 @@ public class DavResourceFinder { for (DavResource member : homeSet.members) addIfCalendar(member); } catch (IOException | HttpException | DavException e) { - Constants.log.debug("PROPFIND on " + url + " failed", e); + log.debug("PROPFIND on " + url + " failed", e); } else if (service == Service.CARDDAV) try { - Constants.log.info("Listing address books in home set " + url); + log.info("Listing address books in home set " + url); DavResource homeSet = new DavResource(log, httpClient, url); homeSet.propfind(1, ResourceType.NAME, DisplayName.NAME, CurrentUserPrivilegeSet.NAME, AddressbookDescription.NAME); @@ -260,7 +267,7 @@ public class DavResourceFinder { for (DavResource member : homeSet.members) addIfAddressBook(member); } catch (IOException | HttpException | DavException e) { - Constants.log.debug("PROPFIND on " + url + " failed", e); + log.debug("PROPFIND on " + url + " failed", e); } // TODO notify user on errors? @@ -280,7 +287,7 @@ public class DavResourceFinder { ResourceType resourceType = (ResourceType)dav.properties.get(ResourceType.NAME); if (resourceType != null && resourceType.types.contains(ResourceType.ADDRESSBOOK)) { dav.location = UrlUtils.withTrailingSlash(dav.location); - Constants.log.info("Found address book at " + dav.location); + log.info("Found address book at " + dav.location); addressbooks.put(dav.location, resourceInfo(dav, ServerInfo.ResourceInfo.Type.ADDRESS_BOOK)); } @@ -298,7 +305,7 @@ public class DavResourceFinder { ResourceType resourceType = (ResourceType)dav.properties.get(ResourceType.NAME); if (resourceType != null && resourceType.types.contains(ResourceType.CALENDAR)) { dav.location = UrlUtils.withTrailingSlash(dav.location); - Constants.log.info("Found calendar collection at " + dav.location); + log.info("Found calendar collection at " + dav.location); boolean supportsEvents = true, supportsTasks = true; SupportedCalendarComponentSet supportedCalendarComponentSet = (SupportedCalendarComponentSet)dav.properties.get(SupportedCalendarComponentSet.NAME); @@ -376,7 +383,7 @@ public class DavResourceFinder { List paths = new LinkedList<>(); // there may be multiple paths to try final String query = "_" + service.name + "s._tcp." + domain; - Constants.log.debug("Looking up SRV records for " + query); + 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) @@ -385,7 +392,7 @@ public class DavResourceFinder { scheme = "https"; fqdn = srv.getTarget().toString(true); port = srv.getPort(); - Constants.log.info("Found " + service + " service: fqdn=" + fqdn + ", port=" + port); + 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(); @@ -394,7 +401,7 @@ public class DavResourceFinder { 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); + log.info("Found TXT record; initial context path=" + paths); break; } } @@ -413,7 +420,7 @@ public class DavResourceFinder { .encodedPath(path) .build(); - Constants.log.info("Trying to determine principal from initial context path=" + initialContextPath); + log.info("Trying to determine principal from initial context path=" + initialContextPath); HttpUrl principal = getCurrentUserPrincipal(initialContextPath); if (principal != null) return principal; @@ -434,7 +441,7 @@ public class DavResourceFinder { if (currentUserPrincipal != null && currentUserPrincipal.href != null) { HttpUrl principal = url.resolve(currentUserPrincipal.href); if (principal != null) { - Constants.log.info("Found current-user-principal: " + principal); + log.info("Found current-user-principal: " + principal); return principal; } } @@ -446,7 +453,7 @@ public class DavResourceFinder { private SRVRecord selectSRVRecord(Record[] records) { if (records.length > 1) - Constants.log.warn("Multiple SRV records not supported yet; using first one"); + log.warn("Multiple SRV records not supported yet; using first one"); return (SRVRecord)records[0]; } diff --git a/app/src/main/java/at/bitfire/davdroid/resource/ServerInfo.java b/app/src/main/java/at/bitfire/davdroid/resource/ServerInfo.java index 16ed37f3..462303c1 100644 --- a/app/src/main/java/at/bitfire/davdroid/resource/ServerInfo.java +++ b/app/src/main/java/at/bitfire/davdroid/resource/ServerInfo.java @@ -25,7 +25,7 @@ public class ServerInfo implements Serializable { final private String userName, password; final boolean authPreemptive; - private String errorMessage; + private String logs; private ResourceInfo addressBooks[] = new ResourceInfo[0], @@ -40,8 +40,12 @@ public class ServerInfo implements Serializable { return false; } + public boolean isEmpty() { + return addressBooks.length == 0 && calendars.length == 0 && taskLists.length == 0; + } - @RequiredArgsConstructor(suppressConstructorProperties=true) + + @RequiredArgsConstructor(suppressConstructorProperties=true) @Data public static class ResourceInfo implements Serializable { diff --git a/app/src/main/java/at/bitfire/davdroid/ui/DebugInfoActivity.java b/app/src/main/java/at/bitfire/davdroid/ui/DebugInfoActivity.java index bf48f8a8..b01b9277 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/DebugInfoActivity.java +++ b/app/src/main/java/at/bitfire/davdroid/ui/DebugInfoActivity.java @@ -39,6 +39,7 @@ import at.bitfire.davdroid.R; public class DebugInfoActivity extends Activity implements LoaderManager.LoaderCallbacks { public static final String KEY_EXCEPTION = "exception", + KEY_LOGS = "logs", KEY_ACCOUNT = "account", KEY_AUTHORITY = "authority", KEY_PHASE = "phase"; @@ -109,22 +110,24 @@ public class DebugInfoActivity extends Activity implements LoaderManager.LoaderC @Override public String loadInBackground() { Exception exception = null; - String authority = null; + String logs = null, + authority = null; Account account = null; - Integer phase = null; + int phase = -1; if (extras != null) { exception = (Exception)extras.getSerializable(KEY_EXCEPTION); + logs = extras.getString(KEY_LOGS); account = extras.getParcelable(KEY_ACCOUNT); authority = extras.getString(KEY_AUTHORITY); - phase = extras.getInt(KEY_PHASE); + phase = extras.getInt(KEY_PHASE, -1); } StringBuilder report = new StringBuilder(); // begin with most specific information - if (phase != null) + if (phase != -1) report.append("SYNCHRONIZATION INFO\nSynchronization phase: " + phase + "\n"); if (account != null) report.append("Account name: " + account.name + "\n"); @@ -146,6 +149,9 @@ public class DebugInfoActivity extends Activity implements LoaderManager.LoaderC report.append("\n"); } + if (logs != null) + report.append("LOGS:\n" + logs + "\n"); + try { PackageManager pm = getContext().getPackageManager(); String installedFrom = pm.getInstallerPackageName(BuildConfig.APPLICATION_ID); diff --git a/app/src/main/java/at/bitfire/davdroid/ui/setup/LoginEmailFragment.java b/app/src/main/java/at/bitfire/davdroid/ui/setup/LoginEmailFragment.java index ce9454d8..0b97120d 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/setup/LoginEmailFragment.java +++ b/app/src/main/java/at/bitfire/davdroid/ui/setup/LoginEmailFragment.java @@ -9,7 +9,6 @@ package at.bitfire.davdroid.ui.setup; import android.app.DialogFragment; import android.app.Fragment; -import android.app.FragmentTransaction; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; @@ -24,7 +23,9 @@ import android.widget.EditText; import java.net.URI; import java.net.URISyntaxException; +import at.bitfire.davdroid.Constants; import at.bitfire.davdroid.R; +import at.bitfire.davdroid.resource.ServerInfo; public class LoginEmailFragment extends Fragment implements TextWatcher { @@ -53,18 +54,22 @@ public class LoginEmailFragment extends Fragment implements TextWatcher { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.next: - FragmentTransaction ft = getFragmentManager().beginTransaction(); - - Bundle args = new Bundle(); - String email = editEmail.getText().toString(); - args.putString(QueryServerDialogFragment.EXTRA_BASE_URI, "mailto:" + email); - args.putString(QueryServerDialogFragment.EXTRA_USER_NAME, email); - args.putString(QueryServerDialogFragment.EXTRA_PASSWORD, editPassword.getText().toString()); - args.putBoolean(QueryServerDialogFragment.EXTRA_AUTH_PREEMPTIVE, true); - - DialogFragment dialog = new QueryServerDialogFragment(); - dialog.setArguments(args); - dialog.show(ft, QueryServerDialogFragment.class.getName()); + try { + String email = editEmail.getText().toString(); + Bundle args = new Bundle(); + args.putSerializable(QueryServerDialogFragment.KEY_SERVER_INFO, new ServerInfo( + new URI("mailto:" + email), + email, + editPassword.getText().toString(), + true + )); + + DialogFragment dialog = new QueryServerDialogFragment(); + dialog.setArguments(args); + dialog.show(getFragmentManager(), QueryServerDialogFragment.class.getName()); + } catch(URISyntaxException e) { + Constants.log.debug("Invalid email address", e); + } break; default: return false; diff --git a/app/src/main/java/at/bitfire/davdroid/ui/setup/LoginURLFragment.java b/app/src/main/java/at/bitfire/davdroid/ui/setup/LoginURLFragment.java index b0fd599a..cfb64184 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/setup/LoginURLFragment.java +++ b/app/src/main/java/at/bitfire/davdroid/ui/setup/LoginURLFragment.java @@ -30,7 +30,9 @@ import android.widget.TextView; import java.net.URI; import java.net.URISyntaxException; +import at.bitfire.davdroid.Constants; import at.bitfire.davdroid.R; +import at.bitfire.davdroid.resource.ServerInfo; public class LoginURLFragment extends Fragment implements TextWatcher { @@ -88,20 +90,21 @@ public class LoginURLFragment extends Fragment implements TextWatcher { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.next: - FragmentTransaction ft = getFragmentManager().beginTransaction(); - - Bundle args = new Bundle(); - try { - args.putString(QueryServerDialogFragment.EXTRA_BASE_URI, getBaseURI().toString()); - } catch (URISyntaxException e) { - } - args.putString(QueryServerDialogFragment.EXTRA_USER_NAME, editUserName.getText().toString()); - args.putString(QueryServerDialogFragment.EXTRA_PASSWORD, editPassword.getText().toString()); - args.putBoolean(QueryServerDialogFragment.EXTRA_AUTH_PREEMPTIVE, checkboxPreemptive.isChecked()); - - DialogFragment dialog = new QueryServerDialogFragment(); - dialog.setArguments(args); - dialog.show(ft, QueryServerDialogFragment.class.getName()); + try { + ServerInfo serverInfo = new ServerInfo( + getBaseURI(), + editUserName.getText().toString(), + editPassword.getText().toString(), + checkboxPreemptive.isChecked() + ); + Bundle args = new Bundle(); + args.putSerializable(QueryServerDialogFragment.KEY_SERVER_INFO, serverInfo); + DialogFragment dialog = new QueryServerDialogFragment(); + dialog.setArguments(args); + dialog.show(getFragmentManager(), null); + } catch(URISyntaxException e) { + Constants.log.debug("Invalid URI", e); + } break; default: return false; diff --git a/app/src/main/java/at/bitfire/davdroid/ui/setup/QueryServerDialogFragment.java b/app/src/main/java/at/bitfire/davdroid/ui/setup/QueryServerDialogFragment.java index 126a0471..b298d824 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/setup/QueryServerDialogFragment.java +++ b/app/src/main/java/at/bitfire/davdroid/ui/setup/QueryServerDialogFragment.java @@ -7,87 +7,126 @@ */ package at.bitfire.davdroid.ui.setup; +import android.app.AlertDialog; +import android.app.Dialog; import android.app.DialogFragment; import android.app.Fragment; import android.app.LoaderManager.LoaderCallbacks; +import android.app.ProgressDialog; import android.content.AsyncTaskLoader; import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; import android.content.Loader; import android.os.Bundle; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.Toast; -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; - -import at.bitfire.dav4android.exception.DavException; -import at.bitfire.dav4android.exception.HttpException; -import at.bitfire.davdroid.Constants; import at.bitfire.davdroid.R; +import at.bitfire.davdroid.log.StringLogger; import at.bitfire.davdroid.resource.DavResourceFinder; import at.bitfire.davdroid.resource.LocalTaskList; import at.bitfire.davdroid.resource.ServerInfo; +import at.bitfire.davdroid.ui.DebugInfoActivity; public class QueryServerDialogFragment extends DialogFragment implements LoaderCallbacks { - private static final String TAG = "davdroid.QueryServer"; - public static final String - EXTRA_BASE_URI = "base_uri", - EXTRA_USER_NAME = "user_name", - EXTRA_PASSWORD = "password", - EXTRA_AUTH_PREEMPTIVE = "auth_preemptive"; + public static final String KEY_SERVER_INFO = "server_info"; - @Override + public static QueryServerDialogFragment newInstance(ServerInfo serverInfo) { + Bundle args = new Bundle(); + args.putSerializable(KEY_SERVER_INFO, serverInfo); + QueryServerDialogFragment fragment = new QueryServerDialogFragment(); + fragment.setArguments(args); + return fragment; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + ProgressDialog dialog = new ProgressDialog(getActivity()); + dialog.setCancelable(false); + dialog.setTitle(R.string.setup_resource_detection); + dialog.setIndeterminate(true); + dialog.setMessage(getString(R.string.setup_querying_server)); + return dialog; + } + + +@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setStyle(DialogFragment.STYLE_NO_TITLE, android.R.style.Theme_Holo_Light_Dialog); - setCancelable(false); Loader loader = getLoaderManager().initLoader(0, getArguments(), this); - if (savedInstanceState == null) // http://code.google.com/p/android/issues/detail?id=14944 - loader.forceLoad(); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.setup_query_server, container, false); } @Override public Loader onCreateLoader(int id, Bundle args) { - Log.i(TAG, "onCreateLoader"); return new ServerInfoLoader(getActivity(), args); } @Override public void onLoadFinished(Loader loader, ServerInfo serverInfo) { - if (serverInfo.getErrorMessage() != null) - Toast.makeText(getActivity(), serverInfo.getErrorMessage(), Toast.LENGTH_LONG).show(); - else { - ((AddAccountActivity)getActivity()).serverInfo = serverInfo; + if (serverInfo.isEmpty()) { + // resource detection didn't find anything + getFragmentManager().beginTransaction() + .add(NothingDetectedFragment.newInstance(serverInfo.getLogs()), null) + .commitAllowingStateLoss(); - Fragment nextFragment; - if (serverInfo.getTaskLists().length > 0 && !LocalTaskList.tasksProviderAvailable(getActivity().getContentResolver())) - nextFragment = new InstallAppsFragment(); - else - nextFragment = new SelectCollectionsFragment(); + } else { + ((AddAccountActivity) getActivity()).serverInfo = serverInfo; + + // resource detection brought some results + Fragment nextFragment; + if (serverInfo.getTaskLists().length > 0 && !LocalTaskList.tasksProviderAvailable(getActivity().getContentResolver())) + nextFragment = new InstallAppsFragment(); + else + nextFragment = new SelectCollectionsFragment(); + + getFragmentManager().beginTransaction() + .replace(R.id.right_pane, nextFragment) + .addToBackStack(null) + .commitAllowingStateLoss(); + } - getFragmentManager().beginTransaction() - .replace(R.id.right_pane, nextFragment) - .addToBackStack(null) - .commitAllowingStateLoss(); - } - getDialog().dismiss(); } @Override public void onLoaderReset(Loader arg0) { } - + + + public static class NothingDetectedFragment extends DialogFragment { + private static String KEY_LOGS = "logs"; + + public static NothingDetectedFragment newInstance(String logs) { + Bundle args = new Bundle(); + args.putString(KEY_LOGS, logs); + NothingDetectedFragment fragment = new NothingDetectedFragment(); + fragment.setArguments(args); + return fragment; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + return new AlertDialog.Builder(getActivity()) + .setTitle(R.string.setup_resource_detection) + .setIcon(android.R.drawable.ic_dialog_info) + .setMessage(R.string.setup_no_collections_found) + .setNeutralButton(R.string.setup_view_logs, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent intent = new Intent(getActivity(), DebugInfoActivity.class); + intent.putExtra(DebugInfoActivity.KEY_LOGS, getArguments().getString(KEY_LOGS)); + startActivity(intent); + } + }) + .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + // dismiss + } + }) + .create(); + } + } static class ServerInfoLoader extends AsyncTaskLoader { private static final String TAG = "davdroid.ServerInfoLoader"; @@ -100,36 +139,22 @@ public class QueryServerDialogFragment extends DialogFragment implements LoaderC this.args = args; } - @Override + @Override + protected void onStartLoading() { + forceLoad(); + } + + @Override public ServerInfo loadInBackground() { - ServerInfo serverInfo = new ServerInfo( - URI.create(args.getString(EXTRA_BASE_URI)), - args.getString(EXTRA_USER_NAME), - args.getString(EXTRA_PASSWORD), - args.getBoolean(EXTRA_AUTH_PREEMPTIVE) - ); - - try { - DavResourceFinder finder = new DavResourceFinder(null, context, serverInfo); - finder.findResources(); - } catch (URISyntaxException e) { - serverInfo.setErrorMessage(getContext().getString(R.string.exception_uri_syntax, e.getMessage())); - } catch (IOException e) { - // general message - serverInfo.setErrorMessage(getContext().getString(R.string.exception_io, e.getLocalizedMessage())); - // overwrite by more specific message, if possible - /*if (ExceptionUtils.indexOfType(e, CertPathValidatorException.class) != -1) - serverInfo.setErrorMessage(getContext().getString(R.string.exception_cert_path_validation, e.getMessage()));*/ - } catch (HttpException e) { - Constants.log.error("HTTP error while querying server info", e); - serverInfo.setErrorMessage(getContext().getString(R.string.exception_http, e.getLocalizedMessage())); - } catch (DavException e) { - Constants.log.error("DAV error while querying server info", e); - serverInfo.setErrorMessage(getContext().getString(R.string.exception_incapable_resource, e.getLocalizedMessage())); - } - + ServerInfo serverInfo = (ServerInfo)args.getSerializable(KEY_SERVER_INFO); + + StringLogger logger = new StringLogger("DavResourceFinder", true); + DavResourceFinder finder = new DavResourceFinder(logger, context, serverInfo); + finder.findResources(); + + serverInfo.setLogs(logger.toString()); return serverInfo; } - } + } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f8591ffc..c214c8a1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -169,8 +169,10 @@ Settings have been updated Internal settings have been updated. If there are problems, please delete your DAVdroid accounts and add them again. + Resource detection + No address books or calendars were found. + View logs DAVdroid: Select collections - No CalDAV-/CardDAV service is available at this location. Add account Querying server. Please wait… Plain Android doesn\'t support to-do lists (in contrast to contacts and calendars). diff --git a/scripts/fetch-translations.sh b/scripts/fetch-translations.sh new file mode 100755 index 00000000..74d5a107 --- /dev/null +++ b/scripts/fetch-translations.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +declare -A android +android=([ca]=ca [cs]=cs [de]=de [es]=es [fr]=fr [hu]=hu [nl]=nl [pl]=pl [pt]=pt [ru]=ru [sr]=sr [uk]=uk [zh_CN]=zh-rcn) + +for lang in ${!android[@]} +do + target=../app/src/main/res/values-${android[$lang]} + mkdir -p $target + curl -n "https://www.transifex.com/api/2/project/davdroid/resource/davdroid/translation/$lang?file" >$target/strings.xml +done