From 6ac5fe020490c104d7422a42854e556d83d74f96 Mon Sep 17 00:00:00 2001 From: Ricki Hirner Date: Tue, 23 Feb 2016 23:10:44 +0100 Subject: [PATCH] Show debug info on management errors --- .../davdroid/ui/CreateCalendarActivity.java | 13 +++ .../davdroid/ui/CreateCollectionFragment.java | 79 +++++++++++++------ .../davdroid/ui/DebugInfoActivity.java | 12 +-- .../davdroid/ui/DeleteCollectionFragment.java | 51 ++++++------ .../davdroid/ui/ExceptionInfoFragment.java | 74 +++++++++++++++++ app/src/main/res/values/strings.xml | 6 ++ 6 files changed, 181 insertions(+), 54 deletions(-) create mode 100644 app/src/main/java/at/bitfire/davdroid/ui/ExceptionInfoFragment.java diff --git a/app/src/main/java/at/bitfire/davdroid/ui/CreateCalendarActivity.java b/app/src/main/java/at/bitfire/davdroid/ui/CreateCalendarActivity.java index f2983b2c..7b799b7c 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/CreateCalendarActivity.java +++ b/app/src/main/java/at/bitfire/davdroid/ui/CreateCalendarActivity.java @@ -10,11 +10,13 @@ package at.bitfire.davdroid.ui; import android.accounts.Account; import android.content.Context; +import android.content.Intent; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.support.v4.app.LoaderManager; +import android.support.v4.app.NavUtils; import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.Loader; import android.support.v7.app.AppCompatActivity; @@ -96,6 +98,17 @@ public class CreateCalendarActivity extends AppCompatActivity implements LoaderM return true; } + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + Intent intent = new Intent(this, AccountActivity.class); + intent.putExtra(AccountActivity.EXTRA_ACCOUNT_NAME, account.name); + NavUtils.navigateUpTo(this, intent); + return true; + } + return false; + } + public void onCreateCalendar(MenuItem item) { boolean ok = true; CollectionInfo info = new CollectionInfo(); diff --git a/app/src/main/java/at/bitfire/davdroid/ui/CreateCollectionFragment.java b/app/src/main/java/at/bitfire/davdroid/ui/CreateCollectionFragment.java index 58e50e62..1365fb5c 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/CreateCollectionFragment.java +++ b/app/src/main/java/at/bitfire/davdroid/ui/CreateCollectionFragment.java @@ -13,14 +13,16 @@ import android.app.Activity; import android.app.Dialog; import android.app.ProgressDialog; import android.content.ContentValues; +import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.NonNull; -import android.support.design.widget.Snackbar; import android.support.v4.app.DialogFragment; -import android.widget.Toast; +import android.support.v4.app.LoaderManager; +import android.support.v4.content.AsyncTaskLoader; +import android.support.v4.content.Loader; import org.apache.commons.lang3.BooleanUtils; import org.xmlpull.v1.XmlSerializer; @@ -38,16 +40,18 @@ import at.bitfire.davdroid.R; import at.bitfire.davdroid.model.CollectionInfo; import at.bitfire.davdroid.model.ServiceDB; import at.bitfire.davdroid.syncadapter.AccountSettings; -import at.bitfire.ical4android.DateUtils; import lombok.Cleanup; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; -public class CreateCollectionFragment extends DialogFragment { +public class CreateCollectionFragment extends DialogFragment implements LoaderManager.LoaderCallbacks { private static final String ARG_ACCOUNT = "account", ARG_COLLECTION_INFO = "collectionInfo"; + protected Account account; + protected CollectionInfo info; + public static CreateCollectionFragment newInstance(Account account, CollectionInfo info) { CreateCollectionFragment frag = new CreateCollectionFragment(); Bundle args = new Bundle(2); @@ -60,9 +64,11 @@ public class CreateCollectionFragment extends DialogFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setRetainInstance(true); - new CreateCollectionTask().execute(getArguments()); + account = getArguments().getParcelable(ARG_ACCOUNT); + info = (CollectionInfo)getArguments().getSerializable(ARG_COLLECTION_INFO); + + getLoaderManager().initLoader(0, null, this); } @NonNull @@ -77,12 +83,50 @@ public class CreateCollectionFragment extends DialogFragment { } - protected class CreateCollectionTask extends AsyncTask { + @Override + public Loader onCreateLoader(int id, Bundle args) { + return new CreateCollectionLoader(getContext(), account, info); + } + + @Override + public void onLoadFinished(Loader loader, Exception exception) { + dismissAllowingStateLoss(); + + Activity parent = getActivity(); + if (parent != null) { + if (exception != null) + getFragmentManager().beginTransaction() + .add(ExceptionInfoFragment.newInstance(exception, account), null) + .commitAllowingStateLoss(); + else + parent.finish(); + } + + } + + @Override + public void onLoaderReset(Loader loader) { + } + + + protected static class CreateCollectionLoader extends AsyncTaskLoader { + final Account account; + final CollectionInfo info; + + public CreateCollectionLoader(Context context, Account account, CollectionInfo info) { + super(context); + this.account = account; + this.info = info; + } + @Override - protected Exception doInBackground(Bundle... params) { - Bundle args = params[0]; - Account account = args.getParcelable(ARG_ACCOUNT); - CollectionInfo info = (CollectionInfo)args.getSerializable(ARG_COLLECTION_INFO); + protected void onStartLoading() { + forceLoad(); + } + + @Override + public Exception loadInBackground() { + Constants.log.info("MKCOl !!!!"); OkHttpClient client = HttpClient.create(getContext()); client = HttpClient.addAuthentication(client, new AccountSettings(getContext(), account)); @@ -204,19 +248,6 @@ public class CreateCollectionFragment extends DialogFragment { return null; } - - @Override - protected void onPostExecute(Exception e) { - dismissAllowingStateLoss(); - - Activity parent = getActivity(); - if (parent != null) { - if (e != null) - Toast.makeText(parent, e.getLocalizedMessage(), Toast.LENGTH_LONG).show(); - else - parent.finish(); - } - } } } 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 91f7174a..5d19e318 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/DebugInfoActivity.java +++ b/app/src/main/java/at/bitfire/davdroid/ui/DebugInfoActivity.java @@ -161,25 +161,25 @@ public class DebugInfoActivity extends AppCompatActivity implements LoaderManage if (account != null) report.append("Account name: " + account.name + "\n"); if (authority != null) - report.append("Authority: " + authority + "\n\n"); + report.append("Authority: " + authority + "\n"); if (exception instanceof HttpException) { HttpException http = (HttpException)exception; if (http.request != null) - report.append("HTTP REQUEST:\n" + http.request + "\n\n"); + report.append("\nHTTP REQUEST:\n" + http.request + "\n\n"); if (http.response != null) - report.append("HTTP RESPONSE:\n" + http.response + "\n\n"); + report.append("HTTP RESPONSE:\n" + http.response + "\n"); } if (exception != null) { - report.append("STACK TRACE:\n"); + report.append("\nSTACK TRACE:\n"); for (String stackTrace : ExceptionUtils.getRootCauseStackTrace(exception)) report.append(stackTrace + "\n"); report.append("\n"); } if (logs != null) - report.append("LOGS:\n" + logs + "\n"); + report.append("\nLOGS:\n" + logs + "\n"); try { PackageManager pm = getContext().getPackageManager(); @@ -191,7 +191,7 @@ public class DebugInfoActivity extends AppCompatActivity implements LoaderManage workaroundInstalled = pm.getPackageInfo("at.bitfire.davdroid.jbworkaround", 0) != null; } catch(PackageManager.NameNotFoundException e) {} report.append( - "SOFTWARE INFORMATION\n" + + "\nSOFTWARE INFORMATION\n" + "DAVdroid version: " + BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ") " + new Date(BuildConfig.buildTime) + "\n" + "Installed from: " + installedFrom + "\n" + "JB Workaround installed: " + (workaroundInstalled ? "yes" : "no") + "\n\n" diff --git a/app/src/main/java/at/bitfire/davdroid/ui/DeleteCollectionFragment.java b/app/src/main/java/at/bitfire/davdroid/ui/DeleteCollectionFragment.java index 82ab838c..3b6eec27 100644 --- a/app/src/main/java/at/bitfire/davdroid/ui/DeleteCollectionFragment.java +++ b/app/src/main/java/at/bitfire/davdroid/ui/DeleteCollectionFragment.java @@ -9,6 +9,7 @@ package at.bitfire.davdroid.ui; import android.accounts.Account; +import android.app.Activity; import android.app.Dialog; import android.app.ProgressDialog; import android.content.Context; @@ -22,7 +23,6 @@ import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.Loader; import android.support.v7.app.AlertDialog; import android.text.TextUtils; -import android.widget.Toast; import java.io.IOException; @@ -41,9 +41,13 @@ public class DeleteCollectionFragment extends DialogFragment implements LoaderMa ARG_ACCOUNT = "account", ARG_COLLECTION_INFO = "collectionInfo"; + protected Account account; + protected CollectionInfo collectionInfo; + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getLoaderManager().initLoader(0, getArguments(), this); } @@ -61,42 +65,41 @@ public class DeleteCollectionFragment extends DialogFragment implements LoaderMa @Override public Loader onCreateLoader(int id, Bundle args) { - CollectionInfo collectionInfo = (CollectionInfo)args.getSerializable(ARG_COLLECTION_INFO); - return new DeleteCollectionLoader( - getContext(), - (Account)args.getParcelable(ARG_ACCOUNT), - collectionInfo.id, - HttpUrl.parse(collectionInfo.url) - ); + account = args.getParcelable(ARG_ACCOUNT); + collectionInfo = (CollectionInfo)args.getSerializable(ARG_COLLECTION_INFO); + return new DeleteCollectionLoader(getContext(), account, collectionInfo); } @Override - public void onLoadFinished(Loader loader, Exception exception) { - if (exception != null) - Toast.makeText(getContext(), exception.getLocalizedMessage(), Toast.LENGTH_LONG).show(); + public void onLoadFinished(Loader loader, Exception exception) { dismissAllowingStateLoss(); - AccountActivity activity = (AccountActivity)getActivity(); - activity.reload(); + if (exception != null) + getFragmentManager().beginTransaction() + .add(ExceptionInfoFragment.newInstance(exception, account), null) + .commitAllowingStateLoss(); + else { + Activity activity = getActivity(); + if (activity instanceof AccountActivity) + ((AccountActivity)activity).reload(); + } } @Override - public void onLoaderReset(Loader loader) { + public void onLoaderReset(Loader loader) { } - private static class DeleteCollectionLoader extends AsyncTaskLoader { + + protected static class DeleteCollectionLoader extends AsyncTaskLoader { final Account account; - final long collectionId; - final HttpUrl url; + final CollectionInfo collectionInfo; final ServiceDB.OpenHelper dbHelper; - public DeleteCollectionLoader(Context context, Account account, long collectionId, HttpUrl url) { + public DeleteCollectionLoader(Context context, Account account, CollectionInfo collectionInfo) { super(context); this.account = account; - this.collectionId = collectionId; - this.url = url; - - dbHelper = new ServiceDB.OpenHelper(context); + this.collectionInfo = collectionInfo; + dbHelper = new ServiceDB.OpenHelper(getContext()); } @Override @@ -109,14 +112,14 @@ public class DeleteCollectionFragment extends DialogFragment implements LoaderMa OkHttpClient httpClient = HttpClient.create(getContext()); httpClient = HttpClient.addAuthentication(httpClient, new AccountSettings(getContext(), account)); - DavResource collection = new DavResource(null, httpClient, url); + DavResource collection = new DavResource(null, httpClient, HttpUrl.parse(collectionInfo.url)); try { // delete collection from server collection.delete(null); // delete collection locally SQLiteDatabase db = dbHelper.getWritableDatabase(); - db.delete(ServiceDB.Collections._TABLE, ServiceDB.Collections.ID + "=?", new String[] { String.valueOf(collectionId) }); + db.delete(ServiceDB.Collections._TABLE, ServiceDB.Collections.ID + "=?", new String[] { String.valueOf(collectionInfo.id) }); return null; } catch (IOException|HttpException e) { diff --git a/app/src/main/java/at/bitfire/davdroid/ui/ExceptionInfoFragment.java b/app/src/main/java/at/bitfire/davdroid/ui/ExceptionInfoFragment.java new file mode 100644 index 00000000..f9dab456 --- /dev/null +++ b/app/src/main/java/at/bitfire/davdroid/ui/ExceptionInfoFragment.java @@ -0,0 +1,74 @@ +/* + * 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.ui; + +import android.accounts.Account; +import android.app.Dialog; +import android.content.DialogInterface; +import android.content.Intent; +import android.os.Bundle; +import android.support.v4.app.DialogFragment; +import android.support.v7.app.AlertDialog; + +import java.io.IOException; + +import at.bitfire.dav4android.exception.HttpException; +import at.bitfire.davdroid.R; +import lombok.NonNull; + +public class ExceptionInfoFragment extends DialogFragment { + protected static final String + ARG_ACCOUNT = "account", + ARG_EXCEPTION = "exception"; + + public static ExceptionInfoFragment newInstance(@NonNull Exception exception, Account account) { + ExceptionInfoFragment frag = new ExceptionInfoFragment(); + Bundle args = new Bundle(1); + args.putSerializable(ARG_EXCEPTION, exception); + args.putParcelable(ARG_ACCOUNT, account); + frag.setArguments(args); + return frag; + } + + @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Bundle args = getArguments(); + final Exception exception = (Exception)args.getSerializable(ARG_EXCEPTION); + final Account account = args.getParcelable(ARG_ACCOUNT); + + int title = R.string.exception; + if (exception instanceof HttpException) + title = R.string.exception_httpexception; + else if (exception instanceof IOException) + title = R.string.exception_ioexception; + + Dialog dialog = new AlertDialog.Builder(getContext()) + .setIcon(R.drawable.ic_error_dark) + .setTitle(title) + .setMessage(exception.getClass().getCanonicalName() + "\n" + exception.getLocalizedMessage()) + .setPositiveButton(R.string.exception_show_details, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + Intent intent = new Intent(getContext(), DebugInfoActivity.class); + intent.putExtra(DebugInfoActivity.KEY_EXCEPTION, exception); + if (account != null) + intent.putExtra(DebugInfoActivity.KEY_ACCOUNT, account); + startActivity(intent); + } + }) + .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + } + }) + .create(); + setCancelable(false); + return dialog; + } +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 19ed8a6e..4490d844 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -179,6 +179,12 @@ This collection (%s) and all its entries from the server will be removed from the server. Deleting collection + + An error has occurred. + An HTTP error has occurred. + An I/O error has occurred. + Show details + Android version update Android version updates may have an impact on how DAVdroid works. If there are problems, please delete your DAVdroid accounts and add them again. Settings have been updated