Skip to content
This repository has been archived by the owner on Jul 7, 2024. It is now read-only.

Commit

Permalink
Add actions to log viewer
Browse files Browse the repository at this point in the history
- Save to user-specified file
- Share logs as a file (In both the log viewer and the installer screen
  replacing the copy logs button)
- Move view logs button to the installer screens toolbar, hidden behind
  developer mode
  • Loading branch information
wingio committed Feb 5, 2024
1 parent 5bbc428 commit a87fea6
Show file tree
Hide file tree
Showing 9 changed files with 213 additions and 37 deletions.
10 changes: 10 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@
android:exported="true"
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />

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

</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import dev.beefers.vendetta.manager.installer.step.StepRunner
import dev.beefers.vendetta.manager.installer.step.StepStatus
import dev.beefers.vendetta.manager.utils.mainThread
import dev.beefers.vendetta.manager.utils.showToast
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.koin.core.component.inject
Expand Down Expand Up @@ -123,8 +124,8 @@ abstract class DownloadStep: Step() {
}

is DownloadResult.Cancelled -> {
runner.logger.e("$fileName download cancelled")
status = StepStatus.UNSUCCESSFUL
throw CancellationException("$fileName download cancelled")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.outlined.Article
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilledTonalButton
Expand All @@ -37,13 +38,15 @@ import cafe.adriel.voyager.koin.getScreenModel
import cafe.adriel.voyager.navigator.LocalNavigator
import cafe.adriel.voyager.navigator.currentOrThrow
import dev.beefers.vendetta.manager.R
import dev.beefers.vendetta.manager.domain.manager.PreferenceManager
import dev.beefers.vendetta.manager.installer.step.StepStatus
import dev.beefers.vendetta.manager.ui.viewmodel.installer.InstallerViewModel
import dev.beefers.vendetta.manager.ui.widgets.dialog.BackWarningDialog
import dev.beefers.vendetta.manager.ui.widgets.dialog.DownloadFailedDialog
import dev.beefers.vendetta.manager.ui.widgets.installer.StepGroupCard
import dev.beefers.vendetta.manager.utils.DiscordVersion
import okhttp3.internal.toImmutableList
import org.koin.androidx.compose.get
import org.koin.core.parameter.parametersOf
import java.util.UUID

Expand Down Expand Up @@ -113,6 +116,7 @@ class InstallerScreen(
Scaffold(
topBar = {
TitleBar(
viewModel = viewModel,
onBackClick = {
if(!viewModel.runner.completed)
viewModel.openBackDialog()
Expand Down Expand Up @@ -161,11 +165,12 @@ class InstallerScreen(
.fillMaxWidth()
) {
FilledTonalButton(
onClick = { nav.push(LogViewerScreen(viewModel.runner.logger.logs.toImmutableList())) },
onClick = { viewModel.shareLogs(activity!!) },
modifier = Modifier.weight(1f)
) {
Text(stringResource(R.string.action_view_logs))
Text(stringResource(R.string.action_share_logs))
}

FilledTonalButton(
onClick = { viewModel.clearCache() },
modifier = Modifier.weight(1f)
Expand All @@ -181,8 +186,12 @@ class InstallerScreen(
@Composable
@OptIn(ExperimentalMaterial3Api::class)
private fun TitleBar(
onBackClick: () -> Unit
onBackClick: () -> Unit,
viewModel: InstallerViewModel
) {
val prefs: PreferenceManager = get()
val nav = LocalNavigator.currentOrThrow

TopAppBar(
title = { Text(stringResource(R.string.title_installer)) },
navigationIcon = {
Expand All @@ -192,6 +201,16 @@ class InstallerScreen(
contentDescription = stringResource(R.string.action_back)
)
}
},
actions = {
if (prefs.isDeveloper && viewModel.runner.completed) {
IconButton(onClick = { nav.push(LogViewerScreen(viewModel.runner.logger.logs.toImmutableList())) }) {
Icon(
imageVector = Icons.Outlined.Article,
contentDescription = stringResource(R.string.action_view_logs)
)
}
}
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.WindowInsets
Expand All @@ -14,6 +15,11 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Share
import androidx.compose.material.icons.outlined.MoreVert
import androidx.compose.material.icons.outlined.Save
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
Expand All @@ -33,11 +39,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import cafe.adriel.voyager.core.screen.Screen
Expand All @@ -48,6 +56,7 @@ import dev.beefers.vendetta.manager.R
import dev.beefers.vendetta.manager.installer.util.LogEntry
import dev.beefers.vendetta.manager.ui.viewmodel.installer.LogViewerViewModel
import dev.beefers.vendetta.manager.utils.DimenUtils
import dev.beefers.vendetta.manager.utils.rememberFileSaveLauncher
import dev.beefers.vendetta.manager.utils.thenIf
import org.koin.core.parameter.parametersOf

Expand All @@ -65,7 +74,7 @@ class LogViewerScreen(
}

Scaffold(
topBar = { Toolbar(scrollBehavior) },
topBar = { Toolbar(scrollBehavior, viewModel) },
contentWindowInsets = WindowInsets(0, 0, 0, 0),
modifier = Modifier
.nestedScroll(scrollBehavior.nestedScrollConnection)
Expand Down Expand Up @@ -143,9 +152,12 @@ class LogViewerScreen(

@Composable
private fun Toolbar(
scrollBehavior: TopAppBarScrollBehavior
scrollBehavior: TopAppBarScrollBehavior,
viewModel: LogViewerViewModel
) {
val navigator = LocalNavigator.currentOrThrow
val context = LocalContext.current
val saveFile = rememberFileSaveLauncher(content = viewModel.logsString)

TopAppBar(
title = { Text(stringResource(R.string.title_logs)) },
Expand All @@ -157,8 +169,62 @@ class LogViewerScreen(
)
}
},
actions = {
var showDropdown by remember {
mutableStateOf(false)
}

IconButton(onClick = { saveFile.launch("VD-Manager-${System.currentTimeMillis()}.log") }) {
Icon(
imageVector = Icons.Outlined.Save,
contentDescription = stringResource(R.string.action_save_logs)
)
}

IconButton(onClick = { viewModel.shareLogs(context) }) {
Icon(
imageVector = Icons.Filled.Share,
contentDescription = stringResource(R.string.action_share_logs)
)
}

IconButton(onClick = { showDropdown = true }) {
Icon(
imageVector = Icons.Outlined.MoreVert,
contentDescription = stringResource(R.string.action_more_options)
)
}

Dropdown(
viewModel,
expanded = showDropdown,
onDismiss = { showDropdown = false }
)
},
scrollBehavior = scrollBehavior
)
}

@Composable
fun Dropdown(
viewModel: LogViewerViewModel,
expanded: Boolean,
onDismiss: () -> Unit
) {
Box {
DropdownMenu(
expanded = expanded,
onDismissRequest = onDismiss,
offset = DpOffset(
10.dp, 26.dp
)
) {
DropdownMenuItem(
text = { Text(stringResource(R.string.action_copy_logs)) },
onClick = { viewModel.copyLogs() }
)
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,53 +2,29 @@ package dev.beefers.vendetta.manager.ui.viewmodel.installer

import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Environment
import android.util.Log
import androidx.annotation.StringRes
import androidx.compose.runtime.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.core.app.ShareCompat
import androidx.core.content.FileProvider
import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.screenModelScope
import com.github.diamondminer88.zip.ZipCompression
import com.github.diamondminer88.zip.ZipReader
import com.github.diamondminer88.zip.ZipWriter
import dev.beefers.vendetta.manager.BuildConfig
import dev.beefers.vendetta.manager.R
import dev.beefers.vendetta.manager.domain.manager.DownloadManager
import dev.beefers.vendetta.manager.domain.manager.DownloadResult
import dev.beefers.vendetta.manager.domain.manager.InstallManager
import dev.beefers.vendetta.manager.domain.manager.InstallMethod
import dev.beefers.vendetta.manager.domain.manager.PreferenceManager
import dev.beefers.vendetta.manager.installer.Installer
import dev.beefers.vendetta.manager.installer.session.SessionInstaller
import dev.beefers.vendetta.manager.installer.shizuku.ShizukuInstaller
import dev.beefers.vendetta.manager.installer.step.Step
import dev.beefers.vendetta.manager.installer.step.StepGroup
import dev.beefers.vendetta.manager.installer.step.StepRunner
import dev.beefers.vendetta.manager.installer.step.installing.InstallStep
import dev.beefers.vendetta.manager.installer.util.ManifestPatcher
import dev.beefers.vendetta.manager.installer.util.Patcher
import dev.beefers.vendetta.manager.installer.util.Signer
import dev.beefers.vendetta.manager.utils.DiscordVersion
import dev.beefers.vendetta.manager.utils.copyText
import dev.beefers.vendetta.manager.utils.isMiui
import dev.beefers.vendetta.manager.utils.mainThread
import dev.beefers.vendetta.manager.utils.showToast
import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.toImmutableMap
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.lsposed.patch.util.Logger
import java.io.File
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.time.measureTimedValue

class InstallerViewModel(
private val context: Context,
Expand All @@ -64,6 +40,14 @@ class InstallerViewModel(
}
.toImmutableMap()

private val tempLogStorageDir = context.filesDir.resolve("logsTmp").also {
it.mkdirs()
}

private val logsString by lazy {
runner.logger.logs.joinToString("\n") { it.toString() }
}

private val installationRunning = AtomicBoolean(false)

private val job = screenModelScope.launch(Dispatchers.Main) {
Expand All @@ -90,10 +74,6 @@ class InstallerViewModel(
runner.logger.e(msg)
}

fun copyDebugInfo() {
context.copyText(runner.logger.logs.joinToString("\n"))
}

fun clearCache() {
runner.clearCache()
context.showToast(R.string.msg_cleared_cache)
Expand Down Expand Up @@ -130,4 +110,36 @@ class InstallerViewModel(
}
}

private fun saveToAppStorage(): File {
// Delete old logs to prevent junk buildup
tempLogStorageDir.deleteRecursively()
tempLogStorageDir.mkdirs()

val tmpFile = tempLogStorageDir.resolve("VD-Manager-${System.currentTimeMillis()}.log")
tmpFile.outputStream().use { stream ->
stream.write(logsString.toByteArray())
}

return tmpFile
}

fun shareLogs(activityContext: Context) {
val saved = saveToAppStorage()
val uri = FileProvider.getUriForFile(
activityContext,
BuildConfig.APPLICATION_ID + ".provider",
saved
)

ShareCompat.IntentBuilder(activityContext)
.setType("text/plain")
.setStream(uri)
.apply {
intent.apply {
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
}
.startChooser()
}

}
Loading

0 comments on commit a87fea6

Please sign in to comment.