From f8aa3a7bc0f4a8154962d43366479860a3f84847 Mon Sep 17 00:00:00 2001 From: Hadrian Burkhardt Date: Thu, 12 Feb 2026 23:29:43 +0100 Subject: [PATCH] better performance --- .../gradle-9.2.1-all.zip.lck | 0 .../gradle-9.2.1-all.zip.part | 0 USE_CASES.md | 53 ++++++++++++++++++ app/build.gradle.kts | 54 +++++++++--------- .../data/scanner/MlKitBarcodeAnalyzer.kt | 45 ++++++++++----- .../com/clean/scanner/ui/ScannerViewModel.kt | 22 ++++++-- .../scanner/ui/components/CameraPreview.kt | 23 +++++--- .../scanner/ui/screens/SettingsScreen.kt | 56 +++++++++++++++++++ .../java/com/clean/scanner/util/Intents.kt | 5 +- app/src/main/res/values-de/strings.xml | 9 +++ app/src/main/res/values/strings.xml | 9 +++ build.gradle.kts | 6 +- gradle.properties | 9 +++ gradle/gradle-daemon-jvm.properties | 13 +++++ gradle/wrapper/gradle-wrapper.properties | 2 +- settings.gradle.kts | 3 + 16 files changed, 250 insertions(+), 59 deletions(-) create mode 100644 .gradle-local/wrapper/dists/gradle-9.2.1-all/2lbhfocpgk6niea1fja7mj8kz/gradle-9.2.1-all.zip.lck create mode 100644 .gradle-local/wrapper/dists/gradle-9.2.1-all/2lbhfocpgk6niea1fja7mj8kz/gradle-9.2.1-all.zip.part create mode 100644 USE_CASES.md create mode 100644 gradle/gradle-daemon-jvm.properties diff --git a/.gradle-local/wrapper/dists/gradle-9.2.1-all/2lbhfocpgk6niea1fja7mj8kz/gradle-9.2.1-all.zip.lck b/.gradle-local/wrapper/dists/gradle-9.2.1-all/2lbhfocpgk6niea1fja7mj8kz/gradle-9.2.1-all.zip.lck new file mode 100644 index 0000000..e69de29 diff --git a/.gradle-local/wrapper/dists/gradle-9.2.1-all/2lbhfocpgk6niea1fja7mj8kz/gradle-9.2.1-all.zip.part b/.gradle-local/wrapper/dists/gradle-9.2.1-all/2lbhfocpgk6niea1fja7mj8kz/gradle-9.2.1-all.zip.part new file mode 100644 index 0000000..e69de29 diff --git a/USE_CASES.md b/USE_CASES.md new file mode 100644 index 0000000..bac7540 --- /dev/null +++ b/USE_CASES.md @@ -0,0 +1,53 @@ +# Clean Scanner Use Cases + +## 1. Everyday Personal Use +- Scan restaurant menus, product QR labels, and website links quickly. +- Copy/share scanned values to chat apps or notes. +- Open links directly with local risk warning support. + +## 2. Event & Ticketing +- Scan tickets at venues and quickly validate repeated entries. +- Use batch mode to process multiple attendees without leaving the camera. +- Share batch captures to organizers for quick reconciliation. + +## 3. Inventory & Operations +- Scan product barcodes in stock rooms. +- Use batch mode for continuous scanning of many items. +- Export and share history (TXT/CSV/JSON) for downstream reporting. + +## 4. Field Work & Service Teams +- Scan device labels/serials on-site. +- Save local history for audit trails when enabled. +- Share captured codes with support teams in real time. + +## 5. Office & Admin Workflows +- Scan contact QR/vCard and add to contacts. +- Scan Wi-Fi setup QR and jump to Wi-Fi settings. +- Scan calendar/event data and create calendar entries. + +## 6. Communication Shortcuts +- Scan phone/SMS/email QR data. +- One-tap actions: call, send SMS, send email. +- Reduce manual entry errors for phone numbers and addresses. + +## 7. Security-Conscious Browsing +- Scan URL QR codes and get local warning prompts for suspicious patterns. +- Decide whether to open risky links with explicit confirmation. +- Keep scanning offline-first without backend calls. + +## 8. Offline / Low-Connectivity Scenarios +- Use the scanner with no internet dependency for core scanning. +- Keep data local-first and share outputs when connectivity returns. +- Useful for travel, warehouses, and remote job sites. + +## 9. Accessibility & Speed +- Launch directly into camera for 0-click scan flow. +- Pinch-to-zoom for small or distant QR/barcodes. +- Friendly scanner guide with immediate feedback on successful scans. + +## 10. Team Handover & Data Transfer +- Export scan history in multiple formats: + - TXT for human-readable logs + - CSV for spreadsheets/BI tools + - JSON for system integrations +- Share exports to teammates via native Android share sheet. diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 8afe4d7..a2930ac 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,17 +1,17 @@ plugins { id("com.android.application") - id("org.jetbrains.kotlin.android") id("com.google.devtools.ksp") + id("org.jetbrains.kotlin.plugin.compose") } android { namespace = "com.clean.scanner" - compileSdk = 35 + compileSdk = 36 defaultConfig { applicationId = "com.clean.scanner" minSdk = 24 - targetSdk = 35 + targetSdk = 36 versionCode = 1 versionName = "1.0.0" buildConfigField("boolean", "FEATURE_PAYWALL_ENABLED", "false") @@ -40,19 +40,11 @@ android { targetCompatibility = JavaVersion.VERSION_17 } - kotlinOptions { - jvmTarget = "17" - } - buildFeatures { compose = true buildConfig = true } - composeOptions { - kotlinCompilerExtensionVersion = "1.5.14" - } - packaging { resources { excludes += "/META-INF/{AL2.0,LGPL2.1}" @@ -60,41 +52,47 @@ android { } } +kotlin { + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) + } +} + dependencies { val composeBom = platform("androidx.compose:compose-bom:2024.09.00") implementation(composeBom) androidTestImplementation(composeBom) - implementation("androidx.core:core-ktx:1.13.1") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.6") - implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.6") - implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.6") - implementation("androidx.activity:activity-compose:1.9.2") + implementation("androidx.core:core-ktx:1.17.0") + implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.10.0") + implementation("androidx.lifecycle:lifecycle-runtime-compose:2.10.0") + implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.10.0") + implementation("androidx.activity:activity-compose:1.12.3") implementation("androidx.compose.ui:ui") implementation("androidx.compose.ui:ui-tooling-preview") implementation("androidx.compose.material3:material3") implementation("androidx.compose.material:material-icons-extended") - implementation("com.google.android.material:material:1.12.0") - implementation("androidx.navigation:navigation-compose:2.8.2") + implementation("com.google.android.material:material:1.13.0") + implementation("androidx.navigation:navigation-compose:2.9.7") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.2") - implementation("androidx.camera:camera-core:1.3.4") - implementation("androidx.camera:camera-camera2:1.3.4") - implementation("androidx.camera:camera-lifecycle:1.3.4") - implementation("androidx.camera:camera-view:1.3.4") + implementation("androidx.camera:camera-core:1.5.3") + implementation("androidx.camera:camera-camera2:1.5.3") + implementation("androidx.camera:camera-lifecycle:1.5.3") + implementation("androidx.camera:camera-view:1.5.3") implementation("com.google.mlkit:barcode-scanning:17.3.0") - implementation("androidx.room:room-runtime:2.6.1") - implementation("androidx.room:room-ktx:2.6.1") - ksp("androidx.room:room-compiler:2.6.1") + implementation("androidx.room:room-runtime:2.8.4") + implementation("androidx.room:room-ktx:2.8.4") + ksp("androidx.room:room-compiler:2.8.4") - implementation("androidx.datastore:datastore-preferences:1.1.1") + implementation("androidx.datastore:datastore-preferences:1.2.0") testImplementation("junit:junit:4.13.2") - testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.1") + testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.2") debugImplementation("androidx.compose.ui:ui-tooling") debugImplementation("androidx.compose.ui:ui-test-manifest") diff --git a/app/src/main/java/com/clean/scanner/data/scanner/MlKitBarcodeAnalyzer.kt b/app/src/main/java/com/clean/scanner/data/scanner/MlKitBarcodeAnalyzer.kt index 05c4898..db32d8c 100644 --- a/app/src/main/java/com/clean/scanner/data/scanner/MlKitBarcodeAnalyzer.kt +++ b/app/src/main/java/com/clean/scanner/data/scanner/MlKitBarcodeAnalyzer.kt @@ -4,20 +4,30 @@ import androidx.camera.core.ImageAnalysis import androidx.camera.core.ImageProxy import com.clean.scanner.domain.ScanResult import com.google.mlkit.vision.barcode.BarcodeScanning +import com.google.mlkit.vision.barcode.common.Barcode import com.google.mlkit.vision.common.InputImage class MlKitBarcodeAnalyzer( + private val isAnalysisEnabled: () -> Boolean = { true }, private val onDetected: (ScanResult) -> Unit -) : ImageAnalysis.Analyzer { +) : ImageAnalysis.Analyzer, AutoCloseable { private val scanner = BarcodeScanning.getClient() + @Volatile + private var processing = false override fun analyze(imageProxy: ImageProxy) { + if (!isAnalysisEnabled() || processing) { + imageProxy.close() + return + } + val mediaImage = imageProxy.image ?: run { imageProxy.close() return } val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) + processing = true scanner.process(image) .addOnSuccessListener { barcodes -> @@ -26,22 +36,29 @@ class MlKitBarcodeAnalyzer( val type = first.valueType.toHumanType() onDetected(ScanResult(content = raw, type = type)) } - .addOnCompleteListener { imageProxy.close() } + .addOnCompleteListener { + processing = false + imageProxy.close() + } } private fun Int.toHumanType(): String = when (this) { - 1 -> "Contact" - 2 -> "Email" - 3 -> "ISBN" - 4 -> "Phone" - 5 -> "Product" - 6 -> "SMS" - 7 -> "Text" - 8 -> "URL" - 9 -> "WiFi" - 10 -> "Geo" - 11 -> "Calendar" - 12 -> "Driver license" + Barcode.TYPE_CONTACT_INFO -> "Contact" + Barcode.TYPE_EMAIL -> "Email" + Barcode.TYPE_ISBN -> "ISBN" + Barcode.TYPE_PHONE -> "Phone" + Barcode.TYPE_PRODUCT -> "Product" + Barcode.TYPE_SMS -> "SMS" + Barcode.TYPE_TEXT -> "Text" + Barcode.TYPE_URL -> "URL" + Barcode.TYPE_WIFI -> "WiFi" + Barcode.TYPE_GEO -> "Geo" + Barcode.TYPE_CALENDAR_EVENT -> "Calendar" + Barcode.TYPE_DRIVER_LICENSE -> "Driver license" else -> "Unknown" } + + override fun close() { + scanner.close() + } } diff --git a/app/src/main/java/com/clean/scanner/ui/ScannerViewModel.kt b/app/src/main/java/com/clean/scanner/ui/ScannerViewModel.kt index d6bb727..3bc1d2c 100644 --- a/app/src/main/java/com/clean/scanner/ui/ScannerViewModel.kt +++ b/app/src/main/java/com/clean/scanner/ui/ScannerViewModel.kt @@ -32,6 +32,8 @@ class ScannerViewModel( ) : ViewModel() { private val _uiState = MutableStateFlow(ScannerUiState()) val uiState: StateFlow = _uiState.asStateFlow() + private val recentScanKeySet = LinkedHashSet(200) + private val batchKeySet = LinkedHashSet(100) fun onScan(result: ScanResult) { val now = nowProvider() @@ -40,14 +42,25 @@ class ScannerViewModel( if (now - current.lastScanTimestamp < 800) return val key = "${result.type}|${result.content}" - val isDuplicate = current.recentScanKeys.contains(key) - val updatedRecent = (listOf(key) + current.recentScanKeys).distinct().take(200) + val isDuplicate = key in recentScanKeySet + recentScanKeySet.remove(key) + recentScanKeySet.add(key) + while (recentScanKeySet.size > 200) { + recentScanKeySet.remove(recentScanKeySet.first()) + } + val updatedRecent = recentScanKeySet.toList().asReversed() _uiState.value = if (current.batchMode) { - val updatedBatch = if (current.batchResults.any { "${it.result.type}|${it.result.content}" == key }) { + val updatedBatch = if (key in batchKeySet) { current.batchResults } else { - (listOf(BatchScanRecord(result = result, timestamp = now)) + current.batchResults).take(100) + batchKeySet.add(key) + val nextBatch = (listOf(BatchScanRecord(result = result, timestamp = now)) + current.batchResults) + .take(100) + while (batchKeySet.size > 100) { + batchKeySet.remove(batchKeySet.first()) + } + nextBatch } current.copy( lastResult = null, @@ -87,6 +100,7 @@ class ScannerViewModel( } fun clearBatchResults() { + batchKeySet.clear() _uiState.value = _uiState.value.copy(batchResults = emptyList()) } diff --git a/app/src/main/java/com/clean/scanner/ui/components/CameraPreview.kt b/app/src/main/java/com/clean/scanner/ui/components/CameraPreview.kt index 0c627a3..609361c 100644 --- a/app/src/main/java/com/clean/scanner/ui/components/CameraPreview.kt +++ b/app/src/main/java/com/clean/scanner/ui/components/CameraPreview.kt @@ -13,6 +13,7 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberUpdatedState import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.compose.LocalLifecycleOwner @@ -39,6 +40,15 @@ fun CameraPreview( val previewView = remember { PreviewView(context) } val cameraRef = remember { mutableStateOf(null) } val zoomRatio = remember { mutableStateOf(1f) } + val latestAnalysisEnabled = rememberUpdatedState(analysisEnabled) + val latestOnScan = rememberUpdatedState(onScan) + val analyzer = remember { + MlKitBarcodeAnalyzer( + isAnalysisEnabled = { latestAnalysisEnabled.value }, + onDetected = { result -> latestOnScan.value(result.content, result.type) } + ) + } + val cameraProviderRef = remember { mutableStateOf(null) } val scaleGestureDetector = remember { ScaleGestureDetector(context, object : ScaleGestureDetector.SimpleOnScaleGestureListener() { @@ -56,12 +66,15 @@ fun CameraPreview( DisposableEffect(Unit) { onDispose { + cameraProviderRef.value?.unbindAll() + analyzer.close() cameraExecutor.shutdown() } } - LaunchedEffect(analysisEnabled) { + LaunchedEffect(lifecycleOwner) { val provider = ProcessCameraProvider.getInstance(context).get() + cameraProviderRef.value = provider provider.unbindAll() val preview = Preview.Builder().build().apply { @@ -71,13 +84,7 @@ fun CameraPreview( val imageAnalysis = ImageAnalysis.Builder() .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .build().apply { - if (analysisEnabled) { - setAnalyzer(cameraExecutor, MlKitBarcodeAnalyzer { result -> - onScan(result.content, result.type) - }) - } else { - clearAnalyzer() - } + setAnalyzer(cameraExecutor, analyzer) } val camera = provider.bindToLifecycle( diff --git a/app/src/main/java/com/clean/scanner/ui/screens/SettingsScreen.kt b/app/src/main/java/com/clean/scanner/ui/screens/SettingsScreen.kt index fbe4adc..479c792 100644 --- a/app/src/main/java/com/clean/scanner/ui/screens/SettingsScreen.kt +++ b/app/src/main/java/com/clean/scanner/ui/screens/SettingsScreen.kt @@ -1,5 +1,6 @@ package com.clean.scanner.ui.screens +import android.widget.Toast import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -7,6 +8,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.material3.AlertDialog +import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.material3.TextButton @@ -14,9 +16,11 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import com.clean.scanner.R +import com.clean.scanner.util.Intents @Composable fun SettingsScreen( @@ -27,7 +31,10 @@ fun SettingsScreen( onWarningsToggle: (Boolean) -> Unit, onScanFeedbackToggle: (Boolean) -> Unit ) { + val context = LocalContext.current val showDeleteConfirm = remember { mutableStateOf(false) } + val showFeatureRequestForm = remember { mutableStateOf(false) } + val requesterNeed = remember { mutableStateOf("") } if (showDeleteConfirm.value) { AlertDialog( @@ -49,6 +56,51 @@ fun SettingsScreen( ) } + if (showFeatureRequestForm.value) { + AlertDialog( + onDismissRequest = { showFeatureRequestForm.value = false }, + title = { Text(stringResource(R.string.feature_request_title)) }, + text = { + Column(verticalArrangement = Arrangement.spacedBy(8.dp)) { + OutlinedTextField( + value = requesterNeed.value, + onValueChange = { requesterNeed.value = it }, + label = { Text(stringResource(R.string.feature_request_details)) } + ) + } + }, + confirmButton = { + TextButton( + onClick = { + val body = buildString { + appendLine("Request:") + append(requesterNeed.value.trim()) + } + Intents.sendEmail( + context = context, + email = context.getString(R.string.support_email), + subject = context.getString(R.string.feature_request_subject), + body = body + ) + showFeatureRequestForm.value = false + requesterNeed.value = "" + Toast.makeText( + context, + context.getString(R.string.feature_request_sent), + Toast.LENGTH_SHORT + ).show() + }, + enabled = requesterNeed.value.isNotBlank() + ) { Text(stringResource(R.string.send_request)) } + }, + dismissButton = { + TextButton(onClick = { showFeatureRequestForm.value = false }) { + Text(stringResource(R.string.cancel)) + } + } + ) + } + Column( modifier = Modifier .fillMaxSize() @@ -82,5 +134,9 @@ fun SettingsScreen( Text(text = stringResource(R.string.version)) Text(text = stringResource(R.string.licenses)) Text(text = stringResource(R.string.contact)) + Spacer(modifier = Modifier.height(12.dp)) + TextButton(onClick = { showFeatureRequestForm.value = true }) { + Text(text = stringResource(R.string.feature_request)) + } } } diff --git a/app/src/main/java/com/clean/scanner/util/Intents.kt b/app/src/main/java/com/clean/scanner/util/Intents.kt index e0283a7..e29a446 100644 --- a/app/src/main/java/com/clean/scanner/util/Intents.kt +++ b/app/src/main/java/com/clean/scanner/util/Intents.kt @@ -43,12 +43,15 @@ object Intents { startActivity(context, intent, null) } - fun sendEmail(context: Context, email: String, subject: String?) { + fun sendEmail(context: Context, email: String, subject: String?, body: String? = null) { val intent = Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:${email.trim()}")) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) if (!subject.isNullOrBlank()) { intent.putExtra(Intent.EXTRA_SUBJECT, subject) } + if (!body.isNullOrBlank()) { + intent.putExtra(Intent.EXTRA_TEXT, body) + } startActivity(context, intent, null) } diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 8818854..c96f595 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -52,4 +52,13 @@ WLAN-Einstellungen öffnen Kontakt hinzufügen Kalendereintrag hinzufügen + support@example.com + Feature-Request-Formular + Feature-Request + Dein Name + Deine E-Mail + Was brauchst du? + Feature-Request von App-Nutzer + Anfrage senden + E-Mail-App wird geöffnet... diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fcd1e5e..2e8ffc3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -52,4 +52,13 @@ Open Wi-Fi settings Add contact Add calendar event + support@example.com + Feature request form + Feature request + Your name + Your email + What do you need? + Feature request from app user + Send request + Opening email app... diff --git a/build.gradle.kts b/build.gradle.kts index 60fafab..801cd3f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("com.android.application") version "8.13.2" apply false - id("org.jetbrains.kotlin.android") version "1.9.24" apply false - id("com.google.devtools.ksp") version "1.9.24-1.0.20" apply false + id("com.android.application") version "9.0.0" apply false + id("com.google.devtools.ksp") version "2.3.5" apply false + id("org.jetbrains.kotlin.plugin.compose") version "2.2.10" apply false } diff --git a/gradle.properties b/gradle.properties index 2318707..fedc9e4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,3 +2,12 @@ org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8 android.useAndroidX=true kotlin.code.style=official android.nonTransitiveRClass=true +android.uniquePackageNames=false +android.dependency.useConstraints=true +android.r8.strictFullModeForKeepRules=false +android.defaults.buildfeatures.resvalues=false +android.sdk.defaultTargetSdkToCompileSdkIfUnset=true +android.usesSdkInManifest.disallowed=true +android.builtInKotlin=true +android.r8.optimizedResourceShrinking=true +android.newDsl=true \ No newline at end of file diff --git a/gradle/gradle-daemon-jvm.properties b/gradle/gradle-daemon-jvm.properties new file mode 100644 index 0000000..62db551 --- /dev/null +++ b/gradle/gradle-daemon-jvm.properties @@ -0,0 +1,13 @@ +#This file is generated by updateDaemonJvm +toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/29ee363f71d060405f729a8f1b7f7aef/redirect +toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/67a0fee3c4236b6397dcbe8575ca2011/redirect +toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/29ee363f71d060405f729a8f1b7f7aef/redirect +toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/ecd23fd7707c683afbcd6052998cb6a9/redirect +toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/10fc3bf1ee0001078a473afe6e43cfdb/redirect +toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/658299a896470fbb3103ba3a430ee227/redirect +toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/29ee363f71d060405f729a8f1b7f7aef/redirect +toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/ecd23fd7707c683afbcd6052998cb6a9/redirect +toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/23adb857f3cb3cbe28750bc7faa7abc0/redirect +toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/932015f6361ccaead0c6d9b8717ed96e/redirect +toolchainVendor=JETBRAINS +toolchainVersion=21 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f407850..cf64724 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-all.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle.kts b/settings.gradle.kts index b315feb..070a854 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -5,6 +5,9 @@ pluginManagement { gradlePluginPortal() } } +plugins { + id("org.gradle.toolchains.foojay-resolver-convention") version "1.0.0" +} dependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)