feat(LogScreen): add log sharing
This commit is contained in:
parent
3a9e3562ca
commit
d64a1767fc
9 changed files with 155 additions and 43 deletions
|
|
@ -34,6 +34,16 @@
|
|||
android:name=".installers.pm.PMIntentReceiver"
|
||||
android:exported="false" />
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/provider_paths" />
|
||||
</provider>
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.aliucord.manager.manager
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
|
|
@ -13,6 +14,9 @@ import kotlinx.serialization.ExperimentalSerializationApi
|
|||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.decodeFromStream
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Central manager for storing all attempted installations and
|
||||
|
|
@ -103,4 +107,35 @@ data class InstallLogData(
|
|||
) {
|
||||
val isError: Boolean
|
||||
get() = errorStacktrace != null
|
||||
|
||||
fun getFormattedInstallDate(): String {
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
return SimpleDateFormat("yyyy-MM-dd HH-mm-ss", Locale.ENGLISH)
|
||||
.format(Date(installDate.toEpochMilliseconds()))
|
||||
}
|
||||
|
||||
fun getLogFileContents(): String = buildString {
|
||||
appendLine("////////////////// Environment Info //////////////////")
|
||||
appendLine(environmentInfo)
|
||||
|
||||
append("\n\n")
|
||||
appendLine("////////////////// Installation Info //////////////////")
|
||||
appendLine()
|
||||
append("Install ID: ")
|
||||
appendLine(id)
|
||||
append("Install time: ")
|
||||
appendLine(getFormattedInstallDate())
|
||||
append("Result: ")
|
||||
appendLine(if (isError) "Failure" else "Success")
|
||||
|
||||
append("\n\n")
|
||||
appendLine("////////////////// Error Stacktrace //////////////////")
|
||||
appendLine()
|
||||
appendLine(errorStacktrace ?: "None")
|
||||
|
||||
append("\n\n")
|
||||
appendLine("////////////////// Installation Log //////////////////")
|
||||
appendLine()
|
||||
appendLine(installationLog)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,9 @@ package com.aliucord.manager.ui.screens.log
|
|||
import android.os.Parcelable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import cafe.adriel.voyager.core.screen.Screen
|
||||
|
|
@ -16,7 +15,8 @@ import cafe.adriel.voyager.navigator.LocalNavigator
|
|||
import cafe.adriel.voyager.navigator.currentOrThrow
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.manager.InstallLogData
|
||||
import com.aliucord.manager.ui.components.*
|
||||
import com.aliucord.manager.ui.components.back
|
||||
import com.aliucord.manager.ui.screens.log.components.LogAppBar
|
||||
import com.aliucord.manager.ui.screens.log.components.SelectableTextArea
|
||||
import com.aliucord.manager.ui.screens.patchopts.components.options.PatchOption
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
|
|
@ -41,7 +41,8 @@ class LogScreen(private val installId: String) : Screen, Parcelable {
|
|||
model.data?.let {
|
||||
LogScreenContent(
|
||||
data = it,
|
||||
onSaveLog = model::saveLog,
|
||||
onExportLog = model::saveLog,
|
||||
onShareLog = model::shareLog,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -50,13 +51,14 @@ class LogScreen(private val installId: String) : Screen, Parcelable {
|
|||
@Composable
|
||||
fun LogScreenContent(
|
||||
data: InstallLogData,
|
||||
onSaveLog: () -> Unit,
|
||||
onExportLog: () -> Unit,
|
||||
onShareLog: () -> Unit,
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.log_title)) },
|
||||
navigationIcon = { BackButton() },
|
||||
LogAppBar(
|
||||
onExportLog = onExportLog,
|
||||
onShareLog = onShareLog,
|
||||
)
|
||||
},
|
||||
) { paddingValues ->
|
||||
|
|
@ -118,15 +120,6 @@ fun LogScreenContent(
|
|||
// )
|
||||
// }
|
||||
// }
|
||||
|
||||
item("SAVE_BUTTON") {
|
||||
MainActionButton(
|
||||
text = stringResource(R.string.log_action_export),
|
||||
icon = painterResource(R.drawable.ic_download),
|
||||
onClick = onSaveLog,
|
||||
modifier = Modifier.padding(top = 10.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
package com.aliucord.manager.ui.screens.log
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.core.content.FileProvider
|
||||
import cafe.adriel.voyager.core.model.ScreenModel
|
||||
import cafe.adriel.voyager.core.model.screenModelScope
|
||||
import com.aliucord.manager.BuildConfig
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.manager.InstallLogData
|
||||
import com.aliucord.manager.manager.InstallLogManager
|
||||
import com.aliucord.manager.util.*
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
|
||||
class LogScreenModel(
|
||||
private val installId: String,
|
||||
|
|
@ -34,33 +34,49 @@ class LogScreenModel(
|
|||
fun saveLog() = screenModelScope.launchBlock {
|
||||
val data = data ?: return@launchBlock
|
||||
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
val formattedDate = SimpleDateFormat("yyyy-MM-dd hh-mm-s a", Locale.ENGLISH)
|
||||
.format(Date(data.installDate.toEpochMilliseconds()))
|
||||
val formattedDate = data.getFormattedInstallDate()
|
||||
val content = data.getLogFileContents()
|
||||
|
||||
val content = buildString {
|
||||
appendLine("////////////////// Installation Info //////////////////")
|
||||
append("Install ID: ")
|
||||
appendLine(data.id)
|
||||
append("Install time: ")
|
||||
appendLine(formattedDate)
|
||||
append("Result: ")
|
||||
appendLine(if (data.isError) "Failure" else "Success")
|
||||
application.saveFile("Aliucord Install $formattedDate.log", content)
|
||||
}
|
||||
|
||||
append("\n\n")
|
||||
appendLine("////////////////// Environment Info //////////////////")
|
||||
appendLine(data.environmentInfo)
|
||||
/**
|
||||
* Writes the log to internal cache and launches a share intent of the log file.
|
||||
*/
|
||||
fun shareLog() = screenModelScope.launchBlock {
|
||||
val data = data ?: return@launchBlock
|
||||
val formattedDate = data.getFormattedInstallDate()
|
||||
val formattedName = "Aliucord Install $formattedDate.log"
|
||||
val content = data.getLogFileContents()
|
||||
|
||||
append("\n\n")
|
||||
appendLine("////////////////// Error Stacktrace //////////////////")
|
||||
appendLine(data.errorStacktrace ?: "None")
|
||||
val file = application.cacheDir.resolve(formattedName)
|
||||
val fileUri = FileProvider.getUriForFile(
|
||||
/* context = */ application,
|
||||
/* authority = */ "${BuildConfig.APPLICATION_ID}.provider",
|
||||
/* file = */ file,
|
||||
/* displayName = */ formattedName,
|
||||
)
|
||||
|
||||
append("\n\n")
|
||||
appendLine("////////////////// Installation Log //////////////////")
|
||||
appendLine(data.installationLog)
|
||||
val intent = Intent(Intent.ACTION_SEND)
|
||||
.setType("text/*")
|
||||
.putExtra(Intent.EXTRA_STREAM, fileUri)
|
||||
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
.let {
|
||||
Intent.createChooser(
|
||||
/* target = */ it,
|
||||
/* title = */ application.getString(R.string.log_action_share),
|
||||
)
|
||||
}
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
|
||||
try {
|
||||
file.writeText(content)
|
||||
file.deleteOnExit()
|
||||
application.startActivity(intent)
|
||||
} catch (t: Throwable) {
|
||||
Log.w(BuildConfig.TAG, "Failed to share log", t)
|
||||
application.showToast(R.string.status_failed)
|
||||
}
|
||||
|
||||
application.saveFile("Aliucord Manager $formattedDate.log", content)
|
||||
}
|
||||
|
||||
private fun loadLogData() = screenModelScope.launchBlock {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
package com.aliucord.manager.ui.screens.log.components
|
||||
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.ui.components.BackButton
|
||||
|
||||
@Composable
|
||||
fun LogAppBar(
|
||||
onExportLog: () -> Unit,
|
||||
onShareLog: () -> Unit,
|
||||
) {
|
||||
TopAppBar(
|
||||
title = { Text(stringResource(R.string.log_title)) },
|
||||
navigationIcon = { BackButton() },
|
||||
actions = {
|
||||
IconButton(onClick = onExportLog) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_save),
|
||||
contentDescription = stringResource(R.string.log_action_export),
|
||||
)
|
||||
}
|
||||
IconButton(onClick = onShareLog) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_share),
|
||||
contentDescription = stringResource(R.string.log_action_share),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
9
app/src/main/res/drawable/ic_save.xml
Normal file
9
app/src/main/res/drawable/ic_save.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="#e8eaed"
|
||||
android:pathData="M200,840q-33,0 -56.5,-23.5T120,760v-560q0,-33 23.5,-56.5T200,120h447q16,0 30.5,6t25.5,17l114,114q11,11 17,25.5t6,30.5v447q0,33 -23.5,56.5T760,840L200,840ZM480,720q50,0 85,-35t35,-85q0,-50 -35,-85t-85,-35q-50,0 -85,35t-35,85q0,50 35,85t85,35ZM280,400h280q17,0 28.5,-11.5T600,360v-80q0,-17 -11.5,-28.5T560,240L280,240q-17,0 -28.5,11.5T240,280v80q0,17 11.5,28.5T280,400Z" />
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_share.xml
Normal file
9
app/src/main/res/drawable/ic_share.xml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="#e8eaed"
|
||||
android:pathData="M680,880q-50,0 -85,-35t-35,-85q0,-6 3,-28L282,568q-16,15 -37,23.5t-45,8.5q-50,0 -85,-35t-35,-85q0,-50 35,-85t85,-35q24,0 45,8.5t37,23.5l281,-164q-2,-7 -2.5,-13.5T560,200q0,-50 35,-85t85,-35q50,0 85,35t35,85q0,50 -35,85t-85,35q-24,0 -45,-8.5T598,288L317,452q2,7 2.5,13.5t0.5,14.5q0,8 -0.5,14.5T317,508l281,164q16,-15 37,-23.5t45,-8.5q50,0 85,35t35,85q0,50 -35,85t-85,35Z" />
|
||||
</vector>
|
||||
|
|
@ -192,6 +192,7 @@
|
|||
<string name="log_section_error">Error Stacktrace</string>
|
||||
<string name="log_section_log">Installation Log</string>
|
||||
<string name="log_action_export">Export log</string>
|
||||
<string name="log_action_share">Share log</string>
|
||||
|
||||
<string name="notif_group_install_title">Active installation</string>
|
||||
<string name="notif_group_install_desc">Progress notifications sent during a minimized installation</string>
|
||||
|
|
|
|||
6
app/src/main/res/xml/provider_paths.xml
Normal file
6
app/src/main/res/xml/provider_paths.xml
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths>
|
||||
<cache-path
|
||||
name="cache"
|
||||
path="." />
|
||||
</paths>
|
||||
Loading…
Add table
Reference in a new issue