build: convert remaining .gradle files to Kotlin DSL
Converts the last four Groovy build scripts to Kotlin DSL so the entire build is consistent with the rest of the modules (commons, quartz, etc.). - settings.gradle -> settings.gradle.kts - build.gradle (root) -> build.gradle.kts - amethyst/build.gradle -> amethyst/build.gradle.kts - benchmark/build.gradle -> benchmark/build.gradle.kts Notes: - Switched the per-subproject spotless format from groovyGradle to kotlinGradle now that the only gradle scripts are .gradle.kts. - benchmark module: moved targetSdk from defaultConfig to testOptions — AGP 9 removed targetSdk from library defaultConfig. - Reimplemented getCurrentBranch() with ProcessBuilder (Groovy's String.execute() isn't available in Kotlin DSL).
This commit is contained in:
@@ -1,435 +0,0 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.androidApplication)
|
||||
alias(libs.plugins.googleServices)
|
||||
alias(libs.plugins.jetbrainsComposeCompiler)
|
||||
alias(libs.plugins.serialization)
|
||||
}
|
||||
|
||||
def getCurrentBranch() {
|
||||
try {
|
||||
def branch = 'git rev-parse --abbrev-ref HEAD'.execute().text.trim()
|
||||
return branch
|
||||
} catch (Exception e) {
|
||||
println "Could not determine git branch: ${e.message}"
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
def generateVersionName(String baseVersion) {
|
||||
def currentBranch = getCurrentBranch()
|
||||
|
||||
if (currentBranch == "main" || currentBranch == "master" || currentBranch == "unknown" || currentBranch == "HEAD") {
|
||||
return baseVersion
|
||||
} else {
|
||||
// Clean branch name for version (replace special characters)
|
||||
def cleanBranch = currentBranch.replaceAll(/[^a-zA-Z0-9\-_]/, "-")
|
||||
|
||||
// Limit branch name to maximum 20 characters
|
||||
if (cleanBranch.length() > 20) {
|
||||
cleanBranch = cleanBranch.substring(0, 20)
|
||||
}
|
||||
|
||||
return "${baseVersion}-${cleanBranch}"
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround: stability.analyzer plugin doesn't declare task dependencies properly for Gradle 9.x
|
||||
afterEvaluate {
|
||||
def stabilityNames = tasks.names.findAll { it.contains("StabilityCheck") }
|
||||
def compileNames = tasks.names.findAll { it.matches("compile.*UnitTestKotlin") }
|
||||
stabilityNames.each { scName ->
|
||||
compileNames.each { ctName ->
|
||||
tasks.named(scName).configure { mustRunAfter(tasks.named(ctName)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = 'com.vitorpamplona.amethyst'
|
||||
compileSdk = libs.versions.android.compileSdk.get().toInteger()
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.vitorpamplona.amethyst"
|
||||
minSdk = libs.versions.android.minSdk.get().toInteger()
|
||||
targetSdk = libs.versions.android.targetSdk.get().toInteger()
|
||||
versionCode = 446
|
||||
versionName = generateVersionName(libs.versions.app.get())
|
||||
buildConfigField "String", "RELEASE_NOTES_ID", "\"b1b91d7ee0c5da9d081d1a53470248ee4585b058b11aa34fe28c0e3e07ac1e0a\""
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
resourceConfigurations += [
|
||||
'ar',
|
||||
'ar-rSA',
|
||||
'bn-rBD',
|
||||
'cs',
|
||||
'cs-rCZ',
|
||||
'cy-rGB',
|
||||
'da-rDK',
|
||||
'de',
|
||||
'de-rDE',
|
||||
'el-rGR',
|
||||
'en-rGB',
|
||||
'eo',
|
||||
'eo-rUY',
|
||||
'es',
|
||||
'es-rES',
|
||||
'es-rMX',
|
||||
'es-rUS',
|
||||
'et-rEE',
|
||||
'fa',
|
||||
'fa-rIR',
|
||||
'fi-rFI',
|
||||
'fo-rFO',
|
||||
'fr',
|
||||
'fr-rCA',
|
||||
'fr-rFR',
|
||||
'gu-rIN',
|
||||
'hi-rIN',
|
||||
'hr-rHR',
|
||||
'hu',
|
||||
'hu-rHU',
|
||||
'in',
|
||||
'in-rID',
|
||||
'it-rIT',
|
||||
'iw-rIL',
|
||||
'ja',
|
||||
'ja-rJP',
|
||||
'kk-rKZ',
|
||||
'ko-rKR',
|
||||
'ks-rIN',
|
||||
'ku-rTR',
|
||||
'lt-rLT',
|
||||
'ne-rNP',
|
||||
'nl',
|
||||
'nl-rBE',
|
||||
'nl-rNL',
|
||||
'pcm-rNG',
|
||||
'pl-rPL',
|
||||
'pt-rBR',
|
||||
'pt-rPT',
|
||||
'ru',
|
||||
'ru-rRU',
|
||||
'ru-rUA',
|
||||
'sa-rIN',
|
||||
'sl-rSI',
|
||||
'so-rSO',
|
||||
'sr-rSP',
|
||||
'ss-rZA',
|
||||
'sv-rSE',
|
||||
'sw-rKE',
|
||||
'sw-rTZ',
|
||||
'ta',
|
||||
'ta-rIN',
|
||||
'th',
|
||||
'th-rTH',
|
||||
'tr',
|
||||
'tr-rTR',
|
||||
'uk',
|
||||
'uk-rUA',
|
||||
'ur-rIN',
|
||||
'uz-rUZ',
|
||||
'vi-rVN',
|
||||
'zh',
|
||||
'zh-rCN',
|
||||
'zh-rHK',
|
||||
'zh-rSG',
|
||||
'zh-rTW'
|
||||
]
|
||||
}
|
||||
|
||||
// Opt-in fast-build flags. Default behavior is unchanged.
|
||||
//
|
||||
// -PdisableAbiSplits=true skip per-ABI APK splits; produces a single
|
||||
// APK per (flavor, buildType) instead of 5.
|
||||
// Cuts ~600 MB of intermediates and several
|
||||
// minutes off CI.
|
||||
// -PdisableUniversalApk=true when ABI splits are enabled, skip the
|
||||
// extra universal APK output. (No effect
|
||||
// when disableAbiSplits is also set, since
|
||||
// there are no splits to add to.)
|
||||
// -Pamethyst.skipMapping=true disable R8 minification on release and
|
||||
// benchmark. APK is larger, but builds are
|
||||
// much faster and outputs/mapping/ (~260MB)
|
||||
// is not produced. Local-dev and PR-CI use
|
||||
// only — release pipelines must not set it.
|
||||
def disableAbiSplits = providers.gradleProperty("disableAbiSplits")
|
||||
.map { it.toBoolean() }.getOrElse(false)
|
||||
def disableUniversalApk = providers.gradleProperty("disableUniversalApk")
|
||||
.map { it.toBoolean() }.getOrElse(false)
|
||||
def skipMapping = providers.gradleProperty("amethyst.skipMapping")
|
||||
.map { it.toBoolean() }.getOrElse(false)
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), 'proguard-rules.pro'
|
||||
minifyEnabled = !skipMapping
|
||||
}
|
||||
debug {
|
||||
applicationIdSuffix '.debug'
|
||||
versionNameSuffix '-DEBUG'
|
||||
resValue "string", "app_name", "@string/app_name_debug"
|
||||
}
|
||||
create("benchmark") {
|
||||
initWith(getByName("release"))
|
||||
applicationIdSuffix '.benchmark'
|
||||
versionNameSuffix '-BENCHMARK'
|
||||
resValue "string", "app_name", "@string/app_name_benchmark"
|
||||
profileable = true
|
||||
signingConfig = signingConfigs.debug
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove this when lightcompressor uses one MP4 parser only
|
||||
packaging {
|
||||
resources {
|
||||
resources.pickFirsts.add('builddef.lst')
|
||||
resources.pickFirsts.add('META-INF/LICENSE.md')
|
||||
resources.pickFirsts.add('META-INF/LICENSE-notice.md')
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions = ["channel"]
|
||||
|
||||
productFlavors {
|
||||
play {
|
||||
getIsDefault().set(true)
|
||||
dimension "channel"
|
||||
buildConfigField "boolean", "IS_CASTING_AVAILABLE", "true"
|
||||
}
|
||||
|
||||
fdroid {
|
||||
dimension "channel"
|
||||
buildConfigField "boolean", "IS_CASTING_AVAILABLE", "false"
|
||||
}
|
||||
}
|
||||
|
||||
splits {
|
||||
abi {
|
||||
enable = !disableAbiSplits
|
||||
reset()
|
||||
include "x86", "x86_64", "arm64-v8a", "armeabi-v7a"
|
||||
universalApk = !disableUniversalApk
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_21
|
||||
targetCompatibility JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
compose = true
|
||||
buildConfig = true
|
||||
resValues = true
|
||||
}
|
||||
|
||||
packagingOptions {
|
||||
resources {
|
||||
excludes += ['/META-INF/{AL2.0,LGPL2.1}', '**/libscrypt.dylib']
|
||||
}
|
||||
}
|
||||
|
||||
lint {
|
||||
disable 'MissingTranslation'
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests.returnDefaultValues = true
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: until google merges and unifiedpush updates https://github.com/tink-crypto/tink-java-apps/pull/5
|
||||
configurations.all {
|
||||
def tink = "com.google.crypto.tink:tink-android:1.17.0"
|
||||
resolutionStrategy {
|
||||
force(tink)
|
||||
dependencySubstitution {
|
||||
substitute module('com.google.crypto.tink:tink') using module(tink)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
jvmTarget = JvmTarget.JVM_21
|
||||
}
|
||||
}
|
||||
|
||||
composeCompiler {
|
||||
reportsDestination = layout.buildDirectory.dir("compose_compiler")
|
||||
metricsDestination = layout.buildDirectory.dir("compose_compiler")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation platform(libs.androidx.compose.bom)
|
||||
|
||||
implementation project(path: ':quartz')
|
||||
implementation project(path: ':commons')
|
||||
implementation project(path: ':nestsClient')
|
||||
implementation libs.androidx.core.ktx
|
||||
implementation libs.androidx.activity.compose
|
||||
|
||||
implementation libs.androidx.ui
|
||||
implementation libs.androidx.ui.graphics
|
||||
implementation libs.androidx.ui.tooling.preview
|
||||
|
||||
// Needs this to open gallery / image upload
|
||||
implementation libs.androidx.fragment.ktx
|
||||
|
||||
// Navigation
|
||||
implementation libs.androidx.navigation.compose
|
||||
|
||||
// Material 3 Design
|
||||
implementation libs.androidx.material3
|
||||
|
||||
// Adaptive Layout / Two Pane
|
||||
implementation libs.androidx.material3.windowSize
|
||||
implementation libs.accompanist.adaptive
|
||||
|
||||
// Lifecycle
|
||||
implementation libs.androidx.lifecycle.runtime.ktx
|
||||
implementation libs.androidx.lifecycle.runtime.compose
|
||||
implementation libs.androidx.lifecycle.viewmodel.compose
|
||||
|
||||
// Zoomable images
|
||||
implementation libs.zoomable
|
||||
|
||||
// Biometrics
|
||||
implementation libs.androidx.biometric.ktx
|
||||
|
||||
// Background Work
|
||||
implementation libs.androidx.work.runtime.ktx
|
||||
|
||||
// Websockets API
|
||||
implementation libs.okhttp
|
||||
implementation libs.okhttpCoroutines
|
||||
|
||||
// Encrypted Key Storage
|
||||
implementation libs.androidx.security.crypto.ktx
|
||||
implementation libs.androidx.datastore.preferences
|
||||
|
||||
// view videos
|
||||
implementation libs.androidx.media3.exoplayer
|
||||
implementation libs.androidx.media3.exoplayer.hls
|
||||
implementation libs.androidx.media3.ui.compose.material3
|
||||
implementation libs.androidx.media3.session
|
||||
|
||||
// important for proxy / tor
|
||||
implementation libs.androidx.media3.datasource.okhttp
|
||||
|
||||
// Load images from the web.
|
||||
implementation libs.coil.compose
|
||||
// view gifs
|
||||
implementation libs.coil.gif
|
||||
// view svgs
|
||||
implementation libs.coil.svg
|
||||
// enables network for coil
|
||||
implementation libs.coil.okhttp
|
||||
// loads thumbnails for media3
|
||||
// TODO: Replace this to the FrameExtractor in media 3
|
||||
// when FrameExtractor accepts custom data sources.
|
||||
implementation(libs.coil.video)
|
||||
|
||||
// Permission to upload pictures:
|
||||
implementation libs.accompanist.permissions
|
||||
|
||||
// For QR generation
|
||||
implementation libs.zxing
|
||||
implementation libs.zxing.embedded
|
||||
|
||||
// Markdown
|
||||
//implementation "com.halilibo.compose-richtext:richtext-ui:0.16.0"
|
||||
//implementation "com.halilibo.compose-richtext:richtext-ui-material:0.16.0"
|
||||
//implementation "com.halilibo.compose-richtext:richtext-commonmark:0.16.0"
|
||||
|
||||
// Markdown (With fix for full-image bleeds)
|
||||
implementation libs.markdown.ui
|
||||
implementation libs.markdown.ui.material3
|
||||
implementation libs.markdown.commonmark
|
||||
|
||||
// Language picker and Theme chooser
|
||||
implementation libs.androidx.appcompat
|
||||
|
||||
// Dynamically adjust between phone and tablet UI
|
||||
implementation libs.androidx.window.core.android
|
||||
|
||||
// Local model for language identification
|
||||
playImplementation libs.google.mlkit.language.id
|
||||
|
||||
// Google services model the translate text
|
||||
playImplementation libs.google.mlkit.translate
|
||||
|
||||
// On-device AI writing assistance (Gemini Nano via AICore)
|
||||
playImplementation libs.google.mlkit.genai.proofreading
|
||||
playImplementation libs.google.mlkit.genai.prompt
|
||||
playImplementation libs.google.mlkit.genai.rewriting
|
||||
|
||||
// On-device alt-text suggestions: genai image description (preferred, descriptive sentences)
|
||||
// with image-labeling as a keyword-join fallback for devices without AICore.
|
||||
playImplementation libs.google.mlkit.genai.image.description
|
||||
|
||||
// PushNotifications
|
||||
playImplementation platform(libs.firebase.bom)
|
||||
playImplementation libs.firebase.messaging
|
||||
|
||||
//PushNotifications(FDroid)
|
||||
fdroidImplementation libs.unifiedpush
|
||||
|
||||
// Google Cast SDK — Chromecast support. Play flavor only because the
|
||||
// framework hard-depends on Google Play services, which is unavailable
|
||||
// on de-Googled / GrapheneOS devices that ship the F-Droid build.
|
||||
playImplementation libs.play.services.cast.framework
|
||||
|
||||
// Charts
|
||||
implementation libs.vico.charts.compose
|
||||
implementation libs.vico.charts.m3
|
||||
|
||||
// Waveform visualizer
|
||||
implementation libs.audiowaveform
|
||||
|
||||
// Video compression lib
|
||||
implementation libs.abedElazizShe.video.compressor.fork
|
||||
// Image compression lib
|
||||
implementation libs.zelory.image.compressor
|
||||
|
||||
// EXIF metadata stripping
|
||||
implementation libs.androidx.exifinterface
|
||||
|
||||
// Voice anonymization DSP
|
||||
implementation libs.tarsosdsp
|
||||
|
||||
// WebRTC for voice/video calls
|
||||
implementation libs.stream.webrtc.android
|
||||
|
||||
// Cbor for cashuB format
|
||||
implementation libs.kotlinx.serialization.cbor
|
||||
|
||||
// Kotlin serialization for the times where we need the Json tree and performance is not that important.
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
|
||||
testImplementation libs.junit
|
||||
testImplementation libs.mockk
|
||||
testImplementation libs.kotlinx.coroutines.test
|
||||
|
||||
androidTestImplementation platform(libs.androidx.compose.bom)
|
||||
androidTestImplementation libs.androidx.junit
|
||||
androidTestImplementation libs.androidx.junit.ktx
|
||||
androidTestImplementation libs.androidx.espresso.core
|
||||
androidTestImplementation libs.androidx.ui.test.junit4
|
||||
androidTestImplementation libs.mockk.android
|
||||
|
||||
debugImplementation platform(libs.androidx.compose.bom)
|
||||
debugImplementation libs.androidx.ui.tooling
|
||||
debugImplementation libs.androidx.ui.test.manifest
|
||||
|
||||
implementation libs.androidx.camera.core
|
||||
implementation libs.androidx.camera.camera2
|
||||
implementation libs.androidx.camera.lifecycle
|
||||
implementation libs.androidx.camera.view
|
||||
implementation libs.androidx.camera.extensions
|
||||
}
|
||||
@@ -0,0 +1,440 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.androidApplication)
|
||||
alias(libs.plugins.googleServices)
|
||||
alias(libs.plugins.jetbrainsComposeCompiler)
|
||||
alias(libs.plugins.serialization)
|
||||
}
|
||||
|
||||
fun getCurrentBranch(): String {
|
||||
return try {
|
||||
val process = ProcessBuilder("git", "rev-parse", "--abbrev-ref", "HEAD")
|
||||
.redirectErrorStream(true)
|
||||
.start()
|
||||
val branch = process.inputStream.bufferedReader().use { it.readText() }.trim()
|
||||
process.waitFor()
|
||||
branch
|
||||
} catch (e: Exception) {
|
||||
println("Could not determine git branch: ${e.message}")
|
||||
"unknown"
|
||||
}
|
||||
}
|
||||
|
||||
fun generateVersionName(baseVersion: String): String {
|
||||
val currentBranch = getCurrentBranch()
|
||||
|
||||
if (currentBranch == "main" || currentBranch == "master" || currentBranch == "unknown" || currentBranch == "HEAD") {
|
||||
return baseVersion
|
||||
}
|
||||
|
||||
// Clean branch name for version (replace special characters)
|
||||
var cleanBranch = currentBranch.replace(Regex("[^a-zA-Z0-9\\-_]"), "-")
|
||||
|
||||
// Limit branch name to maximum 20 characters
|
||||
if (cleanBranch.length > 20) {
|
||||
cleanBranch = cleanBranch.substring(0, 20)
|
||||
}
|
||||
|
||||
return "$baseVersion-$cleanBranch"
|
||||
}
|
||||
|
||||
// Workaround: stability.analyzer plugin doesn't declare task dependencies properly for Gradle 9.x
|
||||
afterEvaluate {
|
||||
val stabilityNames = tasks.names.filter { it.contains("StabilityCheck") }
|
||||
val compileNames = tasks.names.filter { it.matches(Regex("compile.*UnitTestKotlin")) }
|
||||
stabilityNames.forEach { scName ->
|
||||
compileNames.forEach { ctName ->
|
||||
tasks.named(scName).configure { mustRunAfter(tasks.named(ctName)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.vitorpamplona.amethyst"
|
||||
compileSdk = libs.versions.android.compileSdk.get().toInt()
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.vitorpamplona.amethyst"
|
||||
minSdk = libs.versions.android.minSdk.get().toInt()
|
||||
targetSdk = libs.versions.android.targetSdk.get().toInt()
|
||||
versionCode = 446
|
||||
versionName = generateVersionName(libs.versions.app.get())
|
||||
buildConfigField("String", "RELEASE_NOTES_ID", "\"b1b91d7ee0c5da9d081d1a53470248ee4585b058b11aa34fe28c0e3e07ac1e0a\"")
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
@Suppress("UnstableApiUsage")
|
||||
resourceConfigurations += listOf(
|
||||
"ar",
|
||||
"ar-rSA",
|
||||
"bn-rBD",
|
||||
"cs",
|
||||
"cs-rCZ",
|
||||
"cy-rGB",
|
||||
"da-rDK",
|
||||
"de",
|
||||
"de-rDE",
|
||||
"el-rGR",
|
||||
"en-rGB",
|
||||
"eo",
|
||||
"eo-rUY",
|
||||
"es",
|
||||
"es-rES",
|
||||
"es-rMX",
|
||||
"es-rUS",
|
||||
"et-rEE",
|
||||
"fa",
|
||||
"fa-rIR",
|
||||
"fi-rFI",
|
||||
"fo-rFO",
|
||||
"fr",
|
||||
"fr-rCA",
|
||||
"fr-rFR",
|
||||
"gu-rIN",
|
||||
"hi-rIN",
|
||||
"hr-rHR",
|
||||
"hu",
|
||||
"hu-rHU",
|
||||
"in",
|
||||
"in-rID",
|
||||
"it-rIT",
|
||||
"iw-rIL",
|
||||
"ja",
|
||||
"ja-rJP",
|
||||
"kk-rKZ",
|
||||
"ko-rKR",
|
||||
"ks-rIN",
|
||||
"ku-rTR",
|
||||
"lt-rLT",
|
||||
"ne-rNP",
|
||||
"nl",
|
||||
"nl-rBE",
|
||||
"nl-rNL",
|
||||
"pcm-rNG",
|
||||
"pl-rPL",
|
||||
"pt-rBR",
|
||||
"pt-rPT",
|
||||
"ru",
|
||||
"ru-rRU",
|
||||
"ru-rUA",
|
||||
"sa-rIN",
|
||||
"sl-rSI",
|
||||
"so-rSO",
|
||||
"sr-rSP",
|
||||
"ss-rZA",
|
||||
"sv-rSE",
|
||||
"sw-rKE",
|
||||
"sw-rTZ",
|
||||
"ta",
|
||||
"ta-rIN",
|
||||
"th",
|
||||
"th-rTH",
|
||||
"tr",
|
||||
"tr-rTR",
|
||||
"uk",
|
||||
"uk-rUA",
|
||||
"ur-rIN",
|
||||
"uz-rUZ",
|
||||
"vi-rVN",
|
||||
"zh",
|
||||
"zh-rCN",
|
||||
"zh-rHK",
|
||||
"zh-rSG",
|
||||
"zh-rTW",
|
||||
)
|
||||
}
|
||||
|
||||
// Opt-in fast-build flags. Default behavior is unchanged.
|
||||
//
|
||||
// -PdisableAbiSplits=true skip per-ABI APK splits; produces a single
|
||||
// APK per (flavor, buildType) instead of 5.
|
||||
// Cuts ~600 MB of intermediates and several
|
||||
// minutes off CI.
|
||||
// -PdisableUniversalApk=true when ABI splits are enabled, skip the
|
||||
// extra universal APK output. (No effect
|
||||
// when disableAbiSplits is also set, since
|
||||
// there are no splits to add to.)
|
||||
// -Pamethyst.skipMapping=true disable R8 minification on release and
|
||||
// benchmark. APK is larger, but builds are
|
||||
// much faster and outputs/mapping/ (~260MB)
|
||||
// is not produced. Local-dev and PR-CI use
|
||||
// only — release pipelines must not set it.
|
||||
val disableAbiSplits = providers.gradleProperty("disableAbiSplits")
|
||||
.map { it.toBoolean() }.getOrElse(false)
|
||||
val disableUniversalApk = providers.gradleProperty("disableUniversalApk")
|
||||
.map { it.toBoolean() }.getOrElse(false)
|
||||
val skipMapping = providers.gradleProperty("amethyst.skipMapping")
|
||||
.map { it.toBoolean() }.getOrElse(false)
|
||||
|
||||
buildTypes {
|
||||
getByName("release") {
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
isMinifyEnabled = !skipMapping
|
||||
}
|
||||
getByName("debug") {
|
||||
applicationIdSuffix = ".debug"
|
||||
versionNameSuffix = "-DEBUG"
|
||||
resValue("string", "app_name", "@string/app_name_debug")
|
||||
}
|
||||
create("benchmark") {
|
||||
initWith(getByName("release"))
|
||||
applicationIdSuffix = ".benchmark"
|
||||
versionNameSuffix = "-BENCHMARK"
|
||||
resValue("string", "app_name", "@string/app_name_benchmark")
|
||||
isProfileable = true
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: remove this when lightcompressor uses one MP4 parser only
|
||||
packaging {
|
||||
resources {
|
||||
pickFirsts.add("builddef.lst")
|
||||
pickFirsts.add("META-INF/LICENSE.md")
|
||||
pickFirsts.add("META-INF/LICENSE-notice.md")
|
||||
}
|
||||
}
|
||||
|
||||
flavorDimensions += "channel"
|
||||
|
||||
productFlavors {
|
||||
create("play") {
|
||||
isDefault = true
|
||||
dimension = "channel"
|
||||
buildConfigField("boolean", "IS_CASTING_AVAILABLE", "true")
|
||||
}
|
||||
|
||||
create("fdroid") {
|
||||
dimension = "channel"
|
||||
buildConfigField("boolean", "IS_CASTING_AVAILABLE", "false")
|
||||
}
|
||||
}
|
||||
|
||||
splits {
|
||||
abi {
|
||||
isEnable = !disableAbiSplits
|
||||
reset()
|
||||
include("x86", "x86_64", "arm64-v8a", "armeabi-v7a")
|
||||
isUniversalApk = !disableUniversalApk
|
||||
}
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
compose = true
|
||||
buildConfig = true
|
||||
resValues = true
|
||||
}
|
||||
|
||||
packaging {
|
||||
resources {
|
||||
excludes += listOf("/META-INF/{AL2.0,LGPL2.1}", "**/libscrypt.dylib")
|
||||
}
|
||||
}
|
||||
|
||||
lint {
|
||||
disable += "MissingTranslation"
|
||||
}
|
||||
|
||||
testOptions {
|
||||
unitTests.isReturnDefaultValues = true
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: until google merges and unifiedpush updates https://github.com/tink-crypto/tink-java-apps/pull/5
|
||||
configurations.all {
|
||||
val tink = "com.google.crypto.tink:tink-android:1.17.0"
|
||||
resolutionStrategy {
|
||||
force(tink)
|
||||
dependencySubstitution {
|
||||
substitute(module("com.google.crypto.tink:tink")).using(module(tink))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
jvmTarget.set(JvmTarget.JVM_21)
|
||||
}
|
||||
}
|
||||
|
||||
composeCompiler {
|
||||
reportsDestination = layout.buildDirectory.dir("compose_compiler")
|
||||
metricsDestination = layout.buildDirectory.dir("compose_compiler")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
|
||||
implementation(project(":quartz"))
|
||||
implementation(project(":commons"))
|
||||
implementation(project(":nestsClient"))
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
|
||||
implementation(libs.androidx.ui)
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
|
||||
// Needs this to open gallery / image upload
|
||||
implementation(libs.androidx.fragment.ktx)
|
||||
|
||||
// Navigation
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
|
||||
// Material 3 Design
|
||||
implementation(libs.androidx.material3)
|
||||
|
||||
// Adaptive Layout / Two Pane
|
||||
implementation(libs.androidx.material3.windowSize)
|
||||
implementation(libs.accompanist.adaptive)
|
||||
|
||||
// Lifecycle
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.compose)
|
||||
implementation(libs.androidx.lifecycle.viewmodel.compose)
|
||||
|
||||
// Zoomable images
|
||||
implementation(libs.zoomable)
|
||||
|
||||
// Biometrics
|
||||
implementation(libs.androidx.biometric.ktx)
|
||||
|
||||
// Background Work
|
||||
implementation(libs.androidx.work.runtime.ktx)
|
||||
|
||||
// Websockets API
|
||||
implementation(libs.okhttp)
|
||||
implementation(libs.okhttpCoroutines)
|
||||
|
||||
// Encrypted Key Storage
|
||||
implementation(libs.androidx.security.crypto.ktx)
|
||||
implementation(libs.androidx.datastore.preferences)
|
||||
|
||||
// view videos
|
||||
implementation(libs.androidx.media3.exoplayer)
|
||||
implementation(libs.androidx.media3.exoplayer.hls)
|
||||
implementation(libs.androidx.media3.ui.compose.material3)
|
||||
implementation(libs.androidx.media3.session)
|
||||
|
||||
// important for proxy / tor
|
||||
implementation(libs.androidx.media3.datasource.okhttp)
|
||||
|
||||
// Load images from the web.
|
||||
implementation(libs.coil.compose)
|
||||
// view gifs
|
||||
implementation(libs.coil.gif)
|
||||
// view svgs
|
||||
implementation(libs.coil.svg)
|
||||
// enables network for coil
|
||||
implementation(libs.coil.okhttp)
|
||||
// loads thumbnails for media3
|
||||
// TODO: Replace this to the FrameExtractor in media 3
|
||||
// when FrameExtractor accepts custom data sources.
|
||||
implementation(libs.coil.video)
|
||||
|
||||
// Permission to upload pictures:
|
||||
implementation(libs.accompanist.permissions)
|
||||
|
||||
// For QR generation
|
||||
implementation(libs.zxing)
|
||||
implementation(libs.zxing.embedded)
|
||||
|
||||
// Markdown
|
||||
//implementation "com.halilibo.compose-richtext:richtext-ui:0.16.0"
|
||||
//implementation "com.halilibo.compose-richtext:richtext-ui-material:0.16.0"
|
||||
//implementation "com.halilibo.compose-richtext:richtext-commonmark:0.16.0"
|
||||
|
||||
// Markdown (With fix for full-image bleeds)
|
||||
implementation(libs.markdown.ui)
|
||||
implementation(libs.markdown.ui.material3)
|
||||
implementation(libs.markdown.commonmark)
|
||||
|
||||
// Language picker and Theme chooser
|
||||
implementation(libs.androidx.appcompat)
|
||||
|
||||
// Dynamically adjust between phone and tablet UI
|
||||
implementation(libs.androidx.window.core.android)
|
||||
|
||||
// Local model for language identification
|
||||
"playImplementation"(libs.google.mlkit.language.id)
|
||||
|
||||
// Google services model the translate text
|
||||
"playImplementation"(libs.google.mlkit.translate)
|
||||
|
||||
// On-device AI writing assistance (Gemini Nano via AICore)
|
||||
"playImplementation"(libs.google.mlkit.genai.proofreading)
|
||||
"playImplementation"(libs.google.mlkit.genai.prompt)
|
||||
"playImplementation"(libs.google.mlkit.genai.rewriting)
|
||||
|
||||
// On-device alt-text suggestions: genai image description (preferred, descriptive sentences)
|
||||
// with image-labeling as a keyword-join fallback for devices without AICore.
|
||||
"playImplementation"(libs.google.mlkit.genai.image.description)
|
||||
|
||||
// PushNotifications
|
||||
"playImplementation"(platform(libs.firebase.bom))
|
||||
"playImplementation"(libs.firebase.messaging)
|
||||
|
||||
//PushNotifications(FDroid)
|
||||
"fdroidImplementation"(libs.unifiedpush)
|
||||
|
||||
// Google Cast SDK — Chromecast support. Play flavor only because the
|
||||
// framework hard-depends on Google Play services, which is unavailable
|
||||
// on de-Googled / GrapheneOS devices that ship the F-Droid build.
|
||||
"playImplementation"(libs.play.services.cast.framework)
|
||||
|
||||
// Charts
|
||||
implementation(libs.vico.charts.compose)
|
||||
implementation(libs.vico.charts.m3)
|
||||
|
||||
// Waveform visualizer
|
||||
implementation(libs.audiowaveform)
|
||||
|
||||
// Video compression lib
|
||||
implementation(libs.abedElazizShe.video.compressor.fork)
|
||||
// Image compression lib
|
||||
implementation(libs.zelory.image.compressor)
|
||||
|
||||
// EXIF metadata stripping
|
||||
implementation(libs.androidx.exifinterface)
|
||||
|
||||
// Voice anonymization DSP
|
||||
implementation(libs.tarsosdsp)
|
||||
|
||||
// WebRTC for voice/video calls
|
||||
implementation(libs.stream.webrtc.android)
|
||||
|
||||
// Cbor for cashuB format
|
||||
implementation(libs.kotlinx.serialization.cbor)
|
||||
|
||||
// Kotlin serialization for the times where we need the Json tree and performance is not that important.
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
|
||||
testImplementation(libs.junit)
|
||||
testImplementation(libs.mockk)
|
||||
testImplementation(libs.kotlinx.coroutines.test)
|
||||
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.junit.ktx)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
androidTestImplementation(libs.mockk.android)
|
||||
|
||||
debugImplementation(platform(libs.androidx.compose.bom))
|
||||
debugImplementation(libs.androidx.ui.tooling)
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
|
||||
implementation(libs.androidx.camera.core)
|
||||
implementation(libs.androidx.camera.camera2)
|
||||
implementation(libs.androidx.camera.lifecycle)
|
||||
implementation(libs.androidx.camera.view)
|
||||
implementation(libs.androidx.camera.extensions)
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.androidLibrary)
|
||||
alias(libs.plugins.androidBenchmark)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = 'com.vitorpamplona.amethyst.benchmark'
|
||||
compileSdk = libs.versions.android.compileSdk.get().toInteger()
|
||||
|
||||
defaultConfig {
|
||||
minSdk = libs.versions.android.minSdk.get().toInteger()
|
||||
targetSdk = libs.versions.android.targetSdk.get().toInteger()
|
||||
|
||||
// Enable measuring on an emulator, or devices with low battery
|
||||
testInstrumentationRunner 'androidx.benchmark.junit4.AndroidBenchmarkRunner'
|
||||
testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "EMULATOR,LOW-BATTERY"
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_21
|
||||
targetCompatibility JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
sourceSets.androidTest.resources.srcDirs += ["../quartz/src/androidDeviceTest/resources"]
|
||||
|
||||
testBuildType = "benchmark"
|
||||
buildTypes {
|
||||
debug {
|
||||
// Since debuggable can"t be modified by gradle for library modules,
|
||||
// it must be done in a manifest - see src/androidTest/AndroidManifest.xml
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "benchmark-proguard-rules.pro"
|
||||
}
|
||||
release {
|
||||
isDefault = false
|
||||
minifyEnabled = true
|
||||
}
|
||||
create("benchmark") {
|
||||
isDefault = true
|
||||
initWith(getByName("release"))
|
||||
signingConfig = signingConfigs.debug
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
jvmTarget = JvmTarget.JVM_21
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation libs.androidx.core.ktx
|
||||
androidTestImplementation platform(libs.androidx.compose.bom)
|
||||
androidTestImplementation libs.androidx.runner
|
||||
androidTestImplementation libs.androidx.junit
|
||||
androidTestImplementation libs.junit
|
||||
androidTestImplementation libs.androidx.benchmark.junit4
|
||||
androidTestImplementation project(path: ':quartz')
|
||||
androidTestImplementation project(path: ':commons')
|
||||
|
||||
// Custom C secp256k1 (libschnorr256k1) for the 3-way Android benchmark
|
||||
androidTestImplementation libs.schnorr256k1.kmp
|
||||
|
||||
androidTestImplementation libs.androidx.compose.foundation
|
||||
|
||||
// Add your dependencies here. Note that you cannot benchmark code
|
||||
// in an app module this way - you will need to move any code you
|
||||
// want to benchmark to a library module:
|
||||
// https://developer.android.com/studio/projects/android-library#Convert
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.androidLibrary)
|
||||
alias(libs.plugins.androidBenchmark)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.vitorpamplona.amethyst.benchmark"
|
||||
compileSdk = libs.versions.android.compileSdk.get().toInt()
|
||||
|
||||
defaultConfig {
|
||||
minSdk = libs.versions.android.minSdk.get().toInt()
|
||||
|
||||
// Enable measuring on an emulator, or devices with low battery
|
||||
testInstrumentationRunner = "androidx.benchmark.junit4.AndroidBenchmarkRunner"
|
||||
testInstrumentationRunnerArguments["androidx.benchmark.suppressErrors"] = "EMULATOR,LOW-BATTERY"
|
||||
}
|
||||
|
||||
testOptions {
|
||||
targetSdk = libs.versions.android.targetSdk.get().toInt()
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
getByName("androidTest") {
|
||||
resources.srcDirs("../quartz/src/androidDeviceTest/resources")
|
||||
}
|
||||
}
|
||||
|
||||
testBuildType = "benchmark"
|
||||
buildTypes {
|
||||
getByName("debug") {
|
||||
// Since debuggable can"t be modified by gradle for library modules,
|
||||
// it must be done in a manifest - see src/androidTest/AndroidManifest.xml
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "benchmark-proguard-rules.pro")
|
||||
}
|
||||
getByName("release") {
|
||||
isDefault = false
|
||||
isMinifyEnabled = true
|
||||
}
|
||||
create("benchmark") {
|
||||
isDefault = true
|
||||
initWith(getByName("release"))
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kotlin {
|
||||
compilerOptions {
|
||||
jvmTarget.set(JvmTarget.JVM_21)
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.androidx.core.ktx)
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.runner)
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.junit)
|
||||
androidTestImplementation(libs.androidx.benchmark.junit4)
|
||||
androidTestImplementation(project(":quartz"))
|
||||
androidTestImplementation(project(":commons"))
|
||||
|
||||
// Custom C secp256k1 (libschnorr256k1) for the 3-way Android benchmark
|
||||
androidTestImplementation(libs.schnorr256k1.kmp)
|
||||
|
||||
androidTestImplementation(libs.androidx.compose.foundation)
|
||||
|
||||
// Add your dependencies here. Note that you cannot benchmark code
|
||||
// in an app module this way - you will need to move any code you
|
||||
// want to benchmark to a library module:
|
||||
// https://developer.android.com/studio/projects/android-library#Convert
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import com.diffplug.gradle.spotless.SpotlessExtensionPredeclare
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.androidApplication) apply false
|
||||
alias(libs.plugins.androidLibrary) apply false
|
||||
@@ -13,37 +15,42 @@ plugins {
|
||||
}
|
||||
|
||||
// Shared app version for all subprojects — read from gradle/libs.versions.toml.
|
||||
// Android versionCode stays local in amethyst/build.gradle (must be monotonic int).
|
||||
// Android versionCode stays local in amethyst/build.gradle.kts (must be monotonic int).
|
||||
// Desktop packageVersion inherits via project.version in desktopApp/build.gradle.kts.
|
||||
val appVersion = libs.versions.app.get()
|
||||
|
||||
allprojects {
|
||||
version = libs.versions.app.get()
|
||||
version = appVersion
|
||||
|
||||
configurations.configureEach {
|
||||
resolutionStrategy.cacheChangingModulesFor 0, 'seconds'
|
||||
resolutionStrategy.cacheChangingModulesFor(0, "seconds")
|
||||
}
|
||||
|
||||
apply plugin: 'com.diffplug.spotless'
|
||||
apply(plugin = "com.diffplug.spotless")
|
||||
|
||||
if (project === rootProject) {
|
||||
spotless {
|
||||
predeclareDeps()
|
||||
}
|
||||
spotlessPredeclare {
|
||||
configure<SpotlessExtensionPredeclare> {
|
||||
kotlin {
|
||||
ktlint('1.7.1')
|
||||
ktlint("1.7.1")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
spotless {
|
||||
kotlin {
|
||||
target 'src/**/*.kt'
|
||||
target("src/**/*.kt")
|
||||
|
||||
ktlint('1.7.1')
|
||||
licenseHeaderFile rootProject.file('.spotless/copyright.kt'), "@file:|package|import|class|object|sealed|open|interface|abstract "
|
||||
ktlint("1.7.1")
|
||||
licenseHeaderFile(
|
||||
rootProject.file(".spotless/copyright.kt"),
|
||||
"@file:|package|import|class|object|sealed|open|interface|abstract ",
|
||||
)
|
||||
}
|
||||
|
||||
groovyGradle {
|
||||
target '*.gradle'
|
||||
kotlinGradle {
|
||||
target("*.gradle.kts")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,30 +62,28 @@ subprojects {
|
||||
tasks.named("preBuild") {
|
||||
dependsOn("spotlessApply")
|
||||
}
|
||||
} catch (UnknownTaskException ignored) {
|
||||
} catch (ignored: UnknownTaskException) {
|
||||
tasks.matching {
|
||||
it.name.startsWith("pre") && it.name.endsWith("Build")
|
||||
}.configureEach {
|
||||
dependsOn("spotlessApply")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('installGitHook', Copy) {
|
||||
def dotGit = new File(rootProject.rootDir, '.git')
|
||||
def hooksDir
|
||||
if (dotGit.isFile()) {
|
||||
val installGitHook = tasks.register<Copy>("installGitHook") {
|
||||
val dotGit = File(rootProject.rootDir, ".git")
|
||||
val hooksDir: File = if (dotGit.isFile) {
|
||||
// Git worktree: .git is a file with "gitdir: <path>"
|
||||
def gitDir = new File(dotGit.text.trim().replace('gitdir: ', ''))
|
||||
hooksDir = new File(gitDir, 'hooks')
|
||||
val gitDir = File(dotGit.readText().trim().replace("gitdir: ", ""))
|
||||
File(gitDir, "hooks")
|
||||
} else {
|
||||
hooksDir = new File(dotGit, 'hooks')
|
||||
File(dotGit, "hooks")
|
||||
}
|
||||
from new File(rootProject.rootDir, '.git-hooks/pre-commit')
|
||||
from new File(rootProject.rootDir, '.git-hooks/pre-push')
|
||||
into { hooksDir }
|
||||
filePermissions { unix(0777) }
|
||||
from(File(rootProject.rootDir, ".git-hooks/pre-commit"))
|
||||
from(File(rootProject.rootDir, ".git-hooks/pre-push"))
|
||||
into(hooksDir)
|
||||
filePermissions { unix("0777") }
|
||||
}
|
||||
tasks.getByPath(':amethyst:preBuild').dependsOn installGitHook
|
||||
tasks.getByPath(":amethyst:preBuild").dependsOn(installGitHook)
|
||||
@@ -10,9 +10,9 @@ pluginManagement {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
maven {
|
||||
url = "https://jitpack.io"
|
||||
url = uri("https://jitpack.io")
|
||||
content {
|
||||
includeModule 'com.github.UnifiedPush', 'android-connector'
|
||||
includeModule("com.github.UnifiedPush", "android-connector")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,21 +24,21 @@ dependencyResolutionManagement {
|
||||
mavenLocal()
|
||||
google()
|
||||
mavenCentral()
|
||||
maven { url = "https://jitpack.io" }
|
||||
maven { url = "https://raw.githubusercontent.com/guardianproject/gpmaven/master" }
|
||||
maven { url = "https://mvn.0110.be/releases" }
|
||||
maven { url = uri("https://jitpack.io") }
|
||||
maven { url = uri("https://raw.githubusercontent.com/guardianproject/gpmaven/master") }
|
||||
maven { url = uri("https://mvn.0110.be/releases") }
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = "Amethyst"
|
||||
include ':amethyst'
|
||||
include ':benchmark'
|
||||
include ':quartz'
|
||||
include ':geode'
|
||||
include ':commons'
|
||||
include ':quic'
|
||||
include ':nestsClient'
|
||||
include ':desktopApp'
|
||||
include ':cli'
|
||||
include ':quic-interop'
|
||||
project(':quic-interop').projectDir = file('quic/interop')
|
||||
include(":amethyst")
|
||||
include(":benchmark")
|
||||
include(":quartz")
|
||||
include(":geode")
|
||||
include(":commons")
|
||||
include(":quic")
|
||||
include(":nestsClient")
|
||||
include(":desktopApp")
|
||||
include(":cli")
|
||||
include(":quic-interop")
|
||||
project(":quic-interop").projectDir = file("quic/interop")
|
||||
Reference in New Issue
Block a user