mirror of
https://github.com/topjohnwu/Magisk.git
synced 2026-04-28 12:03:09 -07:00
Move stub resources into its own module
Stop relying on internal AGP intermediate paths in the build directory. Use standard AGP classes to achieve the same result
This commit is contained in:
@@ -38,7 +38,7 @@ For Magisk app crashes, record and upload the logcat when the crash occurs.
|
||||
Default string resources for the Magisk app and its stub APK are located here:
|
||||
|
||||
- `app/core/src/main/res/values/strings.xml`
|
||||
- `app/stub/src/main/res/values/strings.xml`
|
||||
- `app/stub-res/src/main/res/values/strings.xml`
|
||||
|
||||
Translate each and place them in the respective locations (`[module]/src/main/res/values-[lang]/strings.xml`).
|
||||
|
||||
|
||||
@@ -170,7 +170,7 @@ fun Project.setupCoreLib() {
|
||||
it.addGeneratedSourceDirectory(syncResources, SyncWithDir::outputFolder)
|
||||
}
|
||||
|
||||
val stubTask = tasks.getByPath(":stub:comment$variantCapped")
|
||||
val stubTask = tasks.getByPath(":stub:transform${variantCapped}Apk")
|
||||
val syncAssets = tasks.register("sync${variantCapped}Assets", SyncWithDir::class) {
|
||||
outputFolder.set(layout.buildDirectory.dir("$variantName/assets"))
|
||||
into(outputFolder)
|
||||
@@ -261,20 +261,23 @@ fun Project.setupAppCommon() {
|
||||
androidAppComponents {
|
||||
onVariants { variant ->
|
||||
val commentTask = tasks.register(
|
||||
"comment${variant.name.replaceFirstChar { it.uppercase() }}",
|
||||
AddCommentTask::class.java
|
||||
"transform${variant.name.replaceFirstChar { it.uppercase() }}Apk",
|
||||
TransformApkTask::class.java
|
||||
)
|
||||
val transformationRequest = variant.artifacts.use(commentTask)
|
||||
.wiredWithDirectories(AddCommentTask::apkFolder, AddCommentTask::outFolder)
|
||||
.wiredWithDirectories(TransformApkTask::apkFolder, TransformApkTask::outFolder)
|
||||
.toTransformMany(SingleArtifact.APK)
|
||||
val signingConfig = androidApp.buildTypes.getByName(variant.buildType!!).signingConfig
|
||||
commentTask.configure {
|
||||
this.transformationRequest = transformationRequest
|
||||
this.signingConfig = signingConfig
|
||||
this.comment = "version=${Config.version}\n" +
|
||||
"versionCode=${Config.versionCode}\n" +
|
||||
"stubVersion=${Config.stubVersion}\n"
|
||||
this.outFolder.set(layout.buildDirectory.dir("outputs/apk/${variant.name}"))
|
||||
// Always add a transformation to set comments on the APK
|
||||
this.transformations.add {
|
||||
it.eocdComment = ("version=${Config.version}\n" +
|
||||
"versionCode=${Config.versionCode}\n" +
|
||||
"stubVersion=${Config.stubVersion}\n").toByteArray()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.CacheableTask
|
||||
import org.gradle.api.tasks.Delete
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.OutputDirectory
|
||||
@@ -14,8 +13,8 @@ import org.gradle.api.tasks.PathSensitive
|
||||
import org.gradle.api.tasks.PathSensitivity
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.kotlin.dsl.assign
|
||||
import org.gradle.kotlin.dsl.named
|
||||
import org.gradle.kotlin.dsl.register
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
@@ -24,9 +23,7 @@ import java.security.SecureRandom
|
||||
import java.util.Random
|
||||
import java.util.zip.Deflater
|
||||
import java.util.zip.DeflaterOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipFile
|
||||
import java.util.zip.ZipOutputStream
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.CipherOutputStream
|
||||
import javax.crypto.spec.IvParameterSpec
|
||||
@@ -290,56 +287,37 @@ fun Project.setupStubApk() {
|
||||
ManifestUpdater::outputManifest)
|
||||
.toTransform(SingleArtifact.MERGED_MANIFEST)
|
||||
|
||||
val aapt = sdkComponents.aapt2.get().executable.get().asFile
|
||||
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
|
||||
"${variantLowered}/process${variantCapped}Resources/" +
|
||||
"linked-resources-binary-format-${variantLowered}.ap_").get().asFile
|
||||
|
||||
val resTask = tasks.getByPath(":stub-res:package$variantCapped")
|
||||
val genResourcesTask = tasks.register("generate${variantCapped}BundledResources", TaskWithDir::class) {
|
||||
dependsOn("process${variantCapped}Resources")
|
||||
dependsOn(resTask)
|
||||
outputFolder.set(layout.buildDirectory.dir("generated/${variantLowered}/resources"))
|
||||
|
||||
doLast {
|
||||
val apkTmp = File("${apk}.tmp")
|
||||
providers.exec {
|
||||
commandLine(aapt, "optimize", "-o", apkTmp, "--collapse-resource-names", apk)
|
||||
}.result.get()
|
||||
val apk = resTask.outputs.files.asFileTree
|
||||
.filter { it.name.endsWith(".apk") }.files.first()
|
||||
|
||||
val bos = ByteArrayOutputStream()
|
||||
ZipFile(apkTmp).use { src ->
|
||||
ZipOutputStream(apk.outputStream()).use {
|
||||
it.setLevel(Deflater.BEST_COMPRESSION)
|
||||
it.putNextEntry(ZipEntry("AndroidManifest.xml"))
|
||||
src.getInputStream(src.getEntry("AndroidManifest.xml")).transferTo(it)
|
||||
it.closeEntry()
|
||||
}
|
||||
ZipFile(apk).use { src ->
|
||||
DeflaterOutputStream(bos, Deflater(Deflater.BEST_COMPRESSION)).use {
|
||||
src.getInputStream(src.getEntry("resources.arsc")).transferTo(it)
|
||||
}
|
||||
}
|
||||
apkTmp.delete()
|
||||
genEncryptedResources(bos.toByteArray(), outputFolder.get().asFile)
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(TransformApkTask::class) {
|
||||
transformations.add {
|
||||
// Always delete resources.arsc from the APK
|
||||
// to ensure that external resources can be loaded
|
||||
it.get("resources.arsc")?.delete()
|
||||
}
|
||||
}
|
||||
|
||||
variant.sources.java?.let {
|
||||
it.addStaticSourceDirectory(componentJavaOutDir.path)
|
||||
it.addGeneratedSourceDirectory(genResourcesTask, TaskWithDir::outputFolder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Override optimizeReleaseResources task
|
||||
val apk = layout.buildDirectory.file("intermediates/linked_resources_binary_format/" +
|
||||
"release/processReleaseResources/linked-resources-binary-format-release.ap_").get().asFile
|
||||
val optRes = layout.buildDirectory.file("intermediates/optimized_processed_res/" +
|
||||
"release/optimizeReleaseResources/resources-release-optimize.ap_").get().asFile
|
||||
afterEvaluate {
|
||||
tasks.named("optimizeReleaseResources") {
|
||||
doLast { apk.copyTo(optRes, true) }
|
||||
}
|
||||
}
|
||||
tasks.named<Delete>("clean") {
|
||||
delete.addAll(listOf("src/debug/AndroidManifest.xml", "src/release/AndroidManifest.xml"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,11 @@ import com.android.ide.common.signing.KeystoreHelper
|
||||
import com.android.tools.build.apkzlib.sign.SigningExtension
|
||||
import com.android.tools.build.apkzlib.sign.SigningOptions
|
||||
import com.android.tools.build.apkzlib.zfile.ZFiles
|
||||
import com.android.tools.build.apkzlib.zip.ZFile
|
||||
import com.android.tools.build.apkzlib.zip.ZFileOptions
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.provider.ListProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFiles
|
||||
@@ -17,10 +19,7 @@ import org.gradle.api.tasks.TaskAction
|
||||
import java.io.File
|
||||
import java.util.jar.JarFile
|
||||
|
||||
abstract class AddCommentTask: DefaultTask() {
|
||||
@get:Input
|
||||
abstract val comment: Property<String>
|
||||
|
||||
abstract class TransformApkTask : DefaultTask() {
|
||||
@get:Input
|
||||
abstract val signingConfig: Property<ApkSigningConfig>
|
||||
|
||||
@@ -31,7 +30,10 @@ abstract class AddCommentTask: DefaultTask() {
|
||||
abstract val outFolder: DirectoryProperty
|
||||
|
||||
@get:Internal
|
||||
abstract val transformationRequest: Property<ArtifactTransformationRequest<AddCommentTask>>
|
||||
abstract val transformations: ListProperty<(ZFile) -> Unit>
|
||||
|
||||
@get:Internal
|
||||
abstract val transformationRequest: Property<ArtifactTransformationRequest<TransformApkTask>>
|
||||
|
||||
@TaskAction
|
||||
fun taskAction() = transformationRequest.get().submit(this) { artifact ->
|
||||
@@ -63,10 +65,10 @@ abstract class AddCommentTask: DefaultTask() {
|
||||
inFile.copyTo(outFile, overwrite = true)
|
||||
ZFiles.apk(outFile, options).use {
|
||||
SigningExtension(signingOptions).register(it)
|
||||
it.eocdComment = comment.get().toByteArray()
|
||||
it.get(IncrementalPackager.APP_METADATA_ENTRY_PATH)?.delete()
|
||||
it.get(IncrementalPackager.VERSION_CONTROL_INFO_ENTRY_PATH)?.delete()
|
||||
it.get(JarFile.MANIFEST_NAME)?.delete()
|
||||
transformations.get().forEach { transform -> transform(it) }
|
||||
}
|
||||
|
||||
outFile
|
||||
@@ -17,4 +17,4 @@ pluginManagement {
|
||||
}
|
||||
|
||||
rootProject.name = "Magisk"
|
||||
include(":apk", ":apk-ng", ":core", ":shared", ":stub", ":test")
|
||||
include(":apk", ":apk-ng", ":core", ":shared", ":stub", ":stub-res", ":test")
|
||||
|
||||
16
app/stub-res/build.gradle.kts
Normal file
16
app/stub-res/build.gradle.kts
Normal file
@@ -0,0 +1,16 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
}
|
||||
|
||||
setupCommon()
|
||||
|
||||
android {
|
||||
namespace = "com.topjohnwu.magisk"
|
||||
enableKotlin = false
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isShrinkResources = false
|
||||
}
|
||||
}
|
||||
}
|
||||
2
app/stub-res/src/main/AndroidManifest.xml
Normal file
2
app/stub-res/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest><application /></manifest>
|
||||
@@ -28,7 +28,6 @@ android {
|
||||
release {
|
||||
proguardFiles("proguard-rules.pro")
|
||||
isMinifyEnabled = true
|
||||
isShrinkResources = false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,6 @@ package com.topjohnwu.magisk;
|
||||
import static android.R.string.no;
|
||||
import static android.R.string.ok;
|
||||
import static android.R.string.yes;
|
||||
import static com.topjohnwu.magisk.R.string.dling;
|
||||
import static com.topjohnwu.magisk.R.string.no_internet_msg;
|
||||
import static com.topjohnwu.magisk.R.string.upgrade_msg;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
@@ -46,14 +43,18 @@ import javax.crypto.spec.SecretKeySpec;
|
||||
public class DownloadActivity extends Activity {
|
||||
|
||||
private static final String APP_NAME = "Magisk";
|
||||
private static final String RES_PKG_NAME = "com.topjohnwu.magisk";
|
||||
|
||||
private Context themed;
|
||||
private boolean dynLoad;
|
||||
|
||||
private int dling;
|
||||
private int no_internet_msg;
|
||||
private int upgrade_msg;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
themed = new ContextThemeWrapper(this, android.R.style.Theme_DeviceDefault);
|
||||
getTheme().applyStyle(android.R.style.Theme_DeviceDefault_Dialog_NoActionBar, true);
|
||||
|
||||
// Only download and dynamic load full APK if hidden
|
||||
dynLoad = !getPackageName().equals(BuildConfig.APPLICATION_ID);
|
||||
@@ -63,6 +64,7 @@ public class DownloadActivity extends Activity {
|
||||
loadResources();
|
||||
} catch (Exception e) {
|
||||
error(e);
|
||||
return;
|
||||
}
|
||||
|
||||
ProviderInstaller.install(this);
|
||||
@@ -70,7 +72,7 @@ public class DownloadActivity extends Activity {
|
||||
if (Networking.checkNetworkStatus(this)) {
|
||||
showDialog();
|
||||
} else {
|
||||
new AlertDialog.Builder(themed)
|
||||
new AlertDialog.Builder(this)
|
||||
.setCancelable(false)
|
||||
.setTitle(APP_NAME)
|
||||
.setMessage(getString(no_internet_msg))
|
||||
@@ -95,7 +97,7 @@ public class DownloadActivity extends Activity {
|
||||
}
|
||||
|
||||
private void showDialog() {
|
||||
new AlertDialog.Builder(themed)
|
||||
new AlertDialog.Builder(this)
|
||||
.setCancelable(false)
|
||||
.setTitle(APP_NAME)
|
||||
.setMessage(getString(upgrade_msg))
|
||||
@@ -105,7 +107,7 @@ public class DownloadActivity extends Activity {
|
||||
}
|
||||
|
||||
private void dlAPK() {
|
||||
ProgressDialog.show(themed, getString(dling), getString(dling) + " " + APP_NAME, true);
|
||||
ProgressDialog.show(this, getString(dling), getString(dling) + " " + APP_NAME, true);
|
||||
// Download and upgrade the app
|
||||
var request = request(BuildConfig.APK_URL).setExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
|
||||
if (dynLoad) {
|
||||
@@ -139,6 +141,7 @@ public class DownloadActivity extends Activity {
|
||||
}
|
||||
|
||||
private void loadResources() throws Exception {
|
||||
var res = getResources();
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
var fd = Os.memfd_create("res", 0);
|
||||
try {
|
||||
@@ -147,14 +150,14 @@ public class DownloadActivity extends Activity {
|
||||
var loader = new ResourcesLoader();
|
||||
try (var pfd = ParcelFileDescriptor.dup(fd)) {
|
||||
loader.addProvider(ResourcesProvider.loadFromTable(pfd, null));
|
||||
getResources().addLoaders(loader);
|
||||
res.addLoaders(loader);
|
||||
}
|
||||
} finally {
|
||||
Os.close(fd);
|
||||
}
|
||||
} else {
|
||||
File res = new File(getCodeCacheDir(), "res.apk");
|
||||
try (var out = new ZipOutputStream(new FileOutputStream(res))) {
|
||||
File apk = new File(getCodeCacheDir(), "res.apk");
|
||||
try (var out = new ZipOutputStream(new FileOutputStream(apk))) {
|
||||
// AndroidManifest.xml is required on Android 6-, and directory support is broken on Android 9-10
|
||||
out.putNextEntry(new ZipEntry("AndroidManifest.xml"));
|
||||
try (var stubApk = new ZipFile(getPackageCodePath())) {
|
||||
@@ -163,7 +166,10 @@ public class DownloadActivity extends Activity {
|
||||
out.putNextEntry(new ZipEntry("resources.arsc"));
|
||||
decryptResources(out);
|
||||
}
|
||||
StubApk.addAssetPath(getResources(), res.getPath());
|
||||
StubApk.addAssetPath(res, apk.getPath());
|
||||
}
|
||||
dling = res.getIdentifier("dling", "string", RES_PKG_NAME);
|
||||
no_internet_msg = res.getIdentifier("no_internet_msg", "string", RES_PKG_NAME);
|
||||
upgrade_msg = res.getIdentifier("upgrade_msg", "string", RES_PKG_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user