Files
IKAR/android/app/build.gradle.kts
T
sevenhill 68f7957043
Tests / test (push) Canceled after 0s
Build Android (FCM) / build-android-fcm (push) Canceled after 0s
Build Android / build-android (push) Canceled after 0s
Build iOS / build-ios (push) Canceled after 0s
Build Linux / build-linux (push) Canceled after 0s
Build macOS / build-macos (push) Canceled after 0s
Build Windows / build-windows (push) Canceled after 0s
Native crypto / native-crypto (push) Canceled after 0s
Release (main) / android (oneme) (push) Canceled after 0s
Release (main) / android (qlyra) (push) Canceled after 0s
Release (main) / windows (push) Canceled after 0s
Release (main) / linux (push) Canceled after 0s
Release (main) / macos (push) Canceled after 0s
Release (main) / ios (push) Canceled after 0s
Release (main) / release (push) Canceled after 0s
Fix phonebook refresh flicker and duplicate Android recent tasks
2026-10-08 06:22:47 +03:00

192 lines
7.3 KiB
Kotlin

import java.util.Properties
import java.io.FileInputStream
import java.security.MessageDigest
import org.gradle.api.file.DirectoryProperty
import org.gradle.api.file.FileSystemOperations
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputDirectory
import org.gradle.api.tasks.TaskAction
import javax.inject.Inject
plugins {
id("com.android.application")
id("kotlin-android")
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id("dev.flutter.flutter-gradle-plugin")
id("com.google.gms.google-services") apply false
}
if (file("google-services.json").exists()) { apply(plugin = "com.google.gms.google-services") }
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file(
providers.gradleProperty("releaseSigningProperties").getOrElse("key.properties")
)
val hasReleaseSigning = keystorePropertiesFile.exists()
if (hasReleaseSigning) {
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}
gradle.taskGraph.whenReady {
if (allTasks.any { it.project == project && it.name.contains("Release", ignoreCase = true) }) {
check(hasReleaseSigning) { "Release signing requires android/key.properties" }
for (key in listOf("keyAlias", "keyPassword", "storeFile", "storePassword")) {
check(!keystoreProperties.getProperty(key).isNullOrBlank()) { "Missing release signing property: $key" }
}
check(file(keystoreProperties.getProperty("storeFile")).isFile) { "Release keystore does not exist" }
}
}
android {
namespace = "ru.ikar.app"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
defaultConfig {
applicationId = "ru.ikar.app"
minSdk = maxOf(flutter.minSdkVersion, 23)
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
flavorDimensions += "distribution"
productFlavors {
create("ikar") {
dimension = "distribution"
isDefault = true
applicationId = "ru.ikar.app"
}
}
signingConfigs {
if (hasReleaseSigning) {
create("release") {
keyAlias = keystoreProperties["keyAlias"] as String
keyPassword = keystoreProperties["keyPassword"] as String
storeFile = file(keystoreProperties["storeFile"] as String)
storePassword = keystoreProperties["storePassword"] as String
}
}
}
buildTypes {
release {
signingConfig = if (hasReleaseSigning) {
signingConfigs.getByName("release")
} else null
}
}
packaging {
jniLibs {
useLegacyPackaging = true
}
}
}
flutter {
source = "../.."
}
abstract class StageFlutterJni : DefaultTask() {
@get:InputDirectory
abstract val inputDirectory: DirectoryProperty
@get:OutputDirectory
abstract val outputDirectory: DirectoryProperty
@get:Inject
abstract val fileSystem: FileSystemOperations
@TaskAction
fun stage() {
fileSystem.sync {
from(inputDirectory)
into(outputDirectory)
}
}
}
androidComponents {
onVariants(selector().withBuildType("release")) { variant ->
val variantName = variant.name
val capitalized = variantName.replaceFirstChar { it.uppercase() }
val stage = tasks.register<StageFlutterJni>("stage${capitalized}FlutterJni") {
dependsOn("copyJniLibsflutterBuild$capitalized")
inputDirectory.set(layout.buildDirectory.dir("intermediates/flutter/$variantName/jniLibs"))
outputDirectory.set(layout.buildDirectory.dir("intermediates/qlyra_flutter_jni/$variantName"))
}
variant.sources.jniLibs?.addGeneratedSourceDirectory(stage) { it.outputDirectory }
}
}
android.applicationVariants.all {
if (buildType.name == "release") {
val variantName = name
val capitalized = variantName.replaceFirstChar { it.uppercase() }
val generated = layout.buildDirectory.dir("intermediates/flutter/$variantName/jniLibs")
val merged = layout.buildDirectory.dir(
"intermediates/merged_native_libs/$variantName/merge${capitalized}NativeLibs/out/lib"
)
val verifyJni = tasks.register("verify${capitalized}FlutterJni") {
dependsOn("merge${capitalized}NativeLibs", "copyJniLibsflutterBuild$capitalized")
doLast {
val root = generated.get().asFile
val libraries = root.walkTopDown().filter { it.isFile && it.extension == "so" }.toList()
check(libraries.any { it.name == "libapp.so" }) { "Compiled Flutter library is missing" }
for (appLibrary in libraries.filter { it.name == "libapp.so" }) {
val abi = appLibrary.parentFile.name
val runtime = merged.get().asFile.resolve("$abi/libfjs.so")
check(runtime.isFile && runtime.length() > 0) {
"Plugin runtime is missing for $abi; inspect the fjs/cargokit build log"
}
val crypto = merged.get().asFile.resolve("$abi/libkomet_crypto.so")
check(crypto.isFile && crypto.length() > 0) {
"E2EE crypto library is missing for $abi"
}
val renderer = merged.get().asFile.resolve("$abi/libkomet_tlottie.so")
check(renderer.isFile && renderer.length() > 0) {
"Tlottie renderer is missing for $abi; inspect the cargokit build log"
}
}
for (library in libraries) {
val relative = library.relativeTo(root)
val packaged = merged.get().asFile.resolve(relative)
check(packaged.isFile) { "Flutter library was not merged: $relative" }
val expected = MessageDigest.getInstance("SHA-256").digest(library.readBytes())
val actual = MessageDigest.getInstance("SHA-256").digest(packaged.readBytes())
check(expected.contentEquals(actual)) { "Stale Flutter library in package: $relative" }
}
logger.lifecycle("Verified fresh Flutter JNI for $variantName (${libraries.size} libraries)")
}
}
tasks.matching { it.name == "strip${capitalized}DebugSymbols" }.configureEach {
dependsOn(verifyJni)
}
}
}
dependencies {
testImplementation("junit:junit:4.13.2")
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
implementation("androidx.media3:media3-transformer:1.9.3")
implementation("androidx.media3:media3-effect:1.9.3")
implementation("androidx.media3:media3-common:1.9.3")
implementation(platform("com.google.firebase:firebase-bom:33.7.0"))
implementation("com.google.firebase:firebase-messaging")
implementation("androidx.core:core-ktx:1.13.1")
implementation("androidx.appcompat:appcompat:1.7.1")
}