diff --git a/amethyst/build.gradle b/amethyst/build.gradle deleted file mode 100644 index 2ce9a3313..000000000 --- a/amethyst/build.gradle +++ /dev/null @@ -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 -} \ No newline at end of file diff --git a/amethyst/build.gradle.kts b/amethyst/build.gradle.kts new file mode 100644 index 000000000..1b3df1ef1 --- /dev/null +++ b/amethyst/build.gradle.kts @@ -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) +} diff --git a/benchmark/build.gradle b/benchmark/build.gradle deleted file mode 100644 index 91df941ea..000000000 --- a/benchmark/build.gradle +++ /dev/null @@ -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 -} \ No newline at end of file diff --git a/benchmark/build.gradle.kts b/benchmark/build.gradle.kts new file mode 100644 index 000000000..65b273b16 --- /dev/null +++ b/benchmark/build.gradle.kts @@ -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 +} diff --git a/build.gradle b/build.gradle.kts similarity index 52% rename from build.gradle rename to build.gradle.kts index 1f0c6a927..8d1b95709 100644 --- a/build.gradle +++ b/build.gradle.kts @@ -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 { 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("installGitHook") { + val dotGit = File(rootProject.rootDir, ".git") + val hooksDir: File = if (dotGit.isFile) { // Git worktree: .git is a file with "gitdir: " - 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) diff --git a/settings.gradle b/settings.gradle.kts similarity index 50% rename from settings.gradle rename to settings.gradle.kts index d70eb627d..eb3c2a376 100644 --- a/settings.gradle +++ b/settings.gradle.kts @@ -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")