11package io.nekohasekai.sfa.compose.screen.tools
22
33import android.content.Intent
4+ import androidx.activity.compose.rememberLauncherForActivityResult
5+ import androidx.activity.result.contract.ActivityResultContracts
46import androidx.compose.foundation.background
57import androidx.compose.foundation.clickable
68import androidx.compose.foundation.layout.Box
@@ -16,14 +18,13 @@ import androidx.compose.material.icons.Icons
1618import androidx.compose.material.icons.automirrored.filled.ArrowBack
1719import androidx.compose.material.icons.filled.DataObject
1820import androidx.compose.material.icons.filled.Delete
21+ import androidx.compose.material.icons.filled.Description
1922import androidx.compose.material.icons.filled.Share
2023import androidx.compose.material.icons.filled.Terminal
2124import androidx.compose.material.icons.outlined.Settings
2225import androidx.compose.material3.Card
2326import androidx.compose.material3.CardDefaults
2427import androidx.compose.material3.CircularProgressIndicator
25- import androidx.compose.material3.DropdownMenu
26- import androidx.compose.material3.DropdownMenuItem
2728import androidx.compose.material3.ExperimentalMaterial3Api
2829import androidx.compose.material3.Icon
2930import androidx.compose.material3.IconButton
@@ -60,6 +61,7 @@ import kotlinx.coroutines.Dispatchers
6061import kotlinx.coroutines.launch
6162import kotlinx.coroutines.withContext
6263import org.json.JSONObject
64+ import java.io.File
6365import java.text.DateFormat
6466
6567@OptIn(ExperimentalMaterial3Api ::class )
@@ -69,10 +71,27 @@ fun OOMReportDetailScreen(navController: NavController, reportId: String) {
6971 val report = reports.find { it.id == reportId }
7072 var files by remember { mutableStateOf<List <OOMReportFile >>(emptyList()) }
7173 var isLoading by remember { mutableStateOf(true ) }
72- var shareMenuExpanded by remember { mutableStateOf(false ) }
74+ var showShareDialog by remember { mutableStateOf(false ) }
75+ var pendingZipFile by remember { mutableStateOf<File ?>(null ) }
7376 val scope = rememberCoroutineScope()
7477 val context = LocalContext .current
7578
79+ val saveLauncher = rememberLauncherForActivityResult(
80+ contract = ActivityResultContracts .CreateDocument (" application/zip" ),
81+ ) { uri ->
82+ val zipFile = pendingZipFile
83+ pendingZipFile = null
84+ if (uri != null && zipFile != null ) {
85+ scope.launch(Dispatchers .IO ) {
86+ runCatching {
87+ context.contentResolver.openOutputStream(uri)?.use { output ->
88+ zipFile.inputStream().use { input -> input.copyTo(output) }
89+ }
90+ }
91+ }
92+ }
93+ }
94+
7695 LaunchedEffect (report) {
7796 if (report != null ) {
7897 withContext(Dispatchers .IO ) {
@@ -90,11 +109,12 @@ fun OOMReportDetailScreen(navController: NavController, reportId: String) {
90109 }
91110
92111 val hasConfig = report != null && OOMReportManager .hasConfigFile(report)
112+ val hasLog = report != null && OOMReportManager .hasLogFile(report)
93113
94- fun shareReport (includeConfig : Boolean ) {
114+ fun shareReport (includeConfig : Boolean , includeLog : Boolean ) {
95115 val currentReport = report ? : return
96116 scope.launch {
97- val zipFile = OOMReportManager .createZipArchive(currentReport, includeConfig)
117+ val zipFile = OOMReportManager .createZipArchive(currentReport, includeConfig, includeLog )
98118 val uri = FileProvider .getUriForFile(context, " ${context.packageName} .cache" , zipFile)
99119 val intent = Intent (Intent .ACTION_SEND ).apply {
100120 type = " application/zip"
@@ -115,33 +135,8 @@ fun OOMReportDetailScreen(navController: NavController, reportId: String) {
115135 },
116136 actions = {
117137 if (! isLoading && files.isNotEmpty()) {
118- if (hasConfig) {
119- IconButton (onClick = { shareMenuExpanded = true }) {
120- Icon (Icons .Default .Share , contentDescription = null )
121- }
122- DropdownMenu (
123- expanded = shareMenuExpanded,
124- onDismissRequest = { shareMenuExpanded = false },
125- ) {
126- DropdownMenuItem (
127- text = { Text (stringResource(R .string.report_share)) },
128- onClick = {
129- shareMenuExpanded = false
130- shareReport(includeConfig = false )
131- },
132- )
133- DropdownMenuItem (
134- text = { Text (stringResource(R .string.report_share_with_config)) },
135- onClick = {
136- shareMenuExpanded = false
137- shareReport(includeConfig = true )
138- },
139- )
140- }
141- } else {
142- IconButton (onClick = { shareReport(includeConfig = false ) }) {
143- Icon (Icons .Default .Share , contentDescription = null )
144- }
138+ IconButton (onClick = { showShareDialog = true }) {
139+ Icon (Icons .Default .Share , contentDescription = null )
145140 }
146141 IconButton (onClick = {
147142 scope.launch {
@@ -219,6 +214,7 @@ fun OOMReportDetailScreen(navController: NavController, reportId: String) {
219214 val icon = when (file.kind) {
220215 OOMReportFile .Kind .METADATA -> Icons .Default .DataObject
221216 OOMReportFile .Kind .CONFIG -> Icons .Outlined .Settings
217+ OOMReportFile .Kind .GO_LOG -> Icons .Default .Description
222218 OOMReportFile .Kind .PROFILE -> Icons .Default .Terminal
223219 }
224220 ListItem (
@@ -257,6 +253,25 @@ fun OOMReportDetailScreen(navController: NavController, reportId: String) {
257253 }
258254 }
259255 }
256+
257+ if (showShareDialog && report != null ) {
258+ ReportShareDialog (
259+ hasConfig = hasConfig,
260+ hasLog = hasLog,
261+ onSave = { includeConfig, includeLog ->
262+ showShareDialog = false
263+ scope.launch {
264+ pendingZipFile = OOMReportManager .createZipArchive(report, includeConfig, includeLog)
265+ saveLauncher.launch(" ${report.id} .zip" )
266+ }
267+ },
268+ onShare = { includeConfig, includeLog ->
269+ showShareDialog = false
270+ shareReport(includeConfig, includeLog)
271+ },
272+ onDismiss = { showShareDialog = false },
273+ )
274+ }
260275}
261276
262277@OptIn(ExperimentalMaterial3Api ::class )
0 commit comments