Different level of APK compression (#947)

* Different level of APK compression
- can reduce from 200mb to 50mb with level 5 of compression. Supports Intellij IDEA and command line gradle invocation
- by default, this feature is disabled. To enable create a file local.properties in `apps/android` and paste this line: `compression_level=5`
- level can be from 0 (no compression) to 9 (slowest and the must effective)
- automatically enables `extractNativeLibs` AndroidManifest's flag since it's required in this case. Feel free to find an alternative that works with compression of .so libs and without enabling this flag
- Windows is not suppored, of course. Only Unix-like OSes

* script corrections

* Missing JAVA_HOME in some environments

* Rename release apk made by IDEA to simplex.apk

* Enhancements

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
Stanislav Dmitrenko
2022-08-19 19:16:52 +03:00
committed by GitHub
parent fee2b247e9
commit 8c716962fb
4 changed files with 118 additions and 0 deletions
+69
View File
@@ -28,6 +28,7 @@ android {
}
manifestPlaceholders.app_name = "@string/app_name"
manifestPlaceholders.provider_authorities = "chat.simplex.app.provider"
manifestPlaceholders.extract_native_libs = compression_level != "0"
}
buildTypes {
@@ -73,6 +74,7 @@ android {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
}
jniLibs.useLegacyPackaging = compression_level != "0"
}
}
@@ -122,3 +124,70 @@ dependencies {
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
}
def buildType = "unknown"
// Don't do anything if no compression is needed
if (compression_level != "0") {
tasks.whenTaskAdded { task ->
if (task.name == 'packageDebug') {
task.doLast {
buildType = "debug"
}
task.finalizedBy compressApk
} else if (task.name == 'packageRelease') {
task.doLast {
buildType = "release"
}
task.finalizedBy compressApk
}
}
}
tasks.register("compressApk") {
doLast {
def javaHome = System.properties['java.home'] ?: org.gradle.internal.jvm.Jvm.current().getJavaHome()
def sdkDir = android.getSdkDirectory().getAbsolutePath()
def keyAlias = ""
def keyPassword = ""
def storeFile = ""
def storePassword = ""
if (project.properties['android.injected.signing.key.alias'] != null) {
keyAlias = project.properties['android.injected.signing.key.alias']
keyPassword = project.properties['android.injected.signing.key.password']
storeFile = project.properties['android.injected.signing.store.file']
storePassword = project.properties['android.injected.signing.store.password']
} else if (android.signingConfigs.hasProperty(buildType)) {
def gradleConfig = android.signingConfigs[buildType]
keyAlias = gradleConfig.keyAlias
keyPassword = gradleConfig.keyPassword
storeFile = gradleConfig.storeFile
storePassword = gradleConfig.storePassword
} else {
// There is no signing config for current build type, can't sign the apk
println("No signing configs for this build type: $buildType")
return
}
def outputDir = tasks["package${buildType.capitalize()}"].outputs.files.last()
exec {
workingDir '../../../scripts/android'
setEnvironment(['JAVA_HOME': "$javaHome"])
commandLine './compress-and-sign-apk.sh', \
"$compression_level", \
"$outputDir", \
"$sdkDir", \
"$storeFile", \
"$storePassword", \
"$keyAlias", \
"$keyPassword"
}
if (project.properties['android.injected.signing.key.alias'] != null && buildType == 'release') {
new File(outputDir, "app-release.apk").renameTo(new File(outputDir, "simplex.apk"))
}
// View all gradle properties set
// project.properties.each { k, v -> println "$k -> $v" }
}
}
@@ -26,6 +26,7 @@
android:allowBackup="true"
android:icon="@mipmap/icon"
android:label="${app_name}"
android:extractNativeLibs="${extract_native_libs}"
android:supportsRtl="true"
android:theme="@style/Theme.SimpleX">
+3
View File
@@ -18,6 +18,9 @@ buildscript {
// to allow debug & release versions to coexist
application_id_suffix = localProperties['application_id_suffix'] ?: ''
// Compression level for debug AND release apk. 0 = disable compression. Max is 9
compression_level = localProperties['compression_level'] ?: '0'
// NOTE: If you need a different version of something, provide it in `local.properties`
// like so: compose_version=123, or gradle_plugin_version=1.2.3, etc
}
+45
View File
@@ -0,0 +1,45 @@
#!/bin/bash
# Fail fast in case any command fails
set -e
level=$1
apk_parent_dir=$2
sdk_dir=$3
store_file=$4
store_password=$5
key_alias=$6
key_password=$7
if [ -z ${7} ]; then echo "You didn't enter all required params:
compress-and-sign-apk.sh level apk_parent_dir sdk_dir store_file store_password key_alias key_password"
fi
cd $apk_parent_dir
ORIG_NAME=$(echo app*.apk)
unzip -o -q -d apk $ORIG_NAME
rm $ORIG_NAME
(cd apk && zip -r -q -$level ../$ORIG_NAME .)
# Shouldn't be compressed because of Android requirement
(cd apk && zip -r -q -0 ../$ORIG_NAME resources.arsc)
#(cd apk && 7z a -r -mx=$level -tzip -x!resources.arsc ../$ORIG_NAME .)
#(cd apk && 7z a -r -mx=0 -tzip ../$ORIG_NAME resources.arsc)
ALL_TOOLS=($sdk_dir/build-tools/*/)
BIN_DIR="${ALL_TOOLS[1]}"
$BIN_DIR/zipalign -p -f 4 $ORIG_NAME $ORIG_NAME-2
mv $ORIG_NAME{-2,}
$BIN_DIR/apksigner sign \
--ks "$store_file" --ks-key-alias "$key_alias" --ks-pass "pass:$store_password" \
--key-pass "pass:$key_password" $ORIG_NAME
# cleanup
rm -rf apk || true
rm ${ORIG_NAME}.idsig 2> /dev/null || true