diff --git a/apps/android/app/build.gradle b/apps/android/app/build.gradle index 45b0cd177c..3eb85d5924 100644 --- a/apps/android/app/build.gradle +++ b/apps/android/app/build.gradle @@ -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" } + } +} diff --git a/apps/android/app/src/main/AndroidManifest.xml b/apps/android/app/src/main/AndroidManifest.xml index 127641dc54..3f04b64fcb 100644 --- a/apps/android/app/src/main/AndroidManifest.xml +++ b/apps/android/app/src/main/AndroidManifest.xml @@ -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"> diff --git a/apps/android/build.gradle b/apps/android/build.gradle index 552ed8c8f1..e7be963f32 100644 --- a/apps/android/build.gradle +++ b/apps/android/build.gradle @@ -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 } diff --git a/scripts/android/compress-and-sign-apk.sh b/scripts/android/compress-and-sign-apk.sh new file mode 100755 index 0000000000..c07a640fbf --- /dev/null +++ b/scripts/android/compress-and-sign-apk.sh @@ -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