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 + +