Commit 2f704b11 authored by Ricki Hirner's avatar Ricki Hirner 🐑

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
parent c37c219a
......@@ -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()"
}
......
......@@ -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
......
......@@ -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);
}
}
/*
* 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();
}
}
......@@ -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 {
......
......@@ -39,6 +39,7 @@ import at.bitfire.davdroid.R;
public class DebugInfoActivity extends Activity implements LoaderManager.LoaderCallbacks<String> {
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);
......
......@@ -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;
......
......@@ -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;
......
......@@ -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<ServerInfo> {
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<ServerInfo> 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<ServerInfo> onCreateLoader(int id, Bundle args) {
Log.i(TAG, "onCreateLoader");
return new ServerInfoLoader(getActivity(), args);
}
@Override
public void onLoadFinished(Loader<ServerInfo> loader, ServerInfo serverInfo) {
if (serverInfo.getErrorMessage() != null)
Toast.makeText(getActivity(), serverInfo.getErrorMessage(), Toast.LENGTH_LONG).show();
else {
((AddAccountActivity)getActivity()).serverInfo = serverInfo;
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();
}
if (serverInfo.isEmpty()) {
// resource detection didn't find anything
getFragmentManager().beginTransaction()
.add(NothingDetectedFragment.newInstance(serverInfo.getLogs()), null)
.commitAllowingStateLoss();
} 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();
}
getDialog().dismiss();
}
@Override
public void onLoaderReset(Loader<ServerInfo> 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<ServerInfo> {
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;
}
}
}
......@@ -169,8 +169,10 @@
<string name="settings_version_update_title">Settings have been updated</string>
<string name="settings_version_update_description">Internal settings have been updated. If there are problems, please delete your DAVdroid accounts and add them again.</string>
<string name="setup_resource_detection">Resource detection</string>
<string name="setup_no_collections_found">No address books or calendars were found.</string>
<string name="setup_view_logs">View logs</string>
<string name="setup_select_collections">DAVdroid: Select collections</string>
<string name="setup_neither_caldav_nor_carddav">No CalDAV-/CardDAV service is available at this location.</string>
<string name="setup_add_account">Add account</string>
<string name="setup_querying_server">Querying server. Please wait…</string>
<string name="setup_install_apps_info">Plain Android doesn\'t support to-do lists (in contrast to contacts and calendars).</string>
......
#!/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
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment