diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 8e18f3e4..12b9e1d9 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -54,6 +54,16 @@
+
+
+
+
+
+
-
-
-
+ viewModel.onLogcatUriReceived(uri)
+ }
+
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
permitDiskReads {
setPreferencesFromResource(R.xml.settings_expert, rootKey)
}
+ findPreference("logcat")?.setOnPreferenceClickListener {
+ val versionName = packageService.getVersionName(requireContext().packageName) ?: "ver"
+ val timestamp = System.currentTimeMillis()
+ val name = "seedvault-$versionName-$timestamp.txt"
+ createFileLauncher.launch(name)
+ true
+ }
}
override fun onStart() {
diff --git a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsViewModel.kt b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsViewModel.kt
index eb7505b5..3c190651 100644
--- a/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsViewModel.kt
+++ b/app/src/main/java/com/stevesoltys/seedvault/settings/SettingsViewModel.kt
@@ -11,6 +11,7 @@ import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.net.Uri
+import android.os.Process.myUid
import android.provider.Settings
import android.util.Log
import android.widget.Toast
@@ -35,8 +36,11 @@ import com.stevesoltys.seedvault.ui.RequireProvisioningViewModel
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
import org.calyxos.backup.storage.api.StorageBackup
import org.calyxos.backup.storage.backup.BackupJobService
+import java.io.IOException
+import java.lang.Runtime.getRuntime
import java.util.concurrent.TimeUnit.HOURS
private const val TAG = "SettingsViewModel"
@@ -233,4 +237,28 @@ internal class SettingsViewModel(
BackupJobService.cancelJob(app)
}
+ fun onLogcatUriReceived(uri: Uri?) = viewModelScope.launch(Dispatchers.IO) {
+ if (uri == null) {
+ onLogcatError()
+ return@launch
+ }
+ // 1000 is system uid, needed to get backup logs from the OS code.
+ val command = "logcat -d --uid=1000,${myUid()} *:V"
+ try {
+ app.contentResolver.openOutputStream(uri, "wt")?.use { outputStream ->
+ getRuntime().exec(command).inputStream.use { inputStream ->
+ inputStream.copyTo(outputStream)
+ }
+ } ?: throw IOException("OutputStream was null")
+ } catch (e: Exception) {
+ Log.e(TAG, "Error saving logcat ", e)
+ onLogcatError()
+ }
+ }
+
+ private suspend fun onLogcatError() = withContext(Dispatchers.Main) {
+ val str = app.getString(R.string.settings_expert_logcat_error)
+ Toast.makeText(app, str, LENGTH_LONG).show()
+ }
+
}
diff --git a/app/src/main/res/drawable/ic_bug_report.xml b/app/src/main/res/drawable/ic_bug_report.xml
new file mode 100644
index 00000000..dd702e24
--- /dev/null
+++ b/app/src/main/res/drawable/ic_bug_report.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 43732b55..b4976db9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -46,6 +46,9 @@
Expert settings
Unlimited app quota
Do not impose a limitation on the size of app backups.\n\nWarning: This can fill up your storage location quickly. Not needed for most apps.
+ Save app log
+ Developers can diagnose bugs with these logs.\n\nWarning: The log file might contain personally identifiable information. Review before and delete after sharing!
+ Error: Could not save app log
Choose where to store backups
diff --git a/app/src/main/res/xml/settings_expert.xml b/app/src/main/res/xml/settings_expert.xml
index a257d898..11e8497c 100644
--- a/app/src/main/res/xml/settings_expert.xml
+++ b/app/src/main/res/xml/settings_expert.xml
@@ -5,4 +5,9 @@
android:key="unlimited_quota"
android:summary="@string/settings_expert_quota_summary"
android:title="@string/settings_expert_quota_title" />
-
\ No newline at end of file
+
+