Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve patcher UI #1494

Merged
merged 19 commits into from
Jan 6, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ dependencies {
implementation(platform(libs.compose.bom))
implementation(libs.compose.ui)
implementation(libs.compose.ui.preview)
implementation(libs.compose.ui.tooling)
implementation(libs.compose.livedata)
implementation(libs.compose.material.icons.extended)
implementation(libs.compose.material3)
Expand Down
6 changes: 3 additions & 3 deletions app/src/main/java/app/revanced/manager/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import app.revanced.manager.ui.destination.SettingsDestination
import app.revanced.manager.ui.screen.AppSelectorScreen
import app.revanced.manager.ui.screen.DashboardScreen
import app.revanced.manager.ui.screen.InstalledAppInfoScreen
import app.revanced.manager.ui.screen.InstallerScreen
import app.revanced.manager.ui.screen.PatcherScreen
import app.revanced.manager.ui.screen.SelectedAppInfoScreen
import app.revanced.manager.ui.screen.SettingsScreen
import app.revanced.manager.ui.screen.VersionSelectorScreen
Expand Down Expand Up @@ -157,7 +157,7 @@ class MainActivity : ComponentActivity() {
is Destination.SelectedApplicationInfo -> SelectedAppInfoScreen(
onPatchClick = { app, patches, options ->
navController.navigate(
Destination.Installer(
Destination.Patcher(
app, patches, options
)
)
Expand All @@ -173,7 +173,7 @@ class MainActivity : ComponentActivity() {
}
)

is Destination.Installer -> InstallerScreen(
is Destination.Patcher -> PatcherScreen(
onBackClick = { navController.popUpTo { it is Destination.Dashboard } },
vm = getComposeViewModel { parametersOf(destination) }
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ val viewModelModule = module {
viewModelOf(::AdvancedSettingsViewModel)
viewModelOf(::AppSelectorViewModel)
viewModelOf(::VersionSelectorViewModel)
viewModelOf(::InstallerViewModel)
viewModelOf(::PatcherViewModel)
viewModelOf(::UpdateViewModel)
viewModelOf(::ChangelogsViewModel)
viewModelOf(::ImportExportViewModel)
Expand Down
73 changes: 58 additions & 15 deletions app/src/main/java/app/revanced/manager/patcher/Session.kt
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package app.revanced.manager.patcher

import android.content.Context
import app.revanced.library.ApkUtils
import app.revanced.manager.ui.viewmodel.ManagerLogger
import app.revanced.manager.R
import app.revanced.manager.patcher.logger.ManagerLogger
import app.revanced.manager.ui.model.State
import app.revanced.patcher.Patcher
import app.revanced.patcher.PatcherOptions
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.PatchResult
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.withContext
import java.io.Closeable
import java.io.File
Expand All @@ -21,10 +25,15 @@ class Session(
frameworkDir: String,
aaptPath: String,
multithreadingDexFileWriter: Boolean,
private val app: Context,
CnC-Robert marked this conversation as resolved.
Show resolved Hide resolved
private val logger: ManagerLogger,
private val input: File,
private val onStepSucceeded: suspend () -> Unit
private val patchesProgress: MutableStateFlow<Pair<Int, Int>>,
private val onProgress: (name: String?, state: State?, message: String?) -> Unit
) : Closeable {
private fun updateProgress(name: String? = null, state: State? = null, message: String? = null) =
onProgress(name, state, message)

private val tempDir = File(cacheDir).resolve("patcher").also { it.mkdirs() }
private val patcher = Patcher(
PatcherOptions(
Expand All @@ -36,22 +45,56 @@ class Session(
)
)

private suspend fun Patcher.applyPatchesVerbose(selectedPatches: PatchList) {
var nextPatchIndex = 0

updateProgress(
name = app.getString(R.string.applying_patch, selectedPatches[nextPatchIndex]),
state = State.RUNNING
)

private suspend fun Patcher.applyPatchesVerbose() {
this.apply(true).collect { (patch, exception) ->
if (exception == null) {
logger.info("$patch succeeded")
onStepSucceeded()
return@collect
if (patch !in selectedPatches) return@collect

if (exception != null) {
updateProgress(
name = app.getString(R.string.failed_to_apply_patch, patch.name),
state = State.FAILED,
message = exception.stackTraceToString()
)

logger.error("${patch.name} failed:")
logger.error(exception.stackTraceToString())
throw exception
}
logger.error("$patch failed:")
logger.error(exception.stackTraceToString())
throw exception

nextPatchIndex++

patchesProgress.value.let {
patchesProgress.emit(it.copy(it.first + 1))
}

selectedPatches.getOrNull(nextPatchIndex)?.let { nextPatch ->
updateProgress(
name = app.getString(R.string.applying_patch, nextPatch.name)
)
}

logger.info("${patch.name} succeeded")
}

updateProgress(
state = State.COMPLETED,
name = app.resources.getQuantityString(
R.plurals.patches_applied,
selectedPatches.size,
selectedPatches.size
)
)
}

suspend fun run(output: File, selectedPatches: PatchList, integrations: List<File>) {
onStepSucceeded() // Unpacking
updateProgress(state = State.COMPLETED) // Unpacking
Logger.getLogger("").apply {
handlers.forEach {
it.close()
Expand All @@ -64,10 +107,10 @@ class Session(
logger.info("Merging integrations")
acceptIntegrations(integrations)
acceptPatches(selectedPatches)
onStepSucceeded() // Merging
updateProgress(state = State.COMPLETED) // Merging

logger.info("Applying patches...")
applyPatchesVerbose()
applyPatchesVerbose(selectedPatches.sortedBy { it.name })
}

logger.info("Writing patched files...")
Expand All @@ -81,7 +124,7 @@ class Session(
withContext(Dispatchers.IO) {
Files.move(aligned.toPath(), output.toPath(), StandardCopyOption.REPLACE_EXISTING)
}
onStepSucceeded() // Saving
updateProgress(state = State.COMPLETED) // Saving
}

override fun close() {
Expand All @@ -90,7 +133,7 @@ class Session(
}

companion object {
operator fun PatchResult.component1() = patch.name
operator fun PatchResult.component1() = patch
operator fun PatchResult.component2() = exception
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package app.revanced.manager.patcher.logger

import android.util.Log
import java.util.logging.Handler
import java.util.logging.Level
import java.util.logging.LogRecord

class ManagerLogger : Handler() {
private val logs = mutableListOf<Pair<LogLevel, String>>()
private fun log(level: LogLevel, msg: String) {
level.androidLog(msg)
if (level == LogLevel.TRACE) return
logs.add(level to msg)
}

fun export() =
logs.asSequence().map { (level, msg) -> "[${level.name}]: $msg" }.joinToString("\n")

fun trace(msg: String) = log(LogLevel.TRACE, msg)
fun info(msg: String) = log(LogLevel.INFO, msg)
fun warn(msg: String) = log(LogLevel.WARN, msg)
fun error(msg: String) = log(LogLevel.ERROR, msg)
override fun publish(record: LogRecord) {
val msg = record.message
val fn = when (record.level) {
Level.INFO -> ::info
Level.SEVERE -> ::error
Level.WARNING -> ::warn
else -> ::trace
}

fn(msg)
}

override fun flush() = Unit

override fun close() = Unit
}

enum class LogLevel {
TRACE {
override fun androidLog(msg: String) = Log.v(androidTag, msg)
},
INFO {
override fun androidLog(msg: String) = Log.i(androidTag, msg)
},
WARN {
override fun androidLog(msg: String) = Log.w(androidTag, msg)
},
ERROR {
override fun androidLog(msg: String) = Log.e(androidTag, msg)
};

abstract fun androidLog(msg: String): Int

private companion object {
const val androidTag = "ReVanced Patcher"
}
}

This file was deleted.

Loading