diff options
10 files changed, 82 insertions, 6 deletions
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index 0954037d..76d59e7b 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -7,10 +7,10 @@ body: attributes: value: | Thanks for taking the time to file this issue! Here are a few things to check before clicking the submit button :) - 1. Make sure you're on the latest version of the app. The current release can be found here: https://github.com/android-password-store/android-password-store/releases/latest. - 2. Search through **both** open and closed issues for your bug: https://github.com/android-password-store/Android-Password-Store/issues?q=is%3Aissue+sort%3Aupdated-desc+. + 1. Make sure you're on the latest version of the app. The current release can be found here: https://github.com/danixland/HardenedPass/releases/latest. + 2. Search through **both** open and closed issues for your bug: https://github.com/danixland/HardenedPass/issues?q=is%3Aissue+sort%3Aupdated-desc+. 3. ALWAYS fill this template. If you fail to do so, your issue will be immediately closed with no response. Maintaining open source projects for free is hard work, and we expect users to respect that time and effort by putting in a little bit of their own. That helps us fix these problems faster and in return you get a better app — everybody wins. - 4. Please record a device log, and attach it to the issue. You can follow the steps here for it: https://docs.passwordstore.app/docs/users/reporting-bugs + 4. Please attach a log: turn on Settings > Misc > Debug logging, restart the app, reproduce the bug, then use Settings > Misc > Export log. Read the file first and remove anything you do not want public: it can show entry names, websites, apps and your git server. - type: textarea id: bug-description @@ -58,7 +58,7 @@ body: value: | - Device: [e.g. Google Pixel 2] - OS: [e.g. Android R] - - App version: [e.g. 1.13.5] + - App version: [e.g. 0.3.2] validations: required: true diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml index 7e075dcc..f252cd29 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yaml +++ b/.github/ISSUE_TEMPLATE/feature_request.yaml @@ -7,7 +7,7 @@ body: attributes: value: | Thanks for taking the time to file this issue! Here are a few things to check before clicking the submit button :) - 1. Make sure you're on the latest version of the app. Features are being added all the time and it is entirely possible what you're requesting has already been added. The current release can be found here: https://github.com/android-password-store/android-password-store/releases/latest. + 1. Make sure you're on the latest version of the app. Features are being added all the time and it is entirely possible what you're requesting has already been added. The current release can be found here: https://github.com/danixland/HardenedPass/releases/latest. 2. Check the changelog file to confirm that the feature hasn't been added for an upcoming release: https://github.com/danixland/HardenedPass/blob/master/CHANGELOG.md 3. ALWAYS fill this template. If you fail to do so, your issue will be immediately closed with no response. Maintaining open source projects for free is hard work, and we expect users to respect that time and effort by putting in a little bit of their own. A feature request that is clear and explicit in its needs lets us get to work faster. diff --git a/CHANGELOG.md b/CHANGELOG.md index e33382c9..ffea326e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ All notable changes to this project will be documented in this file. ### Added - A "Push after each change" setting, off by default, pushes after saving, moving or deleting in the app; autofill saves stay local until the next push or sync +- Settings, Misc: "Export log" saves the app's logs to a file you choose, to attach to a bug report + +### Changed + +- Debug logs no longer show entry names from commits and autofill matches, or form fields' HTML attributes other than id, name, type, autocomplete and maxlength ### Fixed diff --git a/app/src/main/java/app/passwordstore/ui/settings/SettingsDestination.kt b/app/src/main/java/app/passwordstore/ui/settings/SettingsDestination.kt index c3809b36..05dc6b65 100644 --- a/app/src/main/java/app/passwordstore/ui/settings/SettingsDestination.kt +++ b/app/src/main/java/app/passwordstore/ui/settings/SettingsDestination.kt @@ -38,6 +38,7 @@ import androidx.core.net.toUri import androidx.documentfile.provider.DocumentFile import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.LifecycleResumeEffect +import androidx.lifecycle.lifecycleScope import app.passwordstore.BuildConfig import app.passwordstore.R import app.passwordstore.ui.compose.theme.SpacingLarge @@ -52,6 +53,7 @@ import app.passwordstore.util.services.PasswordExportService import app.passwordstore.util.settings.PreferenceKeys import com.github.androidpasswordstore.autofillparser.BrowserAutofillSupportLevel import com.github.androidpasswordstore.autofillparser.getInstalledBrowsersWithAutofillSupportLevel +import kotlinx.coroutines.launch private enum class SettingsDialog { Autofill, @@ -125,6 +127,16 @@ fun SettingsSectionDestination( ) } + val exportLog = + rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) { uri -> + if (uri != null) + activity.lifecycleScope.launch { + val message = + if (viewModel.exportLog(uri)) R.string.log_exported else R.string.log_export_failed + activity.snackbar(message = activity.getString(message)) + } + } + // One screen lock prompt at a time: a double tap must not open two. A recreation resets it // (remember), and the old activity's prompt is gone with it. var prompting by remember { mutableStateOf(false) } @@ -206,6 +218,7 @@ fun SettingsSectionDestination( SettingsAction.ClearSecret -> dialog = SettingsDialog.ClearSecret SettingsAction.DeleteRepository -> dialog = SettingsDialog.DeleteRepository SettingsAction.Export -> export.launch(null) + SettingsAction.ExportLog -> exportLog.launch("hardenedpass-log.txt") else -> onOpen(action) } }, diff --git a/app/src/main/java/app/passwordstore/ui/settings/SettingsGraph.kt b/app/src/main/java/app/passwordstore/ui/settings/SettingsGraph.kt index ba4d158c..e45166e0 100644 --- a/app/src/main/java/app/passwordstore/ui/settings/SettingsGraph.kt +++ b/app/src/main/java/app/passwordstore/ui/settings/SettingsGraph.kt @@ -70,7 +70,8 @@ fun NavGraphBuilder.settingsGraph(activity: MainActivity, nav: NavHostController // Handled by the destination itself. SettingsAction.ClearSecret, SettingsAction.DeleteRepository, - SettingsAction.Export -> Unit + SettingsAction.Export, + SettingsAction.ExportLog -> Unit } }, onDeleted = { diff --git a/app/src/main/java/app/passwordstore/ui/settings/SettingsModel.kt b/app/src/main/java/app/passwordstore/ui/settings/SettingsModel.kt index eca6770c..54250f74 100644 --- a/app/src/main/java/app/passwordstore/ui/settings/SettingsModel.kt +++ b/app/src/main/java/app/passwordstore/ui/settings/SettingsModel.kt @@ -9,6 +9,7 @@ import app.passwordstore.util.extensions.hash import app.passwordstore.util.extensions.time import app.passwordstore.util.git.GitCommit import java.io.IOException +import java.io.OutputStream import logcat.LogPriority.ERROR import logcat.asLog import logcat.logcat @@ -77,3 +78,17 @@ fun gitLog(repo: Repository?): List<GitCommit> { emptyList() } } + +/** + * Writes [header], then what logcat holds for this app: Android shows an app only its own lines, + * and those of its earlier runs and crashes until the buffer rotates. + */ +fun writeAppLog(header: String, out: OutputStream) { + out.write(header.toByteArray()) + val logcat = + ProcessBuilder("logcat", "-d", "-b", "main,crash", "-v", "threadtime") + .redirectErrorStream(true) + .start() + logcat.inputStream.use { it.copyTo(out) } + logcat.waitFor() +} diff --git a/app/src/main/java/app/passwordstore/ui/settings/SettingsScreen.kt b/app/src/main/java/app/passwordstore/ui/settings/SettingsScreen.kt index a7bbe30a..2e7840ad 100644 --- a/app/src/main/java/app/passwordstore/ui/settings/SettingsScreen.kt +++ b/app/src/main/java/app/passwordstore/ui/settings/SettingsScreen.kt @@ -63,6 +63,7 @@ enum class SettingsAction { ClearSecret, DeleteRepository, Export, + ExportLog, } /** Top bar with [title] and back, then [content] in one grouped surface, scrolling. */ @@ -324,6 +325,11 @@ fun SettingsSectionScreen( onCheckedChange = { onBoolean(PreferenceKeys.ENABLE_DEBUG_LOGGING, it) }, summary = stringResource(R.string.pref_debug_logging_summary), ) + PreferenceAction( + title = stringResource(R.string.pref_export_log_title), + summary = stringResource(R.string.pref_export_log_summary), + onClick = { onAction(SettingsAction.ExportLog) }, + ) } SettingsSection.Pgp -> { PreferenceAction( diff --git a/app/src/main/java/app/passwordstore/ui/settings/SettingsViewModel.kt b/app/src/main/java/app/passwordstore/ui/settings/SettingsViewModel.kt index cfc576ae..90aecb2a 100644 --- a/app/src/main/java/app/passwordstore/ui/settings/SettingsViewModel.kt +++ b/app/src/main/java/app/passwordstore/ui/settings/SettingsViewModel.kt @@ -8,6 +8,8 @@ package app.passwordstore.ui.settings import android.app.Application import android.content.SharedPreferences import android.content.pm.ShortcutManager +import android.net.Uri +import android.os.Build import androidx.annotation.StringRes import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -16,6 +18,7 @@ import androidx.core.content.edit import androidx.core.content.getSystemService import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.viewModelScope +import app.passwordstore.BuildConfig import app.passwordstore.R import app.passwordstore.data.crypto.PGPPassphraseCache import app.passwordstore.data.repo.PasswordRepository @@ -31,12 +34,16 @@ import app.passwordstore.util.settings.GitSettings import app.passwordstore.util.settings.PasswordSortOrder import app.passwordstore.util.settings.PreferenceKeys import dagger.hilt.android.lifecycle.HiltViewModel +import java.io.IOException import java.nio.file.Files import java.nio.file.LinkOption import javax.inject.Inject import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import logcat.LogPriority.ERROR +import logcat.asLog +import logcat.logcat /** What the settings sections show: stored values, and the state rows depend on. */ data class SettingsState( @@ -111,6 +118,27 @@ constructor( state = read() } + /** Saves the app's log to [uri]; false when that failed. */ + suspend fun exportLog(uri: Uri): Boolean = + withContext(dispatcherProvider.io()) { + val header = + "HardenedPass ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE}), " + + "Android ${Build.VERSION.RELEASE} (SDK ${Build.VERSION.SDK_INT}), ${Build.MODEL}\n\n" + try { + val out = + getApplication<Application>().contentResolver.openOutputStream(uri) + ?: throw IOException("No stream for the chosen file") + out.use { writeAppLog(header, it) } + true + } catch (e: IOException) { + logcat(ERROR) { e.asLog() } + false + } catch (e: SecurityException) { + logcat(ERROR) { e.asLog() } + false + } + } + fun putBoolean(key: String, value: Boolean) { prefs.edit { putBoolean(key, value) } } diff --git a/app/src/main/res/values-it/strings.xml b/app/src/main/res/values-it/strings.xml index b4569746..fed8adec 100644 --- a/app/src/main/res/values-it/strings.xml +++ b/app/src/main/res/values-it/strings.xml @@ -288,6 +288,10 @@ <string name="pref_rebase_on_pull_title">Rebase al pull</string> <string name="pref_rebase_on_pull_summary">Durante pull o sincronizzazione, crea un merge commit con le modifiche del remote</string> <string name="pref_rebase_on_pull_summary_on">Durante pull o sincronizzazione, esegui il rebase dei commit che non sono sul remote</string> + <string name="pref_export_log_title">Esporta log</string> + <string name="pref_export_log_summary">Salva in un file i log di questa app. Possono mostrare nomi delle voci, siti, app e il tuo server git: leggi il file prima di condividerlo</string> + <string name="log_exported">Log salvato</string> + <string name="log_export_failed">Impossibile salvare il log</string> <string name="pref_push_after_commit_title">Push dopo ogni modifica</string> <string name="pref_push_after_commit_summary">Esegui il push sul repository remoto dopo aver salvato, spostato o eliminato nell\'app</string> <string name="git_push_after_commit_failed">Salvato sul dispositivo, ma push non riuscito. Sincronizza per riprovare</string> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fa9fb0f3..a415e95f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -126,6 +126,10 @@ <string name="pref_rebase_on_pull_title">Rebase on pull</string> <string name="pref_rebase_on_pull_summary">When pulling or syncing, create a merge commit with upstream changes</string> <string name="pref_rebase_on_pull_summary_on">When pulling or syncing, rebase commits that are not present in the remote repository</string> + <string name="pref_export_log_title">Export log</string> + <string name="pref_export_log_summary">Save this app\'s logs to a file. They can show entry names, websites, apps and your git server, so read the file before sharing it</string> + <string name="log_exported">Log saved</string> + <string name="log_export_failed">Could not save the log</string> <string name="pref_push_after_commit_title">Push after each change</string> <string name="pref_push_after_commit_summary">Push to the remote repository after saving, moving or deleting in the app</string> <string name="git_push_after_commit_failed">Saved on this device, but not pushed. Sync to try again</string> |
