mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
Allow user to send bug report from settings screen
This commit is contained in:
@@ -112,6 +112,7 @@ public class MainTest
|
|||||||
Activity.RESULT_OK, new Intent());
|
Activity.RESULT_OK, new Intent());
|
||||||
|
|
||||||
intending(hasAction(equalTo(Intent.ACTION_SEND))).respondWith(okResult);
|
intending(hasAction(equalTo(Intent.ACTION_SEND))).respondWith(okResult);
|
||||||
|
intending(hasAction(equalTo(Intent.ACTION_SENDTO))).respondWith(okResult);
|
||||||
intending(hasAction(equalTo(Intent.ACTION_VIEW))).respondWith(okResult);
|
intending(hasAction(equalTo(Intent.ACTION_VIEW))).respondWith(okResult);
|
||||||
|
|
||||||
skipTutorial();
|
skipTutorial();
|
||||||
@@ -343,4 +344,16 @@ public class MainTest
|
|||||||
onData(isPreferenceWithText("Export as CSV")).perform(click());
|
onData(isPreferenceWithText("Export as CSV")).perform(click());
|
||||||
intended(hasAction(Intent.ACTION_SEND));
|
intended(hasAction(Intent.ACTION_SEND));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* User opens the settings and generates a bug report.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGenerateBugReport()
|
||||||
|
{
|
||||||
|
openActionBarOverflowOrOptionsMenu(targetContext);
|
||||||
|
onView(withText(R.string.settings)).perform(click());
|
||||||
|
onData(isPreferenceWithText("Generate bug report")).perform(click());
|
||||||
|
intended(hasAction(Intent.ACTION_SENDTO));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.unit;
|
||||||
|
|
||||||
|
import android.support.test.runner.AndroidJUnit4;
|
||||||
|
import android.test.suitebuilder.annotation.SmallTest;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.HabitsApplication;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
import static org.hamcrest.Matchers.containsString;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@SmallTest
|
||||||
|
public class HabitsApplicationTest
|
||||||
|
{
|
||||||
|
@Test
|
||||||
|
public void getLogcat() throws IOException
|
||||||
|
{
|
||||||
|
String msg = "LOGCAT TEST";
|
||||||
|
new RuntimeException(msg).printStackTrace();
|
||||||
|
|
||||||
|
String log = HabitsApplication.getLogcat();
|
||||||
|
assertThat(log, containsString(msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,7 +29,7 @@ import org.isoron.uhabits.commands.Command;
|
|||||||
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
|
||||||
abstract public class ReplayableActivity extends Activity
|
abstract public class BaseActivity extends Activity implements Thread.UncaughtExceptionHandler
|
||||||
{
|
{
|
||||||
private static int MAX_UNDO_LEVEL = 15;
|
private static int MAX_UNDO_LEVEL = 15;
|
||||||
|
|
||||||
@@ -37,11 +37,16 @@ abstract public class ReplayableActivity extends Activity
|
|||||||
private LinkedList<Command> redoList;
|
private LinkedList<Command> redoList;
|
||||||
private Toast toast;
|
private Toast toast;
|
||||||
|
|
||||||
|
Thread.UncaughtExceptionHandler androidExceptionHandler;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState)
|
protected void onCreate(Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
androidExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||||
|
Thread.setDefaultUncaughtExceptionHandler(this);
|
||||||
|
|
||||||
undoList = new LinkedList<>();
|
undoList = new LinkedList<>();
|
||||||
redoList = new LinkedList<>();
|
redoList = new LinkedList<>();
|
||||||
}
|
}
|
||||||
@@ -103,7 +108,7 @@ abstract public class ReplayableActivity extends Activity
|
|||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Void aVoid)
|
protected void onPostExecute(Void aVoid)
|
||||||
{
|
{
|
||||||
ReplayableActivity.this.onPostExecuteCommand(refreshKey);
|
BaseActivity.this.onPostExecuteCommand(refreshKey);
|
||||||
BackupManager.dataChanged("org.isoron.uhabits");
|
BackupManager.dataChanged("org.isoron.uhabits");
|
||||||
}
|
}
|
||||||
}.execute();
|
}.execute();
|
||||||
@@ -115,4 +120,23 @@ abstract public class ReplayableActivity extends Activity
|
|||||||
public void onPostExecuteCommand(Long refreshKey)
|
public void onPostExecuteCommand(Long refreshKey)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uncaughtException(Thread thread, Throwable ex)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ex.printStackTrace();
|
||||||
|
HabitsApplication.generateLogFile();
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
// ignored
|
||||||
|
}
|
||||||
|
|
||||||
|
if(androidExceptionHandler != null)
|
||||||
|
androidExceptionHandler.uncaughtException(thread, ex);
|
||||||
|
else
|
||||||
|
System.exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -21,13 +21,21 @@ package org.isoron.uhabits;
|
|||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
import com.activeandroid.ActiveAndroid;
|
import com.activeandroid.ActiveAndroid;
|
||||||
|
|
||||||
import org.isoron.uhabits.helpers.DatabaseHelper;
|
import org.isoron.uhabits.helpers.DatabaseHelper;
|
||||||
|
import org.isoron.uhabits.helpers.DateHelper;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
public class HabitsApplication extends Application
|
public class HabitsApplication extends Application
|
||||||
{
|
{
|
||||||
@@ -76,4 +84,68 @@ public class HabitsApplication extends Application
|
|||||||
ActiveAndroid.dispose();
|
ActiveAndroid.dispose();
|
||||||
super.onTerminate();
|
super.onTerminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getLogcat() throws IOException
|
||||||
|
{
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
String[] command = new String[] { "logcat", "-d"};
|
||||||
|
Process process = Runtime.getRuntime().exec(command);
|
||||||
|
|
||||||
|
InputStreamReader in = new InputStreamReader(process.getInputStream());
|
||||||
|
BufferedReader bufferedReader = new BufferedReader(in);
|
||||||
|
|
||||||
|
String line;
|
||||||
|
while ((line = bufferedReader.readLine()) != null)
|
||||||
|
{
|
||||||
|
builder.append(line);
|
||||||
|
builder.append('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getDeviceInfo()
|
||||||
|
{
|
||||||
|
if(context == null) return "";
|
||||||
|
|
||||||
|
StringBuilder b = new StringBuilder();
|
||||||
|
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||||
|
|
||||||
|
b.append(String.format("App Version Name: %s\n", BuildConfig.VERSION_NAME));
|
||||||
|
b.append(String.format("App Version Code: %s\n", BuildConfig.VERSION_CODE));
|
||||||
|
b.append(String.format("OS Version: %s (%s)\n", System.getProperty("os.version"),
|
||||||
|
android.os.Build.VERSION.INCREMENTAL));
|
||||||
|
b.append(String.format("OS API Level: %s\n", android.os.Build.VERSION.SDK));
|
||||||
|
b.append(String.format("Device: %s\n", android.os.Build.DEVICE));
|
||||||
|
b.append(String.format("Model (Product): %s (%s)\n", android.os.Build.MODEL,
|
||||||
|
android.os.Build.PRODUCT));
|
||||||
|
b.append(String.format("Manufacturer: %s\n", android.os.Build.MANUFACTURER));
|
||||||
|
b.append(String.format("Other tags: %s\n", android.os.Build.TAGS));
|
||||||
|
b.append(String.format("Screen Width: %s\n", wm.getDefaultDisplay().getWidth()));
|
||||||
|
b.append(String.format("Screen Height: %s\n", wm.getDefaultDisplay().getHeight()));
|
||||||
|
b.append(String.format("SD Card state: %s\n\n", Environment.getExternalStorageState()));
|
||||||
|
|
||||||
|
return b.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static File generateLogFile() throws IOException
|
||||||
|
{
|
||||||
|
String logcat = getLogcat();
|
||||||
|
String deviceInfo = getDeviceInfo();
|
||||||
|
String date = DateHelper.getBackupDateFormat().format(DateHelper.getLocalTime());
|
||||||
|
|
||||||
|
if(context == null) throw new RuntimeException("application context should not be null");
|
||||||
|
File dir = DatabaseHelper.getFilesDir("Logs");
|
||||||
|
if (dir == null) throw new IOException("log dir should not be null");
|
||||||
|
|
||||||
|
File logFile = new File(String.format("%s/Log %s.txt", dir.getPath(), date));
|
||||||
|
FileWriter output = new FileWriter(logFile);
|
||||||
|
output.write(deviceInfo);
|
||||||
|
output.write(logcat);
|
||||||
|
output.close();
|
||||||
|
|
||||||
|
return logFile;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,6 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits;
|
package org.isoron.uhabits;
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.appwidget.AppWidgetManager;
|
import android.appwidget.AppWidgetManager;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
@@ -33,8 +32,6 @@ import android.os.AsyncTask;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.v4.app.ActivityCompat;
|
|
||||||
import android.support.v4.content.ContextCompat;
|
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
@@ -50,7 +47,10 @@ import org.isoron.uhabits.widgets.HistoryWidgetProvider;
|
|||||||
import org.isoron.uhabits.widgets.ScoreWidgetProvider;
|
import org.isoron.uhabits.widgets.ScoreWidgetProvider;
|
||||||
import org.isoron.uhabits.widgets.StreakWidgetProvider;
|
import org.isoron.uhabits.widgets.StreakWidgetProvider;
|
||||||
|
|
||||||
public class MainActivity extends ReplayableActivity
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class MainActivity extends BaseActivity
|
||||||
implements ListHabitsFragment.OnHabitClickListener
|
implements ListHabitsFragment.OnHabitClickListener
|
||||||
{
|
{
|
||||||
private ListHabitsFragment listHabitsFragment;
|
private ListHabitsFragment listHabitsFragment;
|
||||||
@@ -63,6 +63,7 @@ public class MainActivity extends ReplayableActivity
|
|||||||
public static final int RESULT_IMPORT_DATA = 1;
|
public static final int RESULT_IMPORT_DATA = 1;
|
||||||
public static final int RESULT_EXPORT_CSV = 2;
|
public static final int RESULT_EXPORT_CSV = 2;
|
||||||
public static final int RESULT_EXPORT_DB = 3;
|
public static final int RESULT_EXPORT_DB = 3;
|
||||||
|
public static final int RESULT_BUG_REPORT = 4;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState)
|
protected void onCreate(Bundle savedInstanceState)
|
||||||
@@ -172,6 +173,29 @@ public class MainActivity extends ReplayableActivity
|
|||||||
case RESULT_EXPORT_DB:
|
case RESULT_EXPORT_DB:
|
||||||
listHabitsFragment.exportDB();
|
listHabitsFragment.exportDB();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case RESULT_BUG_REPORT:
|
||||||
|
generateBugReport();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateBugReport()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
File logFile = HabitsApplication.generateLogFile();
|
||||||
|
|
||||||
|
Intent intent = new Intent();
|
||||||
|
intent.setAction(Intent.ACTION_SENDTO);
|
||||||
|
intent.setData(Uri.parse(getString(R.string.bugReportURL)));
|
||||||
|
intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(logFile));
|
||||||
|
startActivity(intent);
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
e.printStackTrace();
|
||||||
|
showToast(R.string.bug_report_failed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ import android.support.v4.content.LocalBroadcastManager;
|
|||||||
import org.isoron.uhabits.fragments.ShowHabitFragment;
|
import org.isoron.uhabits.fragments.ShowHabitFragment;
|
||||||
import org.isoron.uhabits.models.Habit;
|
import org.isoron.uhabits.models.Habit;
|
||||||
|
|
||||||
public class ShowHabitActivity extends ReplayableActivity
|
public class ShowHabitActivity extends BaseActivity
|
||||||
{
|
{
|
||||||
|
|
||||||
public Habit habit;
|
public Habit habit;
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import com.android.colorpicker.ColorPickerDialog;
|
|||||||
import com.android.colorpicker.ColorPickerSwatch;
|
import com.android.colorpicker.ColorPickerSwatch;
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.ReplayableActivity;
|
import org.isoron.uhabits.BaseActivity;
|
||||||
import org.isoron.uhabits.commands.ArchiveHabitsCommand;
|
import org.isoron.uhabits.commands.ArchiveHabitsCommand;
|
||||||
import org.isoron.uhabits.commands.ChangeHabitColorCommand;
|
import org.isoron.uhabits.commands.ChangeHabitColorCommand;
|
||||||
import org.isoron.uhabits.commands.DeleteHabitsCommand;
|
import org.isoron.uhabits.commands.DeleteHabitsCommand;
|
||||||
@@ -47,7 +47,7 @@ public class HabitSelectionCallback implements ActionMode.Callback
|
|||||||
{
|
{
|
||||||
private HabitListLoader loader;
|
private HabitListLoader loader;
|
||||||
private List<Integer> selectedPositions;
|
private List<Integer> selectedPositions;
|
||||||
private ReplayableActivity activity;
|
private BaseActivity activity;
|
||||||
private Listener listener;
|
private Listener listener;
|
||||||
private DialogHelper.OnSavedListener onSavedListener;
|
private DialogHelper.OnSavedListener onSavedListener;
|
||||||
private ProgressBar progressBar;
|
private ProgressBar progressBar;
|
||||||
@@ -57,7 +57,7 @@ public class HabitSelectionCallback implements ActionMode.Callback
|
|||||||
void onActionModeDestroyed(ActionMode mode);
|
void onActionModeDestroyed(ActionMode mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HabitSelectionCallback(ReplayableActivity activity, HabitListLoader loader)
|
public HabitSelectionCallback(BaseActivity activity, HabitListLoader loader)
|
||||||
{
|
{
|
||||||
this.activity = activity;
|
this.activity = activity;
|
||||||
this.loader = loader;
|
this.loader = loader;
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ import com.mobeta.android.dslv.DragSortListView;
|
|||||||
import com.mobeta.android.dslv.DragSortListView.DropListener;
|
import com.mobeta.android.dslv.DragSortListView.DropListener;
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.ReplayableActivity;
|
import org.isoron.uhabits.BaseActivity;
|
||||||
import org.isoron.uhabits.commands.Command;
|
import org.isoron.uhabits.commands.Command;
|
||||||
import org.isoron.uhabits.commands.ToggleRepetitionCommand;
|
import org.isoron.uhabits.commands.ToggleRepetitionCommand;
|
||||||
import org.isoron.uhabits.dialogs.FilePickerDialog;
|
import org.isoron.uhabits.dialogs.FilePickerDialog;
|
||||||
@@ -88,7 +88,7 @@ public class ListHabitsFragment extends Fragment
|
|||||||
private ListHabitsHelper helper;
|
private ListHabitsHelper helper;
|
||||||
private List<Integer> selectedPositions;
|
private List<Integer> selectedPositions;
|
||||||
private OnHabitClickListener habitClickListener;
|
private OnHabitClickListener habitClickListener;
|
||||||
private ReplayableActivity activity;
|
private BaseActivity activity;
|
||||||
private SharedPreferences prefs;
|
private SharedPreferences prefs;
|
||||||
|
|
||||||
private DragSortListView listView;
|
private DragSortListView listView;
|
||||||
@@ -155,7 +155,7 @@ public class ListHabitsFragment extends Fragment
|
|||||||
public void onAttach(Activity activity)
|
public void onAttach(Activity activity)
|
||||||
{
|
{
|
||||||
super.onAttach(activity);
|
super.onAttach(activity);
|
||||||
this.activity = (ReplayableActivity) activity;
|
this.activity = (BaseActivity) activity;
|
||||||
|
|
||||||
habitClickListener = (OnHabitClickListener) activity;
|
habitClickListener = (OnHabitClickListener) activity;
|
||||||
prefs = PreferenceManager.getDefaultSharedPreferences(activity);
|
prefs = PreferenceManager.getDefaultSharedPreferences(activity);
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ public class SettingsFragment extends PreferenceFragment
|
|||||||
setResultOnPreferenceClick("importData", MainActivity.RESULT_IMPORT_DATA);
|
setResultOnPreferenceClick("importData", MainActivity.RESULT_IMPORT_DATA);
|
||||||
setResultOnPreferenceClick("exportCSV", MainActivity.RESULT_EXPORT_CSV);
|
setResultOnPreferenceClick("exportCSV", MainActivity.RESULT_EXPORT_CSV);
|
||||||
setResultOnPreferenceClick("exportDB", MainActivity.RESULT_EXPORT_DB);
|
setResultOnPreferenceClick("exportDB", MainActivity.RESULT_EXPORT_DB);
|
||||||
|
setResultOnPreferenceClick("bugReport", MainActivity.RESULT_BUG_REPORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setResultOnPreferenceClick(String key, final int result)
|
private void setResultOnPreferenceClick(String key, final int result)
|
||||||
|
|||||||
@@ -21,5 +21,6 @@
|
|||||||
<string name="helpURL">https://isoron.github.io/uhabits/faq.html</string>
|
<string name="helpURL">https://isoron.github.io/uhabits/faq.html</string>
|
||||||
<string name="playStoreURL">market://details?id=org.isoron.uhabits</string>
|
<string name="playStoreURL">market://details?id=org.isoron.uhabits</string>
|
||||||
<string name="feedbackURL" formatted="false">mailto:isoron+habits@gmail.com?subject=Feedback%20about%20Loop%20Habit%20Tracker</string>
|
<string name="feedbackURL" formatted="false">mailto:isoron+habits@gmail.com?subject=Feedback%20about%20Loop%20Habit%20Tracker</string>
|
||||||
|
<string name="bugReportURL" formatted="false">mailto:isoron+habits@gmail.com?subject=Bug%20Report%20-%20Loop%20Habit%20Tracker</string>
|
||||||
<string name="sourceCodeURL">https://github.com/iSoron/uhabits</string>
|
<string name="sourceCodeURL">https://github.com/iSoron/uhabits</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -148,4 +148,7 @@
|
|||||||
<string name="import_data_summary">Supports full backups exported by this app, as well as files generated by Tickmate, HabitBull or Rewire. See FAQ for more information.</string>
|
<string name="import_data_summary">Supports full backups exported by this app, as well as files generated by Tickmate, HabitBull or Rewire. See FAQ for more information.</string>
|
||||||
<string name="export_as_csv_summary">Generates files that can be opened by spreadsheet software such as Microsoft Excel or OpenOffice Calc. This file cannot be imported back.</string>
|
<string name="export_as_csv_summary">Generates files that can be opened by spreadsheet software such as Microsoft Excel or OpenOffice Calc. This file cannot be imported back.</string>
|
||||||
<string name="export_full_backup_summary">Generates a file that contains all your data. This file can be imported back.</string>
|
<string name="export_full_backup_summary">Generates a file that contains all your data. This file can be imported back.</string>
|
||||||
|
<string name="bug_report_failed">Failed to generate bug report.</string>
|
||||||
|
<string name="generate_bug_report">Generate bug report</string>
|
||||||
|
<string name="troubleshooting">Troubleshooting</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -65,6 +65,17 @@
|
|||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory
|
||||||
|
android:key="pref_key_debug"
|
||||||
|
android:title="@string/troubleshooting">
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
android:key="bugReport"
|
||||||
|
android:title="@string/generate_bug_report">
|
||||||
|
</Preference>
|
||||||
|
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:key="pref_key_links"
|
android:key="pref_key_links"
|
||||||
android:title="@string/links">
|
android:title="@string/links">
|
||||||
|
|||||||
Reference in New Issue
Block a user