diff --git a/README.md b/README.md index e4e833b30..dd9e74127 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,11 @@ Disable activity launch on theme: ## Step 1: Package Naming The FIRST thing you need to change is the package identifier (the name the app identifies as) to something more meaningful to you. Open up [build.gradle.kts](app/build.gradle.kts) and look for this line ```kt -applicationId("substratum.theme.template") +applicationId = "substratum.theme.template" ``` Change this to anything you want, for instance: ```kt -applicationId("com.yourname.themename") +applicationId = "com.yourname.themename" ``` Change Package Name in the project structure (optional): @@ -130,26 +130,26 @@ If you take a look at the aforementioned theme_configurations.xml, you will see ## Step 6: Safeguard your theme! Don't let the pirates win! ### If you want to enable the Substratum theme for other Theme Managers (e.g. Slim) -In ThemerConstants.gradle, change the [SUPPORTS_THIRD_PARTY_SYSTEMS](app/ThemerConstants.gradle#L9) on line 9. +In ThemerConstants.kt, change the [SUPPORTS_THIRD_PARTY_SYSTEMS](buildSrc/src/main/kotlin/ThemerConstants.kt#L8) on line 8. ### If you don't want to activate AntiPiracy Then you can stop reading and get your theme published! Good luck! ### Getting started with AntiPiracy -If you are ready to get AntiPiracy set up, all you need to look at is [ThemerConstants.gradle](app/ThemerConstants.gradle)! +If you are ready to get AntiPiracy set up, all you need to look at is [ThemerConstants.kt](buildSrc/src/main/kotlin/ThemerConstants.kt)! -Compile your theme as a SIGNED release APK from Android Studio (Build -> Generate Signed APK). Then launch the signed apk on your device and your log will spit out an error log under the name "SubstratumThemeReport", and you want to copy and paste that into [APK_SIGNATURE_PRODUCTION](app/ThemerConstants.gradle#L13) on line 13. +Compile your theme as a SIGNED release APK from Android Studio (Build -> Generate Signed APK). Then launch the signed apk on your device and your log will spit out an error log under the name "SubstratumThemeReport", and you want to copy and paste that into [APK_SIGNATURE_PRODUCTION](buildSrc/src/main/kotlin/ThemerConstants.kt#L12) on line 12. -**NOTE**: If you are planning to make use of [Google Play App Signing](https://developer.android.com/studio/publish/app-signing.html#google-play-app-signing), **DO NOT** fill the `APK_SIGNATURE_PRODUCTION` field in [ThemerConstants.gradle](app/ThemerConstants.gradle). +**NOTE**: If you are planning to make use of [Google Play App Signing](https://developer.android.com/studio/publish/app-signing.html#google-play-app-signing), **DO NOT** fill the `APK_SIGNATURE_PRODUCTION` field in [ThemerConstants.kt](buildSrc/src/main/kotlin/ThemerConstants.kt). -Then you would need to go to Play Developer Console. Then access to your app -> Services and APIs, generate a new API key for your app and then paste it into [BASE_64_LICENSE_KEY](app/ThemerConstants.gradle#L12) on line 12. +Then you would need to go to Play Developer Console. Then access to your app -> Services and APIs, generate a new API key for your app and then paste it into [BASE_64_LICENSE_KEY](buildSrc/src/main/kotlin/ThemerConstants.kt#L11) on line 12. Third, if you would like to change where it checks for various things such as Amazon App Store Enforcement or Play Store Enforcement, you have options listed on line 16 and lines below it, simply change from `true` to `false` and vice versa to make your desired configuration. -Finally, if you would like to enable intensive mode anti-piracy (App package blacklist), add as many package names as you want under [BLACKLISTED_APPLICATIONS](app/src/main/kotlin/substratum/theme/template/AdvancedConstants.kt#L12) on line 12. Then make sure to enable [ENABLE_APP_BLACKLIST_CHECK](app/ThemerConstants.gradle#L16) on line 16. +Finally, if you would like to enable intensive mode anti-piracy (App package blacklist), add as many package names as you want under [BLACKLISTED_APPLICATIONS](app/src/main/kotlin/substratum/theme/template/AdvancedConstants.kt#L12) on line 12. Then make sure to enable [ENABLE_APP_BLACKLIST_CHECK](buildSrc/src/main/kotlin/ThemerConstants.kt#L15) on line 15. -**Under no circumstances should you share your ThemerConstants.gradle file, unless specifically asked by an [official substratum developer](https://github.com/substratum/documentation#team-info-and-responsibilities)**! +**Under no circumstances should you share your ThemerConstants.kt file, unless specifically asked by an [official substratum developer](https://github.com/substratum/documentation#team-info-and-responsibilities)**! ### Encrypted Assets As of template version 11.0.0, all theme assets are duplicated are encrypted within the APK by default, not your original assets! @@ -159,14 +159,14 @@ Always use a version control tool listed below to host your private themes! BitBucket: https://bitbucket.org/ GitLab: https://about.gitlab.com/ -If you want to keep your theme assets unencrypted, just change the value [here](app/ThemerConstants.gradle#L4) to false. +If you want to keep your theme assets unencrypted, just change the value [here](buildSrc/src/main/kotlin/ThemerConstants.kt#L3) to false. ### Enforcing security As of template version 11.0.0, themes have an additional check on the build of substratum your users should be running. What this means is that themes can ensure their themes ONLY function with our full release cycle with debug and Play Store releases. -If you would like to enable this feature (only allow your theme to be used with official substratum builds), all you have to do is to flip `true` to `false` [here](app/ThemerConstants.gradle#L20)! +If you would like to enable this feature (only allow your theme to be used with official substratum builds), all you have to do is to flip `true` to `false` [here](buildSrc/src/main/kotlin/ThemerConstants.kt#L19)! ### Now what? Nothing. Now you're set to publish your theme! diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e16980d2b..a635ba8f4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -6,37 +6,38 @@ import ThemerConstants.ENFORCE_GOOGLE_PLAY_INSTALL import ThemerConstants.SHOULD_ENCRYPT_ASSETS import ThemerConstants.SUPPORTS_THIRD_PARTY_SYSTEMS -import java.util.Random +import Util.assets +import Util.cleanEncryptedAssets +import Util.copyEncryptedTo +import Util.generateRandomByteArray +import Util.tempAssets +import Util.twistAsset + import java.io.FileInputStream import java.io.FileOutputStream - import javax.crypto.Cipher -import javax.crypto.spec.IvParameterSpec import javax.crypto.spec.SecretKeySpec +import javax.crypto.spec.IvParameterSpec plugins { id("com.android.application") - id("kotlin-android") + kotlin("android") } -val key = ByteArray(16).apply { - Random().nextBytes(this) -} - -val ivKey = ByteArray(16).apply { - Random().nextBytes(this) -} +// Themers: DO NOT MODIFY +val secretKey = generateRandomByteArray() +val ivKey = generateRandomByteArray() android { - compileSdkVersion(30) + compileSdk = 31 defaultConfig { // If you're planning to change up the package name, ensure you have read the readme // thoroughly! - applicationId("substratum.theme.template") + applicationId = "substratum.theme.template" // We are only supporting Nougat and above, all new changes will incorporate Nougat changes // to the substratum repo rather than anything lower. Keep targetSdkVersion the same. - minSdkVersion(24) + minSdk = 24 // Both versions must be changed to increment on Play Store/user's devices versionCode = 2 versionName = "2.0" @@ -45,8 +46,7 @@ android { buildConfigField("boolean", "SUPPORTS_THIRD_PARTY_SYSTEMS", "$SUPPORTS_THIRD_PARTY_SYSTEMS") buildConfigField("boolean", "ENABLE_APP_BLACKLIST_CHECK", "$ENABLE_APP_BLACKLIST_CHECK") buildConfigField("boolean", "ALLOW_THIRD_PARTY_SUBSTRATUM_BUILDS", "$ALLOW_THIRD_PARTY_SUBSTRATUM_BUILDS") - buildConfigField("String", "IV_KEY", "\"$ivKey\"") - buildConfigField("byte[]", "DECRYPTION_KEY", key.joinToString(prefix = "{", postfix = "}")) + buildConfigField("byte[]", "DECRYPTION_KEY", secretKey.joinToString(prefix = "{", postfix = "}")) buildConfigField("byte[]", "IV_KEY", ivKey.joinToString(prefix = "{", postfix = "}")) resValue("string", "encryption_status", if (shouldEncrypt()) "onCompileVerify" else "false") } @@ -78,10 +78,10 @@ android { } dependencies { - //implementation(fileTree(include = ["*.jar"], dir = "libs")) + implementation(kotlin("stdlib-jdk8", version = Constants.kotlinVersion)) + + implementation("androidx.appcompat:appcompat:1.4.1") implementation("com.github.javiersantos:PiracyChecker:1.2.5") - implementation(kotlin("stdlib-jdk8")) - implementation("androidx.appcompat:appcompat:1.2.0") } // Themers: DO NOT MODIFY ANYTHING BELOW @@ -91,53 +91,28 @@ tasks.register("encryptAssets") { return@register } - val tempAssets = File(projectDir, "/src/main/assets-temp") - if (!tempAssets.exists()) { + // Check if temp assets exist + if (!projectDir.tempAssets.exists()) { println("Encrypting duplicated assets, don't worry, your original assets are safe...") - val list = mutableListOf() - val dir = File(projectDir, "/src/main/assets") - dir.listFiles()?.filter { it.isFile }?.forEach { file -> - list.add(file) - - val fis = FileInputStream(file) - val fo = File(file.absolutePath.replace("assets", "assets-temp")) - fo.parentFile.mkdirs() - val fos = FileOutputStream(fo) - val buffer = ByteArray(4096) - var n: Int - while (fis.read(buffer).also { n = it } != -1) { - fos.write(buffer, 0, n) + + val secretKeySpec = SecretKeySpec(secretKey, "AES") + val ivParameterSpec = IvParameterSpec(ivKey) + val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") + .apply { + init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec) } - fis.close() - fos.close() - } - list.forEach { file -> + // Encrypt every single file in the assets dir recursively + projectDir.assets.walkTopDown().filter { it.isFile }.forEach { file -> + file.twistAsset("assets", "assets-temp") + + //Encrypt assets if (file.absolutePath.contains("overlays")) { - val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding") - val secret = SecretKeySpec(key, "AES") - val iv = IvParameterSpec(ivKey) - - cipher.init(Cipher.ENCRYPT_MODE, secret, iv) - val fis = FileInputStream(file) - val fos = FileOutputStream(file.absolutePath + ".enc") - - val input = ByteArray(64) - var bytesRead: Int - while (fis.read(input).also {bytesRead = it } != -1) { - val output = cipher.update(input, 0, bytesRead) - if (output != null) { - fos.write(output) + FileInputStream(file).use { fis -> + FileOutputStream("${file.absolutePath}.enc").use { fos -> + fis.copyEncryptedTo(fos, cipher, bufferSize = 64) } } - val output = cipher.doFinal() - if (output != null) { - fos.write(output) - } - fis.close() - fos.flush() - fos.close() - file.delete() } } @@ -147,36 +122,16 @@ tasks.register("encryptAssets") { } project.afterEvaluate { - tasks.named("preBuild"){ + tasks.named("preBuild") { dependsOn("encryptAssets") } } gradle.buildFinished { - val tempAssets = File(projectDir, "/src/main/assets-temp") - if (tempAssets.exists()) { - println("Cleaning duplicated encrypted assets, not your decrypted assets...") - val encryptedAssets = File(projectDir, "src/main/assets") - encryptedAssets.delete() - - tempAssets.listFiles()?.filter{ it.isFile }?.forEach { file -> - val fis = FileInputStream(file) - val fo = File(file.absolutePath.replace("assets-temp", "assets")) - fo.parentFile.mkdirs() - val fos = FileOutputStream(fo) - val buffer = ByteArray(4096) - var n: Int - while (fis.read(buffer).also { n = it } != -1) { - fos.write(buffer, 0, n) - } - fis.close() - fos.close() - } - tempAssets.delete() - } + projectDir.cleanEncryptedAssets() } fun shouldEncrypt(): Boolean { val tasks = project.gradle.startParameter.taskNames - return SHOULD_ENCRYPT_ASSETS && tasks.joinToString { it.toLowerCase() }.contains("release") + return SHOULD_ENCRYPT_ASSETS && tasks.joinToString().contains("release", ignoreCase = true) } diff --git a/build.gradle.kts b/build.gradle.kts index 00ee807f4..2b833bc31 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,56 +1,27 @@ -import java.io.FileInputStream -import java.io.FileOutputStream +import Util.cleanEncryptedAssets buildscript { - extra["kotlin_version"] = Constants.kotlinVersion repositories { google() mavenCentral() } dependencies { - classpath("com.android.tools.build:gradle:4.2.1") + classpath("com.android.tools.build:gradle:7.1.0") classpath(kotlin("gradle-plugin", version = Constants.kotlinVersion)) } } -tasks { - wrapper { - gradleVersion = "7.0.2" - distributionType = Wrapper.DistributionType.ALL - } -} - allprojects { repositories { google() - maven("https://jitpack.io") + maven(url = "https://jitpack.io") mavenCentral() } } tasks.register("clean") { delete(rootProject.buildDir) - val tempAssets = File(projectDir, "/src/main/assets-temp") - if (tempAssets.exists()) { - println("cleaning encrypted assets...") - val encryptedAssets = File(projectDir, "src/main/assets") - encryptedAssets.delete() - - tempAssets.listFiles()?.filter { it.isFile }?.forEach { file -> - val fis = FileInputStream(file) - val fo = File(file.absolutePath.replace("assets-temp", "assets")) - fo.parentFile.mkdirs() - val fos = FileOutputStream(fo) - val buffer = ByteArray(4096) - var n: Int - while (fis.read(buffer).also { n = it } != -1) { - fos.write(buffer, 0, n) - } - fis.close() - fos.close() - } - tempAssets.delete() - } + projectDir.cleanEncryptedAssets() } diff --git a/buildSrc/src/main/kotlin/Constants.kt b/buildSrc/src/main/kotlin/Constants.kt index 6ae4a3344..0c34d6dd6 100644 --- a/buildSrc/src/main/kotlin/Constants.kt +++ b/buildSrc/src/main/kotlin/Constants.kt @@ -1,5 +1,5 @@ object Constants { - const val kotlinVersion = "1.5.0" + const val kotlinVersion = "1.6.10" } \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/Util.kt b/buildSrc/src/main/kotlin/Util.kt new file mode 100644 index 000000000..eaaa4541d --- /dev/null +++ b/buildSrc/src/main/kotlin/Util.kt @@ -0,0 +1,79 @@ +import java.io.* +import java.util.* +import javax.crypto.Cipher + +object Util { + + /** + * @receiver Gradle project directory. + * @return Assets directory. + */ + val File.assets get() = File(this, "/src/main/assets") + + /** + * @receiver Gradle project directory. + * @return Assets directory. + */ + val File.tempAssets get() = File(this, "/src/main/assets-temp") + + /** + * Copies this [InputStream] to the given [OutputStream] and encrypts the content of it. + */ + fun InputStream.copyEncryptedTo( + out: OutputStream, + cipher: Cipher, + bufferSize: Int = DEFAULT_BUFFER_SIZE + ) { + val buffer = ByteArray(bufferSize) + var bytesRead = read(buffer) + while (bytesRead != -1) { + val output = cipher.update(buffer, 0, bytesRead) + if (output != null) { + out.write(output) + } + bytesRead = read(buffer) + } + val output = cipher.doFinal() + if (output != null) { + out.write(output) + } + } + + /** + * Cleans the encrypted assets in the project. + * @receiver Gradle project directory. + */ + fun File.cleanEncryptedAssets() { + val tempAssets = tempAssets + if (tempAssets.exists()) { + println("Cleaning duplicated encrypted assets, not your decrypted assets...") + assets.deleteRecursively() + + tempAssets.walkTopDown().filter { it.isFile }.forEach { file -> + file.twistAsset("assets-temp", "assets") + } + tempAssets.deleteRecursively() + } + } + + /** + * Copies the asset to a new destination by changing its [oldName] in the path with [newName]. + */ + fun File.twistAsset(oldName: String, newName: String) { + val fo = File(absolutePath.replace(oldName, newName)) + .apply { + parentFile.mkdirs() + } + + FileInputStream(this).use { fis -> + FileOutputStream(fo).use { fos -> + fis.copyTo(fos, bufferSize = 4096) + } + } + } + + fun generateRandomByteArray() = + ByteArray(16).apply { + Random().nextBytes(this) + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 7bb130fa6..b83ec5c1c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun May 16 11:34:40 GET 2021 +#Thu Jan 27 15:07:27 GET 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME