mirror of
https://github.com/revanced/revanced-manager-compose-old.git
synced 2025-04-29 22:14:28 +02:00
feat: switch back to WorkManager for patching
This commit is contained in:
parent
f68a9b31a9
commit
a3e41552f8
@ -81,11 +81,13 @@ dependencies {
|
||||
|
||||
// AndroidX activity
|
||||
implementation("androidx.activity:activity-compose:1.6.1")
|
||||
implementation("androidx.work:work-runtime-ktx:2.7.1")
|
||||
|
||||
// Koin
|
||||
val koinVersion = "3.3.0"
|
||||
implementation("io.insert-koin:koin-android:$koinVersion")
|
||||
implementation("io.insert-koin:koin-androidx-compose:3.3.0")
|
||||
implementation("io.insert-koin:koin-androidx-workmanager:$koinVersion")
|
||||
|
||||
// Compose
|
||||
val composeVersion = "1.4.0-alpha01"
|
||||
@ -116,7 +118,7 @@ dependencies {
|
||||
|
||||
// Signing & aligning
|
||||
implementation("org.bouncycastle:bcpkix-jdk15on:1.70")
|
||||
implementation("com.android.tools.build:apksig:8.0.0-alpha08")
|
||||
implementation("com.android.tools.build:apksig:8.0.0-alpha09")
|
||||
|
||||
// Licenses
|
||||
implementation("com.mikepenz:aboutlibraries-compose:10.5.1")
|
||||
|
@ -9,6 +9,7 @@
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
@ -51,6 +52,17 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<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>
|
||||
|
||||
<service android:name=".installer.service.InstallService" />
|
||||
<service android:name=".installer.service.UninstallService" />
|
||||
</application>
|
||||
|
@ -3,6 +3,7 @@ package app.revanced.manager
|
||||
import android.app.Application
|
||||
import app.revanced.manager.di.*
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.androidx.workmanager.koin.workManagerFactory
|
||||
import org.koin.core.context.startKoin
|
||||
|
||||
class ManagerApplication : Application() {
|
||||
@ -11,11 +12,13 @@ class ManagerApplication : Application() {
|
||||
|
||||
startKoin {
|
||||
androidContext(this@ManagerApplication)
|
||||
workManagerFactory()
|
||||
modules(
|
||||
httpModule,
|
||||
preferencesModule,
|
||||
viewModelModule,
|
||||
repositoryModule,
|
||||
workerModule,
|
||||
patcherModule,
|
||||
serviceModule
|
||||
)
|
||||
|
10
app/src/main/java/app/revanced/manager/di/WorkerModule.kt
Normal file
10
app/src/main/java/app/revanced/manager/di/WorkerModule.kt
Normal file
@ -0,0 +1,10 @@
|
||||
package app.revanced.manager.di
|
||||
|
||||
import app.revanced.manager.patcher.worker.PatcherWorker
|
||||
import org.koin.android.ext.koin.androidContext
|
||||
import org.koin.androidx.workmanager.dsl.worker
|
||||
import org.koin.dsl.module
|
||||
|
||||
val workerModule = module {
|
||||
worker { PatcherWorker(androidContext(), get(), get(), get()) }
|
||||
}
|
@ -0,0 +1,244 @@
|
||||
package app.revanced.manager.patcher.worker
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.Icon
|
||||
import android.os.Environment
|
||||
import android.os.PowerManager
|
||||
import android.util.Log
|
||||
import android.view.WindowManager
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.work.CoroutineWorker
|
||||
import androidx.work.ForegroundInfo
|
||||
import androidx.work.WorkerParameters
|
||||
import app.revanced.manager.R
|
||||
import app.revanced.manager.network.api.ManagerAPI
|
||||
import app.revanced.manager.patcher.PatcherUtils
|
||||
import app.revanced.manager.patcher.aapt.Aapt
|
||||
import app.revanced.manager.patcher.aligning.ZipAligner
|
||||
import app.revanced.manager.patcher.aligning.zip.ZipFile
|
||||
import app.revanced.manager.patcher.aligning.zip.structures.ZipEntry
|
||||
import app.revanced.manager.patcher.signing.Signer
|
||||
import app.revanced.manager.util.tag
|
||||
import app.revanced.patcher.Patcher
|
||||
import app.revanced.patcher.PatcherOptions
|
||||
import app.revanced.patcher.logging.Logger
|
||||
import io.sentry.Sentry
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koin.core.component.KoinComponent
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.StandardCopyOption
|
||||
|
||||
class PatcherWorker(
|
||||
context: Context,
|
||||
parameters: WorkerParameters,
|
||||
private val managerAPI: ManagerAPI,
|
||||
private val patcherUtils: PatcherUtils
|
||||
) : CoroutineWorker(context, parameters), KoinComponent {
|
||||
private val workdir = createWorkDir()
|
||||
|
||||
override suspend fun getForegroundInfo(): ForegroundInfo {
|
||||
return ForegroundInfo(1, createNotification())
|
||||
}
|
||||
|
||||
private fun createNotification(): Notification {
|
||||
val notificationIntent = Intent(applicationContext, PatcherWorker::class.java)
|
||||
val pendingIntent: PendingIntent = PendingIntent.getActivity(
|
||||
applicationContext, 0, notificationIntent, PendingIntent.FLAG_IMMUTABLE
|
||||
)
|
||||
val channel = NotificationChannel(
|
||||
"revanced-patcher-patching", "Patching", NotificationManager.IMPORTANCE_HIGH
|
||||
)
|
||||
val notificationManager =
|
||||
ContextCompat.getSystemService(applicationContext, NotificationManager::class.java)
|
||||
notificationManager!!.createNotificationChannel(channel)
|
||||
return Notification.Builder(applicationContext, channel.id)
|
||||
.setContentTitle(applicationContext.getText(R.string.app_name))
|
||||
.setContentText(applicationContext.getText(R.string.patcher_notification_message))
|
||||
.setLargeIcon(Icon.createWithResource(applicationContext, R.drawable.manager))
|
||||
.setSmallIcon(Icon.createWithResource(applicationContext, R.drawable.manager))
|
||||
.setContentIntent(pendingIntent).build()
|
||||
}
|
||||
|
||||
override suspend fun doWork(): Result {
|
||||
if (runAttemptCount > 0) {
|
||||
Log.d(tag, "Android requested retrying but retrying is disabled.")
|
||||
return Result.failure() // don't retry
|
||||
}
|
||||
|
||||
try {
|
||||
setForeground(ForegroundInfo(1, createNotification()))
|
||||
} catch (e: Exception) {
|
||||
Log.d(tag, "Failed to set foreground info:", e)
|
||||
Sentry.captureException(e)
|
||||
}
|
||||
|
||||
return try {
|
||||
runPatcher(workdir)
|
||||
Result.success()
|
||||
} catch (e: Exception) {
|
||||
log("Error while patching: ${e::class.simpleName}: ${e.message}", ERROR)
|
||||
Log.e(tag, e.stackTraceToString())
|
||||
Sentry.captureException(e)
|
||||
Result.failure()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun runPatcher(
|
||||
workdir: File
|
||||
): Boolean {
|
||||
val wakeLock: PowerManager.WakeLock =
|
||||
(applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager).run {
|
||||
newWakeLock(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, "$tag::Patcher").apply {
|
||||
acquire(10 * 60 * 1000L)
|
||||
}
|
||||
}
|
||||
Log.d(tag, "Acquired wakelock.")
|
||||
|
||||
try {
|
||||
val aaptPath = Aapt.binary(applicationContext)?.absolutePath
|
||||
if (aaptPath == null) {
|
||||
log("AAPT2 not found.", ERROR)
|
||||
throw FileNotFoundException()
|
||||
}
|
||||
val frameworkPath =
|
||||
applicationContext.filesDir.resolve("framework").also { it.mkdirs() }.absolutePath
|
||||
val integrationsCacheDir =
|
||||
applicationContext.filesDir.resolve("integrations-cache").also { it.mkdirs() }
|
||||
val reVancedFolder =
|
||||
Environment.getExternalStorageDirectory().resolve("ReVanced").also { it.mkdirs() }
|
||||
val appInfo = patcherUtils.selectedAppPackage.value.get()
|
||||
val appPath = patcherUtils.selectedAppPackagePath.value
|
||||
|
||||
log("Checking prerequisites...", INFO)
|
||||
val patches = patcherUtils.findPatchesByIds(patcherUtils.selectedPatches)
|
||||
if (patches.isEmpty()) throw IllegalStateException("No patches selected.")
|
||||
|
||||
log("Creating directories...", INFO)
|
||||
val inputFile = File(workdir, "input.apk")
|
||||
val patchedFile = File(workdir, "patched.apk")
|
||||
val outputFile = File(inputData.getString("output")!!)
|
||||
val cacheDirectory = workdir.resolve("cache")
|
||||
|
||||
val integrations = managerAPI.downloadIntegrations(integrationsCacheDir)
|
||||
|
||||
log("Copying APK from device...", INFO)
|
||||
withContext(Dispatchers.IO) {
|
||||
Files.copy(
|
||||
File(appPath ?: appInfo.publicSourceDir).toPath(),
|
||||
inputFile.toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING
|
||||
)
|
||||
}
|
||||
log("Decoding resources", INFO)
|
||||
val patcher = Patcher( // start patcher
|
||||
PatcherOptions(inputFile,
|
||||
cacheDirectory.absolutePath,
|
||||
aaptPath = aaptPath,
|
||||
frameworkFolderLocation = frameworkPath,
|
||||
logger = object : Logger {
|
||||
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)
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
Log.d(tag, "Adding ${patches.size} patch(es)")
|
||||
patcher.addPatches(patches)
|
||||
|
||||
log("Merging integrations", INFO)
|
||||
patcher.addFiles(listOf(integrations)) {}
|
||||
|
||||
val patchesString = if (patches.size > 1) "patches" else "patch"
|
||||
log("Applying ${patches.size} $patchesString", INFO)
|
||||
patcher.executePatches().forEach { (patch, result) ->
|
||||
if (result.isFailure) {
|
||||
log(
|
||||
"Failed to apply $patch: " + "${result.exceptionOrNull()!!.message ?: result.exceptionOrNull()!!::class.simpleName}",
|
||||
ERROR
|
||||
)
|
||||
Log.e(tag, result.exceptionOrNull()!!.stackTraceToString())
|
||||
return@forEach
|
||||
}
|
||||
}
|
||||
|
||||
log("Saving file", INFO)
|
||||
val result = patcher.save() // compile apk
|
||||
|
||||
ZipFile(patchedFile).use { fs ->
|
||||
result.dexFiles.forEach {
|
||||
log("Writing dex file ${it.name}", INFO)
|
||||
fs.addEntryCompressData(ZipEntry.createWithName(it.name), it.stream.readBytes())
|
||||
}
|
||||
|
||||
log("Aligning apk...", INFO)
|
||||
result.resourceFile?.let {
|
||||
fs.copyEntriesFromFileAligned(ZipFile(it), ZipAligner::getEntryAlignment)
|
||||
}
|
||||
fs.copyEntriesFromFileAligned(ZipFile(inputFile), ZipAligner::getEntryAlignment)
|
||||
}
|
||||
|
||||
log("Signing apk...", INFO)
|
||||
Signer("ReVanced", "s3cur3p@ssw0rd").signApk(patchedFile, outputFile)
|
||||
withContext(Dispatchers.IO) {
|
||||
Files.copy(
|
||||
outputFile.inputStream(),
|
||||
reVancedFolder.resolve(appInfo.packageName + ".apk").toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING
|
||||
)
|
||||
}
|
||||
log("Successfully patched!", SUCCESS)
|
||||
patcherUtils.cleanup()
|
||||
} finally {
|
||||
Log.d(tag, "Deleting workdir")
|
||||
workdir.deleteRecursively()
|
||||
wakeLock.release()
|
||||
Log.d(tag, "Released wakelock.")
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
private fun createWorkDir(): File {
|
||||
return applicationContext.cacheDir.resolve("tmp-${System.currentTimeMillis()}")
|
||||
.also { it.mkdirs() }
|
||||
}
|
||||
|
||||
private fun log(message: String, status: Int) {
|
||||
applicationContext.sendBroadcast(Intent().apply {
|
||||
action = PATCH_LOG
|
||||
putExtra(PATCH_MESSAGE, message)
|
||||
putExtra(PATCH_STATUS, status)
|
||||
})
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PATCH_MESSAGE = "PATCH_MESSAGE"
|
||||
const val PATCH_STATUS = "PATCH_STATUS"
|
||||
const val PATCH_LOG = "PATCH_LOG"
|
||||
|
||||
const val INFO = 0
|
||||
const val ERROR = 1
|
||||
const val SUCCESS = 2
|
||||
}
|
||||
}
|
@ -6,51 +6,23 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.os.Environment
|
||||
import android.os.PowerManager
|
||||
import android.util.Log
|
||||
import android.view.WindowManager
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateListOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.work.*
|
||||
import app.revanced.manager.installer.service.InstallService
|
||||
import app.revanced.manager.installer.service.UninstallService
|
||||
import app.revanced.manager.installer.utils.PM
|
||||
import app.revanced.manager.network.api.ManagerAPI
|
||||
import app.revanced.manager.patcher.PatcherUtils
|
||||
import app.revanced.manager.patcher.aapt.Aapt
|
||||
import app.revanced.manager.patcher.aligning.ZipAligner
|
||||
import app.revanced.manager.patcher.aligning.zip.ZipFile
|
||||
import app.revanced.manager.patcher.aligning.zip.structures.ZipEntry
|
||||
import app.revanced.manager.patcher.signing.Signer
|
||||
import app.revanced.manager.util.tag
|
||||
import app.revanced.patcher.Patcher
|
||||
import app.revanced.patcher.PatcherOptions
|
||||
import app.revanced.patcher.logging.Logger
|
||||
import io.sentry.Sentry
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import app.revanced.manager.patcher.worker.PatcherWorker
|
||||
import java.io.File
|
||||
import java.io.FileNotFoundException
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.StandardCopyOption
|
||||
import java.util.concurrent.CancellationException
|
||||
|
||||
class PatchingScreenViewModel(
|
||||
private val app: Application,
|
||||
private val managerAPI: ManagerAPI,
|
||||
private val patcherUtils: PatcherUtils
|
||||
) : ViewModel() {
|
||||
|
||||
var installFailure by mutableStateOf(false)
|
||||
|
||||
var pmStatus by mutableStateOf(-999)
|
||||
var extra by mutableStateOf("")
|
||||
|
||||
sealed interface PatchLog {
|
||||
val message: String
|
||||
|
||||
@ -66,7 +38,30 @@ class PatchingScreenViewModel(
|
||||
object Failure : Status()
|
||||
}
|
||||
|
||||
val outputFile = File(app.filesDir, "output.apk")
|
||||
val workManager = WorkManager.getInstance(app)
|
||||
var installFailure by mutableStateOf(false)
|
||||
var pmStatus by mutableStateOf(-999)
|
||||
var extra by mutableStateOf("")
|
||||
|
||||
val outputFile = File(app.cacheDir, "output.apk")
|
||||
|
||||
private val patcherWorker =
|
||||
OneTimeWorkRequest.Builder(PatcherWorker::class.java) // create Worker
|
||||
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST).setInputData(
|
||||
Data.Builder().putString("output", outputFile.path).build()
|
||||
).build()
|
||||
|
||||
private val liveData = workManager.getWorkInfoByIdLiveData(patcherWorker.id) // get LiveData
|
||||
|
||||
private val observer = Observer { workInfo: WorkInfo -> // observer for observing patch status
|
||||
status = when (workInfo.state) {
|
||||
WorkInfo.State.RUNNING -> Status.Patching
|
||||
WorkInfo.State.SUCCEEDED -> Status.Success
|
||||
WorkInfo.State.FAILED -> Status.Failure
|
||||
else -> Status.Idle
|
||||
}
|
||||
}
|
||||
|
||||
val logs = mutableStateListOf<PatchLog>()
|
||||
var status by mutableStateOf<Status>(Status.Idle)
|
||||
|
||||
@ -81,18 +76,29 @@ class PatchingScreenViewModel(
|
||||
}
|
||||
UninstallService.APP_UNINSTALL_ACTION -> {
|
||||
}
|
||||
PatcherWorker.PATCH_LOG -> {
|
||||
val message = intent.getStringExtra(PatcherWorker.PATCH_MESSAGE)
|
||||
val patchLog =
|
||||
when (intent.getIntExtra(PatcherWorker.PATCH_STATUS, PatcherWorker.INFO)) {
|
||||
PatcherWorker.INFO -> PatchLog.Info(message!!)
|
||||
PatcherWorker.SUCCESS -> PatchLog.Success(message!!)
|
||||
PatcherWorker.ERROR -> PatchLog.Error(message!!)
|
||||
else -> null
|
||||
}
|
||||
patchLog?.let { log(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
app.registerReceiver(
|
||||
installBroadcastReceiver,
|
||||
IntentFilter().apply {
|
||||
addAction(InstallService.APP_INSTALL_ACTION)
|
||||
addAction(UninstallService.APP_UNINSTALL_ACTION)
|
||||
}
|
||||
)
|
||||
workManager.enqueueUniqueWork("patching", ExistingWorkPolicy.KEEP, patcherWorker)
|
||||
liveData.observeForever(observer)
|
||||
app.registerReceiver(installBroadcastReceiver, IntentFilter().apply {
|
||||
addAction(InstallService.APP_INSTALL_ACTION)
|
||||
addAction(UninstallService.APP_UNINSTALL_ACTION)
|
||||
addAction(PatcherWorker.PATCH_LOG)
|
||||
})
|
||||
}
|
||||
|
||||
fun installApk(apk: File) {
|
||||
@ -109,145 +115,14 @@ class PatchingScreenViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
private val patcher = viewModelScope.launch(Dispatchers.IO) {
|
||||
withContext(Dispatchers.Main) {
|
||||
status = Status.Patching
|
||||
}
|
||||
val workdir = createWorkDir()
|
||||
val wakeLock: PowerManager.WakeLock =
|
||||
(app.getSystemService(Context.POWER_SERVICE) as PowerManager).run {
|
||||
newWakeLock(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, "$tag::Patcher").apply {
|
||||
acquire(10 * 60 * 1000L)
|
||||
}
|
||||
}
|
||||
Log.d(tag, "Acquired wakelock.")
|
||||
try {
|
||||
val aaptPath = Aapt.binary(app)?.absolutePath
|
||||
if (aaptPath == null) {
|
||||
log(PatchLog.Error("AAPT2 not found."))
|
||||
throw FileNotFoundException()
|
||||
}
|
||||
val frameworkPath = app.filesDir.resolve("framework").also { it.mkdirs() }.absolutePath
|
||||
val integrationsCacheDir =
|
||||
app.filesDir.resolve("integrations-cache").also { it.mkdirs() }
|
||||
val reVancedFolder =
|
||||
Environment.getExternalStorageDirectory().resolve("ReVanced").also { it.mkdirs() }
|
||||
val appInfo = patcherUtils.selectedAppPackage.value.get()
|
||||
val appPath = patcherUtils.selectedAppPackagePath.value
|
||||
|
||||
log(PatchLog.Info("Checking prerequisites..."))
|
||||
val patches = patcherUtils.findPatchesByIds(patcherUtils.selectedPatches)
|
||||
if (patches.isEmpty()) throw IllegalStateException("No patches selected.")
|
||||
|
||||
log(PatchLog.Info("Creating directories..."))
|
||||
val inputFile = File(app.filesDir, "input.apk")
|
||||
val patchedFile = File(workdir, "patched.apk")
|
||||
val cacheDirectory = workdir.resolve("cache")
|
||||
|
||||
val integrations = managerAPI.downloadIntegrations(integrationsCacheDir)
|
||||
|
||||
log(PatchLog.Info("Copying APK from device..."))
|
||||
withContext(Dispatchers.IO) {
|
||||
Files.copy(
|
||||
File(appPath ?: appInfo.publicSourceDir).toPath(),
|
||||
inputFile.toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING
|
||||
)
|
||||
}
|
||||
log(PatchLog.Info("Decoding resources"))
|
||||
val patcher = Patcher( // start patcher
|
||||
PatcherOptions(
|
||||
inputFile,
|
||||
cacheDirectory.absolutePath,
|
||||
aaptPath = aaptPath,
|
||||
frameworkFolderLocation = frameworkPath,
|
||||
logger = object : Logger {
|
||||
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)
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
|
||||
Log.d(tag, "Adding ${patches.size} patch(es)")
|
||||
patcher.addPatches(patches)
|
||||
|
||||
log(PatchLog.Info("Merging integrations"))
|
||||
patcher.addFiles(listOf(integrations)) {}
|
||||
|
||||
val patchesString = if (patches.size > 1) "patches" else "patch"
|
||||
log(PatchLog.Info("Applying ${patches.size} $patchesString"))
|
||||
patcher.executePatches().forEach { (patch, result) ->
|
||||
if (result.isFailure) {
|
||||
log(PatchLog.Info("Failed to apply $patch: " + "${result.exceptionOrNull()!!.message ?: result.exceptionOrNull()!!::class.simpleName}"))
|
||||
Log.e(tag, result.exceptionOrNull()!!.stackTraceToString())
|
||||
return@forEach
|
||||
}
|
||||
}
|
||||
|
||||
log(PatchLog.Info("Saving file"))
|
||||
val result = patcher.save() // compile apk
|
||||
|
||||
ZipFile(patchedFile).use { fs ->
|
||||
result.dexFiles.forEach {
|
||||
log(PatchLog.Info("Writing dex file ${it.name}"))
|
||||
fs.addEntryCompressData(ZipEntry.createWithName(it.name), it.stream.readBytes())
|
||||
}
|
||||
|
||||
log(PatchLog.Info("Aligning apk..."))
|
||||
result.resourceFile?.let {
|
||||
fs.copyEntriesFromFileAligned(ZipFile(it), ZipAligner::getEntryAlignment)
|
||||
}
|
||||
fs.copyEntriesFromFileAligned(ZipFile(inputFile), ZipAligner::getEntryAlignment)
|
||||
}
|
||||
|
||||
log(PatchLog.Info("Signing apk..."))
|
||||
Signer("ReVanced", "s3cur3p@ssw0rd").signApk(patchedFile, outputFile)
|
||||
withContext(Dispatchers.IO) {
|
||||
Files.copy(
|
||||
outputFile.inputStream(),
|
||||
reVancedFolder.resolve(appInfo.packageName + ".apk").toPath(),
|
||||
StandardCopyOption.REPLACE_EXISTING
|
||||
)
|
||||
}
|
||||
log(PatchLog.Success("Successfully patched!"))
|
||||
patcherUtils.cleanup()
|
||||
status = Status.Success
|
||||
} catch (e: Exception) {
|
||||
status = Status.Failure
|
||||
log(PatchLog.Error("Error while patching: ${e::class.simpleName}: ${e.message}"))
|
||||
Log.e(tag, e.stackTraceToString())
|
||||
Sentry.captureException(e)
|
||||
}
|
||||
Log.d(tag, "Deleting workdir")
|
||||
workdir.deleteRecursively()
|
||||
wakeLock.release()
|
||||
Log.d(tag, "Released wakelock.")
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
liveData.removeObserver(observer)
|
||||
app.unregisterReceiver(installBroadcastReceiver)
|
||||
patcher.cancel(CancellationException("ViewModel cleared"))
|
||||
workManager.cancelWorkById(patcherWorker.id)
|
||||
logs.clear()
|
||||
}
|
||||
|
||||
private fun createWorkDir(): File {
|
||||
return app.cacheDir.resolve("tmp-${System.currentTimeMillis()}").also { it.mkdirs() }
|
||||
}
|
||||
|
||||
private fun log(data: PatchLog) {
|
||||
logs.add(data)
|
||||
}
|
||||
|
@ -34,8 +34,7 @@
|
||||
<string name="manager_contributors">Manager</string>
|
||||
<string name="integrations_contributors">Integrations</string>
|
||||
<string name="unsupported_version">Unsupported version</string>
|
||||
<string name="patcher_notification_title">Patching</string>
|
||||
<string name="patcher_notification_message">ReVanced Manager is patching</string>
|
||||
<string name="patcher_notification_message">Patching in progress…</string>
|
||||
<string name="theme">Theme</string>
|
||||
<string name="apply">Apply</string>
|
||||
<string name="warning">Warning</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user