From d76e056b91937c8435fd4187d854258f5b65905d Mon Sep 17 00:00:00 2001 From: tarsin Date: Mon, 5 Jun 2023 11:50:16 +0800 Subject: [PATCH] Implement dumpLogcatLauncher --- .../ehviewer/ui/settings/AdvancedScreen.kt | 59 +++++++++++++++++-- 1 file changed, 55 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt b/app/src/main/java/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt index b18d33b49..87e50adb0 100644 --- a/app/src/main/java/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt +++ b/app/src/main/java/com/hippo/ehviewer/ui/settings/AdvancedScreen.kt @@ -1,5 +1,8 @@ package com.hippo.ehviewer.ui.settings +import android.content.Intent +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.padding @@ -11,21 +14,38 @@ import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource +import com.hippo.ehviewer.AppConfig +import com.hippo.ehviewer.BuildConfig import com.hippo.ehviewer.R import com.hippo.ehviewer.Settings import com.hippo.ehviewer.ui.login.LocalNavController +import com.hippo.ehviewer.util.LogCat +import com.hippo.ehviewer.util.ReadableTime +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import java.io.File +import java.util.zip.ZipEntry +import java.util.zip.ZipOutputStream @Composable fun AdvancedScreen() { val navController = LocalNavController.current + val context = LocalContext.current val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior() + val snackbarHostState = remember { SnackbarHostState() } + val coroutineScope = rememberCoroutineScope { Dispatchers.IO } Scaffold( topBar = { TopAppBar( @@ -38,8 +58,9 @@ fun AdvancedScreen() { scrollBehavior = scrollBehavior, ) }, - ) { - Column(modifier = Modifier.padding(top = it.calculateTopPadding()).nestedScroll(scrollBehavior.nestedScrollConnection).verticalScroll(rememberScrollState())) { + snackbarHost = { SnackbarHost(snackbarHostState) }, + ) { paddingValues -> + Column(modifier = Modifier.padding(top = paddingValues.calculateTopPadding()).nestedScroll(scrollBehavior.nestedScrollConnection).verticalScroll(rememberScrollState())) { SwitchPreference( title = stringResource(id = R.string.settings_advanced_save_parse_error_body), summary = stringResource(id = R.string.settings_advanced_save_parse_error_body_summary), @@ -50,10 +71,40 @@ fun AdvancedScreen() { summary = stringResource(id = R.string.settings_advanced_save_crash_log_summary), value = Settings::saveCrashLog, ) + val dumpLogError = stringResource(id = R.string.settings_advanced_dump_logcat_failed) + val dumpLogcatLauncher = rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("application/zip")) { uri -> + uri?.run { + coroutineScope.launch { + context.runCatching { + grantUriPermission(BuildConfig.APPLICATION_ID, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) + contentResolver.openOutputStream(uri)?.use { outputStream -> + val files = ArrayList() + AppConfig.externalParseErrorDir?.listFiles()?.let { files.addAll(it) } + AppConfig.externalCrashDir?.listFiles()?.let { files.addAll(it) } + ZipOutputStream(outputStream).use { zipOs -> + files.forEach { file -> + if (!file.isFile) return@forEach + val entry = ZipEntry(file.name) + zipOs.putNextEntry(entry) + file.inputStream().use { it.copyTo(zipOs) } + } + val logcatEntry = ZipEntry("logcat-" + ReadableTime.getFilenamableTime(System.currentTimeMillis()) + ".txt") + zipOs.putNextEntry(logcatEntry) + LogCat.save(zipOs) + } + snackbarHostState.showSnackbar(getString(R.string.settings_advanced_dump_logcat_to, uri.toString())) + } + }.onFailure { + snackbarHostState.showSnackbar(dumpLogError) + it.printStackTrace() + } + } + } + } Preference( title = stringResource(id = R.string.settings_advanced_dump_logcat), summary = stringResource(id = R.string.settings_advanced_dump_logcat_summary), - ) + ) { dumpLogcatLauncher.launch("log-" + ReadableTime.getFilenamableTime(System.currentTimeMillis()) + ".zip") } SimpleMenuPreferenceInt( title = stringResource(id = R.string.settings_advanced_read_cache_size), entry = R.array.read_cache_size_entries, @@ -108,7 +159,7 @@ fun AdvancedScreen() { title = stringResource(id = R.string.open_by_default), summary = null, ) - Spacer(modifier = Modifier.size(it.calculateBottomPadding())) + Spacer(modifier = Modifier.size(paddingValues.calculateBottomPadding())) } } }