Skip to content

Commit

Permalink
feat: integrate revanced patcher (#22)
Browse files Browse the repository at this point in the history
  • Loading branch information
Axelen123 authored May 19, 2023
1 parent 2810e2e commit 437a9f1
Show file tree
Hide file tree
Showing 36 changed files with 1,462 additions and 319 deletions.
15 changes: 13 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ android {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}

packagingOptions {
resources {
excludes += "/prebuilt/**"
}
}

kotlinOptions {
jvmTarget = "11"
}
Expand All @@ -43,10 +50,12 @@ dependencies {

// AndroidX Core
implementation("androidx.core:core-ktx:1.10.1")
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.1")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
implementation("androidx.core:core-splashscreen:1.0.1")
implementation("androidx.activity:activity-compose:1.7.1")
implementation("androidx.paging:paging-common-ktx:3.1.1")
implementation("androidx.work:work-runtime-ktx:2.8.1")

// Compose
implementation(platform("androidx.compose:compose-bom:2023.05.01"))
Expand All @@ -70,11 +79,13 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0")

// ReVanced
implementation("app.revanced:revanced-patcher:7.0.0")
implementation("app.revanced:revanced-patcher:7.1.0")

// Koin
implementation("io.insert-koin:koin-android:3.4.0")
val koinVersion = "3.4.0"
implementation("io.insert-koin:koin-android:$koinVersion")
implementation("io.insert-koin:koin-androidx-compose:3.4.4")
implementation("io.insert-koin:koin-androidx-workmanager:$koinVersion")

// Compose Navigation
implementation("dev.olshevski.navigation:reimagined:1.4.0")
Expand Down
13 changes: 13 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
android:name=".ManagerApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:extractNativeLibs="true"
android:largeHeap="true"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
Expand All @@ -40,5 +42,16 @@

<service android:name=".service.InstallService" />
<service android:name=".service.UninstallService" />

<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
</application>
</manifest>
23 changes: 20 additions & 3 deletions app/src/main/java/app/revanced/manager/compose/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import app.revanced.manager.compose.ui.screen.AppSelectorScreen
import app.revanced.manager.compose.ui.screen.DashboardScreen
import app.revanced.manager.compose.ui.screen.PatchesSelectorScreen
import app.revanced.manager.compose.ui.screen.SettingsScreen
import app.revanced.manager.compose.ui.screen.InstallerScreen
import app.revanced.manager.compose.ui.theme.ReVancedManagerTheme
import app.revanced.manager.compose.ui.theme.Theme
import app.revanced.manager.compose.util.PM
Expand All @@ -24,6 +25,8 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import org.koin.androidx.compose.getViewModel
import org.koin.core.parameter.parametersOf

class MainActivity : ComponentActivity() {
private val prefs: PreferencesManager by inject()
Expand Down Expand Up @@ -53,7 +56,6 @@ class MainActivity : ComponentActivity() {
controller = navController
) { destination ->
when (destination) {

is Destination.Dashboard -> DashboardScreen(
onSettingsClick = { navController.navigate(Destination.Settings) },
onAppSelectorClick = { navController.navigate(Destination.AppSelector) }
Expand All @@ -64,14 +66,29 @@ class MainActivity : ComponentActivity() {
)

is Destination.AppSelector -> AppSelectorScreen(
onAppClick = { navController.navigate(Destination.PatchesSelector) },
onAppClick = { navController.navigate(Destination.PatchesSelector(it)) },
onBackClick = { navController.pop() }
)

is Destination.PatchesSelector -> PatchesSelectorScreen(
onBackClick = { navController.pop() }
onBackClick = { navController.pop() },
startPatching = {
navController.navigate(
Destination.Installer(
destination.input,
it
)
)
},
vm = getViewModel { parametersOf(destination.input) }
)

is Destination.Installer -> InstallerScreen(getViewModel {
parametersOf(
destination.input,
destination.selectedPatches
)
})
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,23 @@ package app.revanced.manager.compose
import android.app.Application
import app.revanced.manager.compose.di.*
import org.koin.android.ext.koin.androidContext
import org.koin.androidx.workmanager.koin.workManagerFactory
import org.koin.core.context.startKoin

class ManagerApplication: Application() {
class ManagerApplication : Application() {
override fun onCreate() {
super.onCreate()

startKoin {
androidContext(this@ManagerApplication)
workManagerFactory()
modules(
httpModule,
preferencesModule,
repositoryModule,
serviceModule,
viewModelModule
workerModule,
viewModelModule,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package app.revanced.manager.compose.di

import app.revanced.manager.compose.domain.repository.ReVancedRepositoryImpl
import app.revanced.manager.compose.network.api.ManagerAPI
import app.revanced.manager.compose.patcher.data.repository.*
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module

val repositoryModule = module {
singleOf(::ReVancedRepositoryImpl)
singleOf(::ManagerAPI)
singleOf(::PatchesRepository)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,23 @@ package app.revanced.manager.compose.di

import app.revanced.manager.compose.ui.viewmodel.*
import org.koin.androidx.viewmodel.dsl.viewModelOf
import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module

val viewModelModule = module {
viewModelOf(::PatchesSelectorViewModel)
viewModel {
PatchesSelectorViewModel(
packageInfo = it.get(),
patchesRepository = get()
)
}
viewModelOf(::SettingsViewModel)
}
viewModelOf(::AppSelectorViewModel)
viewModel {
InstallerScreenViewModel(
input = it.get(),
selectedPatches = it.get(),
app = get()
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package app.revanced.manager.compose.di

import app.revanced.manager.compose.patcher.worker.PatcherWorker
import org.koin.androidx.workmanager.dsl.workerOf
import org.koin.dsl.module

val workerModule = module {
workerOf(::PatcherWorker)
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,28 +34,36 @@ class ManagerAPI(
downloadProgress = null
}

suspend fun downloadPatchBundle() {
suspend fun downloadPatchBundle(): File? {
try {
val downloadUrl = revancedRepository.findAsset(ghPatches, ".jar").downloadUrl
val patchesFile = app.filesDir.resolve("patch-bundles").also { it.mkdirs() }
.resolve("patchbundle.jar")
downloadAsset(downloadUrl, patchesFile)

return patchesFile
} catch (e: Exception) {
Log.e(tag, "Failed to download patch bundle", e)
app.toast("Failed to download patch bundle")
}

return null
}

suspend fun downloadIntegrations() {
suspend fun downloadIntegrations(): File? {
try {
val downloadUrl = revancedRepository.findAsset(ghIntegrations, ".apk").downloadUrl
val integrationsFile = app.filesDir.resolve("integrations").also { it.mkdirs() }
.resolve("integrations.apk")
downloadAsset(downloadUrl, integrationsFile)

return integrationsFile
} catch (e: Exception) {
Log.e(tag, "Failed to download integrations", e)
app.toast("Failed to download integrations")
}

return null
}
}

Expand Down
38 changes: 38 additions & 0 deletions app/src/main/java/app/revanced/manager/compose/patcher/Aligning.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package app.revanced.manager.compose.patcher

import app.revanced.manager.compose.patcher.alignment.ZipAligner
import app.revanced.manager.compose.patcher.alignment.zip.ZipFile
import app.revanced.manager.compose.patcher.alignment.zip.structures.ZipEntry
import app.revanced.patcher.PatcherResult
import java.io.File

// This is the same aligner used by the CLI.
// It will be removed eventually.
object Aligning {
fun align(result: PatcherResult, inputFile: File, outputFile: File) {
// logger.info("Aligning ${inputFile.name} to ${outputFile.name}")

if (outputFile.exists()) outputFile.delete()

ZipFile(outputFile).use { file ->
result.dexFiles.forEach {
file.addEntryCompressData(
ZipEntry.createWithName(it.name),
it.stream.readBytes()
)
}

result.resourceFile?.let {
file.copyEntriesFromFileAligned(
ZipFile(it),
ZipAligner::getEntryAlignment
)
}

file.copyEntriesFromFileAligned(
ZipFile(inputFile),
ZipAligner::getEntryAlignment
)
}
}
}
100 changes: 100 additions & 0 deletions app/src/main/java/app/revanced/manager/compose/patcher/Session.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package app.revanced.manager.compose.patcher

import app.revanced.patcher.Patcher
import app.revanced.patcher.PatcherOptions
import app.revanced.patcher.logging.Logger
import android.util.Log
import app.revanced.manager.compose.patcher.worker.Progress
import app.revanced.patcher.data.Context
import app.revanced.patcher.patch.Patch
import java.io.Closeable
import java.io.File
import java.nio.file.Files
import java.nio.file.StandardCopyOption

internal typealias PatchClass = Class<out Patch<Context>>
internal typealias PatchList = List<PatchClass>

class Session(
cacheDir: String,
frameworkDir: String,
aaptPath: String,
private val input: File,
private val onProgress: suspend (Progress) -> Unit = { }
) : Closeable {
class PatchFailedException(val patchName: String, cause: Throwable?) : Exception("Got exception while executing $patchName", cause)

private val logger = LogcatLogger
private val temporary = File(cacheDir).resolve("manager").also { it.mkdirs() }
private val patcher = Patcher(
PatcherOptions(
inputFile = input,
resourceCacheDirectory = temporary.resolve("aapt-resources").path,
frameworkFolderLocation = frameworkDir,
aaptPath = aaptPath,
logger = logger,
)
)

private suspend fun Patcher.applyPatchesVerbose() {
this.executePatches(true).forEach { (patch, result) ->
if (result.isSuccess) {
logger.info("$patch succeeded")
onProgress(Progress.PatchSuccess(patch))
return@forEach
}
logger.error("$patch failed:")
result.exceptionOrNull()!!.printStackTrace()

throw PatchFailedException(patch, result.exceptionOrNull())
}
}

suspend fun run(output: File, selectedPatches: PatchList, integrations: List<File>) {
onProgress(Progress.Merging)

with(patcher) {
logger.info("Merging integrations")
addIntegrations(integrations) {}
addPatches(selectedPatches)

logger.info("Applying patches...")
onProgress(Progress.PatchingStart)

applyPatchesVerbose()
}

onProgress(Progress.Saving)
logger.info("Writing patched files...")
val result = patcher.save()

val aligned = temporary.resolve("aligned.apk").also { Aligning.align(result, input, it) }

logger.info("Patched apk saved to $aligned")

Files.move(aligned.toPath(), output.toPath(), StandardCopyOption.REPLACE_EXISTING)
}

override fun close() {
temporary.delete()
}
}

private object LogcatLogger : Logger {
private const val tag = "revanced-patcher"
override fun error(msg: String) {
Log.e(tag, msg)
}

override fun warn(msg: String) {
Log.w(tag, msg)
}

override fun info(msg: String) {
Log.i(tag, msg)
}

override fun trace(msg: String) {
Log.v(tag, msg)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package app.revanced.manager.compose.patcher.aapt

import android.content.Context
import java.io.File

object Aapt {
fun binary(context: Context): File? {
return File(context.applicationInfo.nativeLibraryDir).resolveAapt()
}
}

private fun File.resolveAapt() =
list { _, f -> !File(f).isDirectory && f.contains("aapt") }?.firstOrNull()?.let { resolve(it) }
Loading

0 comments on commit 437a9f1

Please sign in to comment.