mirror of
https://github.com/revanced/revanced-manager.git
synced 2025-04-29 21:44:26 +02:00
build(Needs bump): Bump dependencies (#1717)
This commit is contained in:
parent
6dee3aa1b7
commit
10bae69db6
@ -113,12 +113,9 @@ flutter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.10"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.9.22"
|
||||||
|
|
||||||
// ReVanced
|
// ReVanced
|
||||||
implementation "app.revanced:revanced-patcher:19.1.0"
|
implementation "app.revanced:revanced-patcher:19.3.1"
|
||||||
|
implementation "app.revanced:revanced-library:2.0.0"
|
||||||
// Signing & aligning
|
|
||||||
implementation("org.bouncycastle:bcpkix-jdk15on:1.70")
|
|
||||||
implementation("com.android.tools.build:apksig:7.2.2")
|
|
||||||
}
|
}
|
||||||
|
@ -7,23 +7,20 @@ import android.content.pm.PackageInstaller
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
|
import app.revanced.library.ApkUtils
|
||||||
|
import app.revanced.library.ApkUtils.applyTo
|
||||||
|
import app.revanced.library.ApkUtils.sign
|
||||||
import app.revanced.manager.flutter.utils.Aapt
|
import app.revanced.manager.flutter.utils.Aapt
|
||||||
import app.revanced.manager.flutter.utils.aligning.ZipAligner
|
|
||||||
import app.revanced.manager.flutter.utils.packageInstaller.InstallerReceiver
|
import app.revanced.manager.flutter.utils.packageInstaller.InstallerReceiver
|
||||||
import app.revanced.manager.flutter.utils.packageInstaller.UninstallerReceiver
|
import app.revanced.manager.flutter.utils.packageInstaller.UninstallerReceiver
|
||||||
import app.revanced.manager.flutter.utils.signing.Signer
|
|
||||||
import app.revanced.manager.flutter.utils.zip.ZipFile
|
|
||||||
import app.revanced.manager.flutter.utils.zip.structures.ZipEntry
|
|
||||||
import app.revanced.patcher.PatchBundleLoader
|
import app.revanced.patcher.PatchBundleLoader
|
||||||
import app.revanced.patcher.PatchSet
|
import app.revanced.patcher.PatchSet
|
||||||
import app.revanced.patcher.Patcher
|
import app.revanced.patcher.Patcher
|
||||||
import app.revanced.patcher.PatcherOptions
|
import app.revanced.patcher.PatcherConfig
|
||||||
import app.revanced.patcher.patch.PatchResult
|
import app.revanced.patcher.patch.PatchResult
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
import io.flutter.embedding.engine.FlutterEngine
|
import io.flutter.embedding.engine.FlutterEngine
|
||||||
import io.flutter.plugin.common.MethodChannel
|
import io.flutter.plugin.common.MethodChannel
|
||||||
import kotlinx.coroutines.InternalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.cancel
|
|
||||||
import kotlinx.coroutines.flow.FlowCollector
|
import kotlinx.coroutines.flow.FlowCollector
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
@ -50,7 +47,10 @@ class MainActivity : FlutterActivity() {
|
|||||||
val installerChannel = "app.revanced.manager.flutter/installer"
|
val installerChannel = "app.revanced.manager.flutter/installer"
|
||||||
val openBrowserChannel = "app.revanced.manager.flutter/browser"
|
val openBrowserChannel = "app.revanced.manager.flutter/browser"
|
||||||
|
|
||||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, openBrowserChannel).setMethodCallHandler { call, result ->
|
MethodChannel(
|
||||||
|
flutterEngine.dartExecutor.binaryMessenger,
|
||||||
|
openBrowserChannel
|
||||||
|
).setMethodCallHandler { call, result ->
|
||||||
if (call.method == "openBrowser") {
|
if (call.method == "openBrowser") {
|
||||||
val searchQuery = call.argument<String>("query")
|
val searchQuery = call.argument<String>("query")
|
||||||
openBrowser(searchQuery)
|
openBrowser(searchQuery)
|
||||||
@ -69,40 +69,34 @@ class MainActivity : FlutterActivity() {
|
|||||||
mainChannel.setMethodCallHandler { call, result ->
|
mainChannel.setMethodCallHandler { call, result ->
|
||||||
when (call.method) {
|
when (call.method) {
|
||||||
"runPatcher" -> {
|
"runPatcher" -> {
|
||||||
val originalFilePath = call.argument<String>("originalFilePath")
|
val inFilePath = call.argument<String>("inFilePath")
|
||||||
val inputFilePath = call.argument<String>("inputFilePath")
|
|
||||||
val patchedFilePath = call.argument<String>("patchedFilePath")
|
|
||||||
val outFilePath = call.argument<String>("outFilePath")
|
val outFilePath = call.argument<String>("outFilePath")
|
||||||
val integrationsPath = call.argument<String>("integrationsPath")
|
val integrationsPath = call.argument<String>("integrationsPath")
|
||||||
val selectedPatches = call.argument<List<String>>("selectedPatches")
|
val selectedPatches = call.argument<List<String>>("selectedPatches")
|
||||||
val options = call.argument<Map<String, Map<String, Any>>>("options")
|
val options = call.argument<Map<String, Map<String, Any>>>("options")
|
||||||
val cacheDirPath = call.argument<String>("cacheDirPath")
|
val tmpDirPath = call.argument<String>("tmpDirPath")
|
||||||
val keyStoreFilePath = call.argument<String>("keyStoreFilePath")
|
val keyStoreFilePath = call.argument<String>("keyStoreFilePath")
|
||||||
val keystorePassword = call.argument<String>("keystorePassword")
|
val keystorePassword = call.argument<String>("keystorePassword")
|
||||||
|
|
||||||
if (
|
if (
|
||||||
originalFilePath != null &&
|
inFilePath != null &&
|
||||||
inputFilePath != null &&
|
|
||||||
patchedFilePath != null &&
|
|
||||||
outFilePath != null &&
|
outFilePath != null &&
|
||||||
integrationsPath != null &&
|
integrationsPath != null &&
|
||||||
selectedPatches != null &&
|
selectedPatches != null &&
|
||||||
options != null &&
|
options != null &&
|
||||||
cacheDirPath != null &&
|
tmpDirPath != null &&
|
||||||
keyStoreFilePath != null &&
|
keyStoreFilePath != null &&
|
||||||
keystorePassword != null
|
keystorePassword != null
|
||||||
) {
|
) {
|
||||||
cancel = false
|
cancel = false
|
||||||
runPatcher(
|
runPatcher(
|
||||||
result,
|
result,
|
||||||
originalFilePath,
|
inFilePath,
|
||||||
inputFilePath,
|
|
||||||
patchedFilePath,
|
|
||||||
outFilePath,
|
outFilePath,
|
||||||
integrationsPath,
|
integrationsPath,
|
||||||
selectedPatches,
|
selectedPatches,
|
||||||
options,
|
options,
|
||||||
cacheDirPath,
|
tmpDirPath,
|
||||||
keyStoreFilePath,
|
keyStoreFilePath,
|
||||||
keystorePassword
|
keystorePassword
|
||||||
)
|
)
|
||||||
@ -214,28 +208,23 @@ class MainActivity : FlutterActivity() {
|
|||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(InternalCoroutinesApi::class)
|
|
||||||
private fun runPatcher(
|
private fun runPatcher(
|
||||||
result: MethodChannel.Result,
|
result: MethodChannel.Result,
|
||||||
originalFilePath: String,
|
inFilePath: String,
|
||||||
inputFilePath: String,
|
|
||||||
patchedFilePath: String,
|
|
||||||
outFilePath: String,
|
outFilePath: String,
|
||||||
integrationsPath: String,
|
integrationsPath: String,
|
||||||
selectedPatches: List<String>,
|
selectedPatches: List<String>,
|
||||||
options: Map<String, Map<String, Any>>,
|
options: Map<String, Map<String, Any>>,
|
||||||
cacheDirPath: String,
|
tmpDirPath: String,
|
||||||
keyStoreFilePath: String,
|
keyStoreFilePath: String,
|
||||||
keystorePassword: String
|
keystorePassword: String
|
||||||
) {
|
) {
|
||||||
val originalFile = File(originalFilePath)
|
val inFile = File(inFilePath)
|
||||||
val inputFile = File(inputFilePath)
|
|
||||||
val patchedFile = File(patchedFilePath)
|
|
||||||
val outFile = File(outFilePath)
|
val outFile = File(outFilePath)
|
||||||
val integrations = File(integrationsPath)
|
val integrations = File(integrationsPath)
|
||||||
val keyStoreFile = File(keyStoreFilePath)
|
val keyStoreFile = File(keyStoreFilePath)
|
||||||
val cacheDir = File(cacheDirPath)
|
val tmpDir = File(tmpDirPath)
|
||||||
|
|
||||||
Thread {
|
Thread {
|
||||||
fun updateProgress(progress: Double, header: String, log: String) {
|
fun updateProgress(progress: Double, header: String, log: String) {
|
||||||
@ -253,6 +242,16 @@ class MainActivity : FlutterActivity() {
|
|||||||
|
|
||||||
fun postStop() = handler.post { stopResult!!.success(null) }
|
fun postStop() = handler.post { stopResult!!.success(null) }
|
||||||
|
|
||||||
|
fun cancel(block: () -> Unit = {}): Boolean {
|
||||||
|
if (cancel) {
|
||||||
|
block()
|
||||||
|
postStop()
|
||||||
|
}
|
||||||
|
|
||||||
|
return cancel
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Setup logger
|
// Setup logger
|
||||||
Logger.getLogger("").apply {
|
Logger.getLogger("").apply {
|
||||||
handlers.forEach {
|
handlers.forEach {
|
||||||
@ -273,37 +272,19 @@ class MainActivity : FlutterActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
updateProgress(0.0, "", "Copying APK")
|
updateProgress(0.0, "Reading APK...", "Reading APK")
|
||||||
|
|
||||||
if (cancel) {
|
|
||||||
postStop()
|
|
||||||
return@Thread
|
|
||||||
}
|
|
||||||
|
|
||||||
originalFile.copyTo(inputFile, true)
|
|
||||||
|
|
||||||
if (cancel) {
|
|
||||||
postStop()
|
|
||||||
return@Thread
|
|
||||||
}
|
|
||||||
|
|
||||||
updateProgress(0.05, "Reading APK...", "Reading APK")
|
|
||||||
|
|
||||||
val patcher = Patcher(
|
val patcher = Patcher(
|
||||||
PatcherOptions(
|
PatcherConfig(
|
||||||
inputFile,
|
inFile,
|
||||||
cacheDir,
|
tmpDir,
|
||||||
Aapt.binary(applicationContext).absolutePath,
|
Aapt.binary(applicationContext).absolutePath,
|
||||||
cacheDir.path,
|
tmpDir.path,
|
||||||
true // TODO: Add option to disable this
|
true // TODO: Add option to disable this
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if (cancel) {
|
if (cancel(patcher::close)) return@Thread
|
||||||
postStop()
|
|
||||||
return@Thread
|
|
||||||
}
|
|
||||||
|
|
||||||
updateProgress(0.1, "Loading patches...", "Loading patches")
|
updateProgress(0.1, "Loading patches...", "Loading patches")
|
||||||
|
|
||||||
val patches = patches.filter { patch ->
|
val patches = patches.filter { patch ->
|
||||||
@ -319,32 +300,25 @@ class MainActivity : FlutterActivity() {
|
|||||||
options[patch.name]?.forEach { (key, value) ->
|
options[patch.name]?.forEach { (key, value) ->
|
||||||
patch.options[key] = value
|
patch.options[key] = value
|
||||||
}
|
}
|
||||||
}
|
}.toSet()
|
||||||
|
|
||||||
if (cancel) {
|
|
||||||
postStop()
|
|
||||||
return@Thread
|
|
||||||
}
|
|
||||||
|
|
||||||
|
if (cancel(patcher::close)) return@Thread
|
||||||
updateProgress(0.15, "Executing...", "")
|
updateProgress(0.15, "Executing...", "")
|
||||||
|
|
||||||
// Update the progress bar every time a patch is executed from 0.15 to 0.7
|
val patcherResult = patcher.use {
|
||||||
val totalPatchesCount = patches.size
|
patcher.apply {
|
||||||
val progressStep = 0.55 / totalPatchesCount
|
acceptIntegrations(setOf(integrations))
|
||||||
var progress = 0.15
|
acceptPatches(patches)
|
||||||
|
}
|
||||||
patcher.apply {
|
|
||||||
acceptIntegrations(listOf(integrations))
|
|
||||||
acceptPatches(patches)
|
|
||||||
|
|
||||||
runBlocking {
|
runBlocking {
|
||||||
apply(false).collect(FlowCollector { patchResult: PatchResult ->
|
// Update the progress bar every time a patch is executed from 0.15 to 0.7
|
||||||
if (cancel) {
|
val totalPatchesCount = patches.size
|
||||||
handler.post { stopResult!!.success(null) }
|
val progressStep = 0.55 / totalPatchesCount
|
||||||
this.cancel()
|
var progress = 0.15
|
||||||
this@apply.close()
|
|
||||||
return@FlowCollector
|
patcher.apply(false).collect(FlowCollector { patchResult: PatchResult ->
|
||||||
}
|
if (cancel(patcher::close)) return@FlowCollector
|
||||||
|
|
||||||
val msg = patchResult.exception?.let {
|
val msg = patchResult.exception?.let {
|
||||||
val writer = StringWriter()
|
val writer = StringWriter()
|
||||||
@ -358,50 +332,30 @@ class MainActivity : FlutterActivity() {
|
|||||||
progress += progressStep
|
progress += progressStep
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cancel(patcher::close)) return@Thread
|
||||||
|
updateProgress(0.75, "Building...", "")
|
||||||
|
|
||||||
|
patcher.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cancel) {
|
inFile.copyTo(outFile)
|
||||||
postStop()
|
|
||||||
patcher.close()
|
|
||||||
return@Thread
|
|
||||||
}
|
|
||||||
|
|
||||||
updateProgress(0.75, "Building...", "")
|
if (cancel(patcher::close)) return@Thread
|
||||||
|
|
||||||
val res = patcher.get()
|
patcherResult.applyTo(outFile)
|
||||||
patcher.close()
|
|
||||||
|
|
||||||
ZipFile(patchedFile).use { file ->
|
if (cancel(patcher::close)) return@Thread
|
||||||
res.dexFiles.forEach {
|
updateProgress(0.8, "Signing...", "")
|
||||||
if (cancel) {
|
|
||||||
postStop()
|
outFile.sign(
|
||||||
return@Thread
|
ApkUtils.SigningOptions(
|
||||||
}
|
keyStoreFile,
|
||||||
file.addEntryCompressData(
|
keystorePassword,
|
||||||
ZipEntry.createWithName(it.name),
|
"alias",
|
||||||
it.stream.readBytes()
|
keystorePassword
|
||||||
)
|
|
||||||
}
|
|
||||||
res.resourceFile?.let {
|
|
||||||
file.copyEntriesFromFileAligned(
|
|
||||||
ZipFile(it),
|
|
||||||
ZipAligner::getEntryAlignment
|
|
||||||
)
|
|
||||||
}
|
|
||||||
file.copyEntriesFromFileAligned(
|
|
||||||
ZipFile(inputFile),
|
|
||||||
ZipAligner::getEntryAlignment
|
|
||||||
)
|
)
|
||||||
}
|
)
|
||||||
|
|
||||||
if (cancel) {
|
|
||||||
postStop()
|
|
||||||
return@Thread
|
|
||||||
}
|
|
||||||
|
|
||||||
updateProgress(0.8, "Signing...", "Signing APK")
|
|
||||||
|
|
||||||
Signer("ReVanced", keystorePassword).signApk(patchedFile, outFile, keyStoreFile)
|
|
||||||
|
|
||||||
updateProgress(.85, "Patched", "Patched APK")
|
updateProgress(.85, "Patched", "Patched APK")
|
||||||
} catch (ex: Throwable) {
|
} catch (ex: Throwable) {
|
||||||
@ -421,7 +375,8 @@ class MainActivity : FlutterActivity() {
|
|||||||
|
|
||||||
private fun installApk(apkPath: String) {
|
private fun installApk(apkPath: String) {
|
||||||
val packageInstaller: PackageInstaller = applicationContext.packageManager.packageInstaller
|
val packageInstaller: PackageInstaller = applicationContext.packageManager.packageInstaller
|
||||||
val sessionParams = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
val sessionParams =
|
||||||
|
PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
|
||||||
val sessionId: Int = packageInstaller.createSession(sessionParams)
|
val sessionId: Int = packageInstaller.createSession(sessionParams)
|
||||||
val session: PackageInstaller.Session = packageInstaller.openSession(sessionId)
|
val session: PackageInstaller.Session = packageInstaller.openSession(sessionId)
|
||||||
session.use { activeSession ->
|
session.use { activeSession ->
|
||||||
@ -436,7 +391,12 @@ class MainActivity : FlutterActivity() {
|
|||||||
val receiverIntent = Intent(applicationContext, InstallerReceiver::class.java).apply {
|
val receiverIntent = Intent(applicationContext, InstallerReceiver::class.java).apply {
|
||||||
action = "APP_INSTALL_ACTION"
|
action = "APP_INSTALL_ACTION"
|
||||||
}
|
}
|
||||||
val receiverPendingIntent = PendingIntent.getBroadcast(context, sessionId, receiverIntent, PackageInstallerManager.flags)
|
val receiverPendingIntent = PendingIntent.getBroadcast(
|
||||||
|
context,
|
||||||
|
sessionId,
|
||||||
|
receiverIntent,
|
||||||
|
PackageInstallerManager.flags
|
||||||
|
)
|
||||||
session.commit(receiverPendingIntent.intentSender)
|
session.commit(receiverPendingIntent.intentSender)
|
||||||
session.close()
|
session.close()
|
||||||
}
|
}
|
||||||
@ -446,7 +406,8 @@ class MainActivity : FlutterActivity() {
|
|||||||
val receiverIntent = Intent(applicationContext, UninstallerReceiver::class.java).apply {
|
val receiverIntent = Intent(applicationContext, UninstallerReceiver::class.java).apply {
|
||||||
action = "APP_UNINSTALL_ACTION"
|
action = "APP_UNINSTALL_ACTION"
|
||||||
}
|
}
|
||||||
val receiverPendingIntent = PendingIntent.getBroadcast(context, 0, receiverIntent, PackageInstallerManager.flags)
|
val receiverPendingIntent =
|
||||||
|
PendingIntent.getBroadcast(context, 0, receiverIntent, PackageInstallerManager.flags)
|
||||||
packageInstaller.uninstall(packageName, receiverPendingIntent.intentSender)
|
packageInstaller.uninstall(packageName, receiverPendingIntent.intentSender)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
package app.revanced.manager.flutter.utils.aligning
|
|
||||||
|
|
||||||
import app.revanced.manager.flutter.utils.zip.structures.ZipEntry
|
|
||||||
|
|
||||||
internal object ZipAligner {
|
|
||||||
private const val DEFAULT_ALIGNMENT = 4
|
|
||||||
private const val LIBRARY_ALIGNMENT = 4096
|
|
||||||
|
|
||||||
fun getEntryAlignment(entry: ZipEntry): Int? =
|
|
||||||
if (entry.compression.toUInt() != 0u) null else if (entry.fileName.endsWith(".so")) LIBRARY_ALIGNMENT else DEFAULT_ALIGNMENT
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
package app.revanced.manager.flutter.utils.signing
|
|
||||||
|
|
||||||
import com.android.apksig.ApkSigner
|
|
||||||
import org.bouncycastle.asn1.x500.X500Name
|
|
||||||
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo
|
|
||||||
import org.bouncycastle.cert.X509v3CertificateBuilder
|
|
||||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter
|
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider
|
|
||||||
import org.bouncycastle.operator.ContentSigner
|
|
||||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder
|
|
||||||
import java.io.File
|
|
||||||
import java.io.FileInputStream
|
|
||||||
import java.io.FileOutputStream
|
|
||||||
import java.math.BigInteger
|
|
||||||
import java.security.*
|
|
||||||
import java.security.cert.X509Certificate
|
|
||||||
import java.util.*
|
|
||||||
|
|
||||||
internal class Signer(
|
|
||||||
private val cn: String, password: String
|
|
||||||
) {
|
|
||||||
private val passwordCharArray = password.toCharArray()
|
|
||||||
private fun newKeystore(out: File) {
|
|
||||||
val (publicKey, privateKey) = createKey()
|
|
||||||
val privateKS = KeyStore.getInstance("BKS", "BC")
|
|
||||||
privateKS.load(null, passwordCharArray)
|
|
||||||
privateKS.setKeyEntry("alias", privateKey, passwordCharArray, arrayOf(publicKey))
|
|
||||||
privateKS.store(FileOutputStream(out), passwordCharArray)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createKey(): Pair<X509Certificate, PrivateKey> {
|
|
||||||
val gen = KeyPairGenerator.getInstance("RSA")
|
|
||||||
gen.initialize(2048)
|
|
||||||
val pair = gen.generateKeyPair()
|
|
||||||
var serialNumber: BigInteger
|
|
||||||
do serialNumber =
|
|
||||||
BigInteger.valueOf(SecureRandom().nextLong()) while (serialNumber < BigInteger.ZERO)
|
|
||||||
val x500Name = X500Name("CN=$cn")
|
|
||||||
val builder = X509v3CertificateBuilder(
|
|
||||||
x500Name,
|
|
||||||
serialNumber,
|
|
||||||
Date(System.currentTimeMillis() - 1000L * 60L * 60L * 24L * 30L),
|
|
||||||
Date(System.currentTimeMillis() + 1000L * 60L * 60L * 24L * 366L * 30L),
|
|
||||||
Locale.ENGLISH,
|
|
||||||
x500Name,
|
|
||||||
SubjectPublicKeyInfo.getInstance(pair.public.encoded)
|
|
||||||
)
|
|
||||||
val signer: ContentSigner = JcaContentSignerBuilder("SHA256withRSA").build(pair.private)
|
|
||||||
return JcaX509CertificateConverter().getCertificate(builder.build(signer)) to pair.private
|
|
||||||
}
|
|
||||||
|
|
||||||
fun signApk(input: File, output: File, ks: File) {
|
|
||||||
Security.addProvider(BouncyCastleProvider())
|
|
||||||
|
|
||||||
if (!ks.exists()) newKeystore(ks)
|
|
||||||
|
|
||||||
val keyStore = KeyStore.getInstance("BKS", "BC")
|
|
||||||
FileInputStream(ks).use { fis -> keyStore.load(fis, null) }
|
|
||||||
val alias = keyStore.aliases().nextElement()
|
|
||||||
|
|
||||||
val config = ApkSigner.SignerConfig.Builder(
|
|
||||||
cn,
|
|
||||||
keyStore.getKey(alias, passwordCharArray) as PrivateKey,
|
|
||||||
listOf(keyStore.getCertificate(alias) as X509Certificate)
|
|
||||||
).build()
|
|
||||||
|
|
||||||
val signer = ApkSigner.Builder(listOf(config))
|
|
||||||
signer.setCreatedBy(cn)
|
|
||||||
signer.setInputApk(input)
|
|
||||||
signer.setOutputApk(output)
|
|
||||||
|
|
||||||
signer.build().sign()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
@file:Suppress("unused")
|
|
||||||
|
|
||||||
package app.revanced.manager.flutter.utils.zip
|
|
||||||
|
|
||||||
import java.io.DataInput
|
|
||||||
import java.io.DataOutput
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
|
|
||||||
fun UInt.toLittleEndian() =
|
|
||||||
(((this.toInt() and 0xff000000.toInt()) shr 24) or ((this.toInt() and 0x00ff0000) shr 8) or ((this.toInt() and 0x0000ff00) shl 8) or (this.toInt() shl 24)).toUInt()
|
|
||||||
|
|
||||||
fun UShort.toLittleEndian() = (this.toUInt() shl 16).toLittleEndian().toUShort()
|
|
||||||
|
|
||||||
fun UInt.toBigEndian() = (((this.toInt() and 0xff) shl 24) or ((this.toInt() and 0xff00) shl 8)
|
|
||||||
or ((this.toInt() and 0x00ff0000) ushr 8) or (this.toInt() ushr 24)).toUInt()
|
|
||||||
|
|
||||||
fun UShort.toBigEndian() = (this.toUInt() shl 16).toBigEndian().toUShort()
|
|
||||||
|
|
||||||
fun ByteBuffer.getUShort() = this.short.toUShort()
|
|
||||||
fun ByteBuffer.getUInt() = this.int.toUInt()
|
|
||||||
|
|
||||||
fun ByteBuffer.putUShort(ushort: UShort): ByteBuffer = this.putShort(ushort.toShort())
|
|
||||||
fun ByteBuffer.putUInt(uint: UInt): ByteBuffer = this.putInt(uint.toInt())
|
|
||||||
|
|
||||||
fun DataInput.readUShort() = this.readShort().toUShort()
|
|
||||||
fun DataInput.readUInt() = this.readInt().toUInt()
|
|
||||||
|
|
||||||
fun DataOutput.writeUShort(ushort: UShort) = this.writeShort(ushort.toInt())
|
|
||||||
fun DataOutput.writeUInt(uint: UInt) = this.writeInt(uint.toInt())
|
|
||||||
|
|
||||||
fun DataInput.readUShortLE() = this.readUShort().toBigEndian()
|
|
||||||
fun DataInput.readUIntLE() = this.readUInt().toBigEndian()
|
|
||||||
|
|
||||||
fun DataOutput.writeUShortLE(ushort: UShort) = this.writeUShort(ushort.toLittleEndian())
|
|
||||||
fun DataOutput.writeUIntLE(uint: UInt) = this.writeUInt(uint.toLittleEndian())
|
|
@ -1,176 +0,0 @@
|
|||||||
package app.revanced.manager.flutter.utils.zip
|
|
||||||
|
|
||||||
import app.revanced.manager.flutter.utils.zip.structures.ZipEndRecord
|
|
||||||
import app.revanced.manager.flutter.utils.zip.structures.ZipEntry
|
|
||||||
import java.io.Closeable
|
|
||||||
import java.io.File
|
|
||||||
import java.io.RandomAccessFile
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
import java.nio.channels.FileChannel
|
|
||||||
import java.util.zip.CRC32
|
|
||||||
import java.util.zip.Deflater
|
|
||||||
|
|
||||||
class ZipFile(file: File) : Closeable {
|
|
||||||
var entries: MutableList<ZipEntry> = mutableListOf()
|
|
||||||
|
|
||||||
private val filePointer: RandomAccessFile = RandomAccessFile(file, "rw")
|
|
||||||
private var CDNeedsRewrite = false
|
|
||||||
|
|
||||||
private val compressionLevel = 5
|
|
||||||
|
|
||||||
init {
|
|
||||||
//if file isn't empty try to load entries
|
|
||||||
if (file.length() > 0) {
|
|
||||||
val endRecord = findEndRecord()
|
|
||||||
|
|
||||||
if (endRecord.diskNumber > 0u || endRecord.totalEntries != endRecord.diskEntries)
|
|
||||||
throw IllegalArgumentException("Multi-file archives are not supported")
|
|
||||||
|
|
||||||
entries = readEntries(endRecord).toMutableList()
|
|
||||||
}
|
|
||||||
|
|
||||||
//seek back to start for writing
|
|
||||||
filePointer.seek(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun findEndRecord(): ZipEndRecord {
|
|
||||||
//look from end to start since end record is at the end
|
|
||||||
for (i in filePointer.length() - 1 downTo 0) {
|
|
||||||
filePointer.seek(i)
|
|
||||||
//possible beginning of signature
|
|
||||||
if (filePointer.readByte() == 0x50.toByte()) {
|
|
||||||
//seek back to get the full int
|
|
||||||
filePointer.seek(i)
|
|
||||||
val possibleSignature = filePointer.readUIntLE()
|
|
||||||
if (possibleSignature == ZipEndRecord.ECD_SIGNATURE) {
|
|
||||||
filePointer.seek(i)
|
|
||||||
return ZipEndRecord.fromECD(filePointer)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw Exception("Couldn't find end record")
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readEntries(endRecord: ZipEndRecord): List<ZipEntry> {
|
|
||||||
filePointer.seek(endRecord.centralDirectoryStartOffset.toLong())
|
|
||||||
|
|
||||||
val numberOfEntries = endRecord.diskEntries.toInt()
|
|
||||||
|
|
||||||
return buildList(numberOfEntries) {
|
|
||||||
for (i in 1..numberOfEntries) {
|
|
||||||
add(
|
|
||||||
ZipEntry.fromCDE(filePointer).also
|
|
||||||
{
|
|
||||||
//for some reason the local extra field can be different from the central one
|
|
||||||
it.readLocalExtra(
|
|
||||||
filePointer.channel.map(
|
|
||||||
FileChannel.MapMode.READ_ONLY,
|
|
||||||
it.localHeaderOffset.toLong() + 28,
|
|
||||||
2
|
|
||||||
)
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun writeCD() {
|
|
||||||
val CDStart = filePointer.channel.position().toUInt()
|
|
||||||
|
|
||||||
entries.forEach {
|
|
||||||
filePointer.channel.write(it.toCDE())
|
|
||||||
}
|
|
||||||
|
|
||||||
val entriesCount = entries.size.toUShort()
|
|
||||||
|
|
||||||
val endRecord = ZipEndRecord(
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
entriesCount,
|
|
||||||
entriesCount,
|
|
||||||
filePointer.channel.position().toUInt() - CDStart,
|
|
||||||
CDStart,
|
|
||||||
""
|
|
||||||
)
|
|
||||||
|
|
||||||
filePointer.channel.write(endRecord.toECD())
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addEntry(entry: ZipEntry, data: ByteBuffer) {
|
|
||||||
CDNeedsRewrite = true
|
|
||||||
|
|
||||||
entry.localHeaderOffset = filePointer.channel.position().toUInt()
|
|
||||||
|
|
||||||
filePointer.channel.write(entry.toLFH())
|
|
||||||
filePointer.channel.write(data)
|
|
||||||
|
|
||||||
entries.add(entry)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addEntryCompressData(entry: ZipEntry, data: ByteArray) {
|
|
||||||
val compressor = Deflater(compressionLevel, true)
|
|
||||||
compressor.setInput(data)
|
|
||||||
compressor.finish()
|
|
||||||
|
|
||||||
val uncompressedSize = data.size
|
|
||||||
val compressedData =
|
|
||||||
ByteArray(uncompressedSize) //i'm guessing compression won't make the data bigger
|
|
||||||
|
|
||||||
val compressedDataLength = compressor.deflate(compressedData)
|
|
||||||
val compressedBuffer =
|
|
||||||
ByteBuffer.wrap(compressedData.take(compressedDataLength).toByteArray())
|
|
||||||
|
|
||||||
compressor.end()
|
|
||||||
|
|
||||||
val crc = CRC32()
|
|
||||||
crc.update(data)
|
|
||||||
|
|
||||||
entry.compression = 8u //deflate compression
|
|
||||||
entry.uncompressedSize = uncompressedSize.toUInt()
|
|
||||||
entry.compressedSize = compressedDataLength.toUInt()
|
|
||||||
entry.crc32 = crc.value.toUInt()
|
|
||||||
|
|
||||||
addEntry(entry, compressedBuffer)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addEntryCopyData(entry: ZipEntry, data: ByteBuffer, alignment: Int? = null) {
|
|
||||||
alignment?.let {
|
|
||||||
//calculate where data would end up
|
|
||||||
val dataOffset = filePointer.filePointer + entry.LFHSize
|
|
||||||
|
|
||||||
val mod = dataOffset % alignment
|
|
||||||
|
|
||||||
//wrong alignment
|
|
||||||
if (mod != 0L) {
|
|
||||||
//add padding at end of extra field
|
|
||||||
entry.localExtraField =
|
|
||||||
entry.localExtraField.copyOf((entry.localExtraField.size + (alignment - mod)).toInt())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addEntry(entry, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getDataForEntry(entry: ZipEntry): ByteBuffer {
|
|
||||||
return filePointer.channel.map(
|
|
||||||
FileChannel.MapMode.READ_ONLY,
|
|
||||||
entry.dataOffset.toLong(),
|
|
||||||
entry.compressedSize.toLong()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun copyEntriesFromFileAligned(file: ZipFile, entryAlignment: (entry: ZipEntry) -> Int?) {
|
|
||||||
for (entry in file.entries) {
|
|
||||||
if (entries.any { it.fileName == entry.fileName }) continue //don't add duplicates
|
|
||||||
|
|
||||||
val data = file.getDataForEntry(entry)
|
|
||||||
addEntryCopyData(entry, data, entryAlignment(entry))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
if (CDNeedsRewrite) writeCD()
|
|
||||||
filePointer.close()
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
package app.revanced.manager.flutter.utils.zip.structures
|
|
||||||
|
|
||||||
import app.revanced.manager.flutter.utils.zip.putUInt
|
|
||||||
import app.revanced.manager.flutter.utils.zip.putUShort
|
|
||||||
import app.revanced.manager.flutter.utils.zip.readUIntLE
|
|
||||||
import app.revanced.manager.flutter.utils.zip.readUShortLE
|
|
||||||
import java.io.DataInput
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
import java.nio.ByteOrder
|
|
||||||
|
|
||||||
data class ZipEndRecord(
|
|
||||||
val diskNumber: UShort,
|
|
||||||
val startingDiskNumber: UShort,
|
|
||||||
val diskEntries: UShort,
|
|
||||||
val totalEntries: UShort,
|
|
||||||
val centralDirectorySize: UInt,
|
|
||||||
val centralDirectoryStartOffset: UInt,
|
|
||||||
val fileComment: String,
|
|
||||||
) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val ECD_HEADER_SIZE = 22
|
|
||||||
const val ECD_SIGNATURE = 0x06054b50u
|
|
||||||
|
|
||||||
fun fromECD(input: DataInput): ZipEndRecord {
|
|
||||||
val signature = input.readUIntLE()
|
|
||||||
|
|
||||||
if (signature != ECD_SIGNATURE)
|
|
||||||
throw IllegalArgumentException("Input doesn't start with end record signature")
|
|
||||||
|
|
||||||
val diskNumber = input.readUShortLE()
|
|
||||||
val startingDiskNumber = input.readUShortLE()
|
|
||||||
val diskEntries = input.readUShortLE()
|
|
||||||
val totalEntries = input.readUShortLE()
|
|
||||||
val centralDirectorySize = input.readUIntLE()
|
|
||||||
val centralDirectoryStartOffset = input.readUIntLE()
|
|
||||||
val fileCommentLength = input.readUShortLE()
|
|
||||||
var fileComment = ""
|
|
||||||
|
|
||||||
if (fileCommentLength > 0u) {
|
|
||||||
val fileCommentBytes = ByteArray(fileCommentLength.toInt())
|
|
||||||
input.readFully(fileCommentBytes)
|
|
||||||
fileComment = fileCommentBytes.toString(Charsets.UTF_8)
|
|
||||||
}
|
|
||||||
|
|
||||||
return ZipEndRecord(
|
|
||||||
diskNumber,
|
|
||||||
startingDiskNumber,
|
|
||||||
diskEntries,
|
|
||||||
totalEntries,
|
|
||||||
centralDirectorySize,
|
|
||||||
centralDirectoryStartOffset,
|
|
||||||
fileComment
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toECD(): ByteBuffer {
|
|
||||||
val commentBytes = fileComment.toByteArray(Charsets.UTF_8)
|
|
||||||
|
|
||||||
val buffer = ByteBuffer.allocate(ECD_HEADER_SIZE + commentBytes.size)
|
|
||||||
.also { it.order(ByteOrder.LITTLE_ENDIAN) }
|
|
||||||
|
|
||||||
buffer.putUInt(ECD_SIGNATURE)
|
|
||||||
buffer.putUShort(diskNumber)
|
|
||||||
buffer.putUShort(startingDiskNumber)
|
|
||||||
buffer.putUShort(diskEntries)
|
|
||||||
buffer.putUShort(totalEntries)
|
|
||||||
buffer.putUInt(centralDirectorySize)
|
|
||||||
buffer.putUInt(centralDirectoryStartOffset)
|
|
||||||
buffer.putUShort(commentBytes.size.toUShort())
|
|
||||||
|
|
||||||
buffer.put(commentBytes)
|
|
||||||
|
|
||||||
buffer.flip()
|
|
||||||
return buffer
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,190 +0,0 @@
|
|||||||
package app.revanced.manager.flutter.utils.zip.structures
|
|
||||||
|
|
||||||
import app.revanced.manager.flutter.utils.zip.*
|
|
||||||
import java.io.DataInput
|
|
||||||
import java.nio.ByteBuffer
|
|
||||||
import java.nio.ByteOrder
|
|
||||||
|
|
||||||
data class ZipEntry(
|
|
||||||
val version: UShort,
|
|
||||||
val versionNeeded: UShort,
|
|
||||||
val flags: UShort,
|
|
||||||
var compression: UShort,
|
|
||||||
val modificationTime: UShort,
|
|
||||||
val modificationDate: UShort,
|
|
||||||
var crc32: UInt,
|
|
||||||
var compressedSize: UInt,
|
|
||||||
var uncompressedSize: UInt,
|
|
||||||
val diskNumber: UShort,
|
|
||||||
val internalAttributes: UShort,
|
|
||||||
val externalAttributes: UInt,
|
|
||||||
var localHeaderOffset: UInt,
|
|
||||||
val fileName: String,
|
|
||||||
val extraField: ByteArray,
|
|
||||||
val fileComment: String,
|
|
||||||
var localExtraField: ByteArray = ByteArray(0), //separate for alignment
|
|
||||||
) {
|
|
||||||
val LFHSize: Int
|
|
||||||
get() = LFH_HEADER_SIZE + fileName.toByteArray(Charsets.UTF_8).size + localExtraField.size
|
|
||||||
|
|
||||||
val dataOffset: UInt
|
|
||||||
get() = localHeaderOffset + LFHSize.toUInt()
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val CDE_HEADER_SIZE = 46
|
|
||||||
const val CDE_SIGNATURE = 0x02014b50u
|
|
||||||
|
|
||||||
const val LFH_HEADER_SIZE = 30
|
|
||||||
const val LFH_SIGNATURE = 0x04034b50u
|
|
||||||
|
|
||||||
fun createWithName(fileName: String): ZipEntry {
|
|
||||||
return ZipEntry(
|
|
||||||
0x1403u, //made by unix, version 20
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0x0821u, //seems to be static time google uses, no idea
|
|
||||||
0x0221u, //same as above
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
0u,
|
|
||||||
fileName,
|
|
||||||
ByteArray(0),
|
|
||||||
""
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun fromCDE(input: DataInput): ZipEntry {
|
|
||||||
val signature = input.readUIntLE()
|
|
||||||
|
|
||||||
if (signature != CDE_SIGNATURE)
|
|
||||||
throw IllegalArgumentException("Input doesn't start with central directory entry signature")
|
|
||||||
|
|
||||||
val version = input.readUShortLE()
|
|
||||||
val versionNeeded = input.readUShortLE()
|
|
||||||
var flags = input.readUShortLE()
|
|
||||||
val compression = input.readUShortLE()
|
|
||||||
val modificationTime = input.readUShortLE()
|
|
||||||
val modificationDate = input.readUShortLE()
|
|
||||||
val crc32 = input.readUIntLE()
|
|
||||||
val compressedSize = input.readUIntLE()
|
|
||||||
val uncompressedSize = input.readUIntLE()
|
|
||||||
val fileNameLength = input.readUShortLE()
|
|
||||||
var fileName = ""
|
|
||||||
val extraFieldLength = input.readUShortLE()
|
|
||||||
val extraField = ByteArray(extraFieldLength.toInt())
|
|
||||||
val fileCommentLength = input.readUShortLE()
|
|
||||||
var fileComment = ""
|
|
||||||
val diskNumber = input.readUShortLE()
|
|
||||||
val internalAttributes = input.readUShortLE()
|
|
||||||
val externalAttributes = input.readUIntLE()
|
|
||||||
val localHeaderOffset = input.readUIntLE()
|
|
||||||
|
|
||||||
val variableFieldsLength =
|
|
||||||
fileNameLength.toInt() + extraFieldLength.toInt() + fileCommentLength.toInt()
|
|
||||||
|
|
||||||
if (variableFieldsLength > 0) {
|
|
||||||
val fileNameBytes = ByteArray(fileNameLength.toInt())
|
|
||||||
input.readFully(fileNameBytes)
|
|
||||||
fileName = fileNameBytes.toString(Charsets.UTF_8)
|
|
||||||
|
|
||||||
input.readFully(extraField)
|
|
||||||
|
|
||||||
val fileCommentBytes = ByteArray(fileCommentLength.toInt())
|
|
||||||
input.readFully(fileCommentBytes)
|
|
||||||
fileComment = fileCommentBytes.toString(Charsets.UTF_8)
|
|
||||||
}
|
|
||||||
|
|
||||||
flags = (flags and 0b1000u.inv()
|
|
||||||
.toUShort()) //disable data descriptor flag as they are not used
|
|
||||||
|
|
||||||
return ZipEntry(
|
|
||||||
version,
|
|
||||||
versionNeeded,
|
|
||||||
flags,
|
|
||||||
compression,
|
|
||||||
modificationTime,
|
|
||||||
modificationDate,
|
|
||||||
crc32,
|
|
||||||
compressedSize,
|
|
||||||
uncompressedSize,
|
|
||||||
diskNumber,
|
|
||||||
internalAttributes,
|
|
||||||
externalAttributes,
|
|
||||||
localHeaderOffset,
|
|
||||||
fileName,
|
|
||||||
extraField,
|
|
||||||
fileComment,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun readLocalExtra(buffer: ByteBuffer) {
|
|
||||||
buffer.order(ByteOrder.LITTLE_ENDIAN)
|
|
||||||
localExtraField = ByteArray(buffer.getUShort().toInt())
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toLFH(): ByteBuffer {
|
|
||||||
val nameBytes = fileName.toByteArray(Charsets.UTF_8)
|
|
||||||
|
|
||||||
val buffer = ByteBuffer.allocate(LFH_HEADER_SIZE + nameBytes.size + localExtraField.size)
|
|
||||||
.also { it.order(ByteOrder.LITTLE_ENDIAN) }
|
|
||||||
|
|
||||||
buffer.putUInt(LFH_SIGNATURE)
|
|
||||||
buffer.putUShort(versionNeeded)
|
|
||||||
buffer.putUShort(flags)
|
|
||||||
buffer.putUShort(compression)
|
|
||||||
buffer.putUShort(modificationTime)
|
|
||||||
buffer.putUShort(modificationDate)
|
|
||||||
buffer.putUInt(crc32)
|
|
||||||
buffer.putUInt(compressedSize)
|
|
||||||
buffer.putUInt(uncompressedSize)
|
|
||||||
buffer.putUShort(nameBytes.size.toUShort())
|
|
||||||
buffer.putUShort(localExtraField.size.toUShort())
|
|
||||||
|
|
||||||
buffer.put(nameBytes)
|
|
||||||
buffer.put(localExtraField)
|
|
||||||
|
|
||||||
buffer.flip()
|
|
||||||
return buffer
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toCDE(): ByteBuffer {
|
|
||||||
val nameBytes = fileName.toByteArray(Charsets.UTF_8)
|
|
||||||
val commentBytes = fileComment.toByteArray(Charsets.UTF_8)
|
|
||||||
|
|
||||||
val buffer =
|
|
||||||
ByteBuffer.allocate(CDE_HEADER_SIZE + nameBytes.size + extraField.size + commentBytes.size)
|
|
||||||
.also { it.order(ByteOrder.LITTLE_ENDIAN) }
|
|
||||||
|
|
||||||
buffer.putUInt(CDE_SIGNATURE)
|
|
||||||
buffer.putUShort(version)
|
|
||||||
buffer.putUShort(versionNeeded)
|
|
||||||
buffer.putUShort(flags)
|
|
||||||
buffer.putUShort(compression)
|
|
||||||
buffer.putUShort(modificationTime)
|
|
||||||
buffer.putUShort(modificationDate)
|
|
||||||
buffer.putUInt(crc32)
|
|
||||||
buffer.putUInt(compressedSize)
|
|
||||||
buffer.putUInt(uncompressedSize)
|
|
||||||
buffer.putUShort(nameBytes.size.toUShort())
|
|
||||||
buffer.putUShort(extraField.size.toUShort())
|
|
||||||
buffer.putUShort(commentBytes.size.toUShort())
|
|
||||||
buffer.putUShort(diskNumber)
|
|
||||||
buffer.putUShort(internalAttributes)
|
|
||||||
buffer.putUInt(externalAttributes)
|
|
||||||
buffer.putUInt(localHeaderOffset)
|
|
||||||
|
|
||||||
buffer.put(nameBytes)
|
|
||||||
buffer.put(extraField)
|
|
||||||
buffer.put(commentBytes)
|
|
||||||
|
|
||||||
buffer.flip()
|
|
||||||
return buffer
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -3,7 +3,12 @@ allprojects {
|
|||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
maven {
|
maven {
|
||||||
url 'https://jitpack.io'
|
// A repository must be speficied for some reason. "registry" is a dummy.
|
||||||
|
url = uri("https://maven.pkg.github.com/revanced/registry")
|
||||||
|
credentials {
|
||||||
|
username = project.findProperty("gpr.user") as String ?: System.getenv("GITHUB_ACTOR")
|
||||||
|
password = project.findProperty("gpr.key") as String ?: System.getenv("GITHUB_TOKEN")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
}
|
}
|
||||||
|
@ -28,3 +28,14 @@ Learn how to build ReVanced Manager from source.
|
|||||||
```sh
|
```sh
|
||||||
flutter build apk
|
flutter build apk
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> If the build fails due to authentication, you may need to authenticate to GitHub Packages.
|
||||||
|
> Create a PAT with the scope `read:packages` [here](https://github.com/settings/tokens/new?scopes=read:packages&description=ReVanced) and add your token to ~/.gradle/gradle.properties.
|
||||||
|
>
|
||||||
|
> Example `gradle.properties` file:
|
||||||
|
>
|
||||||
|
> ```properties
|
||||||
|
> gpr.user = user
|
||||||
|
> gpr.key = key
|
||||||
|
> ```
|
||||||
|
@ -172,25 +172,22 @@ class PatcherAPI {
|
|||||||
_dataDir.createSync();
|
_dataDir.createSync();
|
||||||
_tmpDir.createSync();
|
_tmpDir.createSync();
|
||||||
final Directory workDir = _tmpDir.createTempSync('tmp-');
|
final Directory workDir = _tmpDir.createTempSync('tmp-');
|
||||||
final File inputFile = File('${workDir.path}/base.apk');
|
|
||||||
final File patchedFile = File('${workDir.path}/patched.apk');
|
|
||||||
outFile = File('${workDir.path}/out.apk');
|
outFile = File('${workDir.path}/out.apk');
|
||||||
final Directory cacheDir = Directory('${workDir.path}/cache');
|
|
||||||
cacheDir.createSync();
|
final Directory tmpDir =
|
||||||
final String originalFilePath = apkFilePath;
|
Directory('${workDir.path}/revanced-temporary-files');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await patcherChannel.invokeMethod(
|
await patcherChannel.invokeMethod(
|
||||||
'runPatcher',
|
'runPatcher',
|
||||||
{
|
{
|
||||||
'originalFilePath': originalFilePath,
|
'inFilePath': apkFilePath,
|
||||||
'inputFilePath': inputFile.path,
|
|
||||||
'patchedFilePath': patchedFile.path,
|
|
||||||
'outFilePath': outFile!.path,
|
'outFilePath': outFile!.path,
|
||||||
'integrationsPath': integrationsFile.path,
|
'integrationsPath': integrationsFile.path,
|
||||||
'selectedPatches': selectedPatches.map((p) => p.name).toList(),
|
'selectedPatches': selectedPatches.map((p) => p.name).toList(),
|
||||||
'options': options,
|
'options': options,
|
||||||
'cacheDirPath': cacheDir.path,
|
'tmpDirPath': tmpDir.path,
|
||||||
'keyStoreFilePath': _keyStoreFile.path,
|
'keyStoreFilePath': _keyStoreFile.path,
|
||||||
'keystorePassword': _managerAPI.getKeystorePassword(),
|
'keystorePassword': _managerAPI.getKeystorePassword(),
|
||||||
},
|
},
|
||||||
@ -462,7 +459,6 @@ enum InstallStatus {
|
|||||||
mountNoRoot(1),
|
mountNoRoot(1),
|
||||||
mountVersionMismatch(1.1),
|
mountVersionMismatch(1.1),
|
||||||
mountMissingInstallation(1.2),
|
mountMissingInstallation(1.2),
|
||||||
|
|
||||||
statusFailureBlocked(2),
|
statusFailureBlocked(2),
|
||||||
installFailedVerificationFailure(3.1),
|
installFailedVerificationFailure(3.1),
|
||||||
statusFailureInvalid(4),
|
statusFailureInvalid(4),
|
||||||
@ -473,6 +469,7 @@ enum InstallStatus {
|
|||||||
statusFailureTimeout(8);
|
statusFailureTimeout(8);
|
||||||
|
|
||||||
const InstallStatus(this.statusCode);
|
const InstallStatus(this.statusCode);
|
||||||
|
|
||||||
final double statusCode;
|
final double statusCode;
|
||||||
|
|
||||||
static String byCode(num code) {
|
static String byCode(num code) {
|
||||||
|
@ -2,13 +2,10 @@ import 'dart:ui';
|
|||||||
import 'package:dynamic_color/dynamic_color.dart';
|
import 'package:dynamic_color/dynamic_color.dart';
|
||||||
import 'package:dynamic_themes/dynamic_themes.dart';
|
import 'package:dynamic_themes/dynamic_themes.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:revanced_manager/app/app.locator.dart';
|
|
||||||
import 'package:revanced_manager/app/app.router.dart';
|
import 'package:revanced_manager/app/app.router.dart';
|
||||||
import 'package:revanced_manager/gen/strings.g.dart';
|
import 'package:revanced_manager/gen/strings.g.dart';
|
||||||
import 'package:revanced_manager/services/manager_api.dart';
|
|
||||||
import 'package:revanced_manager/theme.dart';
|
import 'package:revanced_manager/theme.dart';
|
||||||
import 'package:stacked_services/stacked_services.dart';
|
import 'package:stacked_services/stacked_services.dart';
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user