better performance
This commit is contained in:
@@ -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.
|
||||
+26
-28
@@ -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")
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,8 @@ class ScannerViewModel(
|
||||
) : ViewModel() {
|
||||
private val _uiState = MutableStateFlow(ScannerUiState())
|
||||
val uiState: StateFlow<ScannerUiState> = _uiState.asStateFlow()
|
||||
private val recentScanKeySet = LinkedHashSet<String>(200)
|
||||
private val batchKeySet = LinkedHashSet<String>(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())
|
||||
}
|
||||
|
||||
|
||||
@@ -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<androidx.camera.core.Camera?>(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<ProcessCameraProvider?>(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(
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -52,4 +52,13 @@
|
||||
<string name="open_wifi_settings">WLAN-Einstellungen öffnen</string>
|
||||
<string name="add_contact">Kontakt hinzufügen</string>
|
||||
<string name="add_calendar_event">Kalendereintrag hinzufügen</string>
|
||||
<string name="support_email">support@example.com</string>
|
||||
<string name="feature_request">Feature-Request-Formular</string>
|
||||
<string name="feature_request_title">Feature-Request</string>
|
||||
<string name="feature_request_name">Dein Name</string>
|
||||
<string name="feature_request_email">Deine E-Mail</string>
|
||||
<string name="feature_request_details">Was brauchst du?</string>
|
||||
<string name="feature_request_subject">Feature-Request von App-Nutzer</string>
|
||||
<string name="send_request">Anfrage senden</string>
|
||||
<string name="feature_request_sent">E-Mail-App wird geöffnet...</string>
|
||||
</resources>
|
||||
|
||||
@@ -52,4 +52,13 @@
|
||||
<string name="open_wifi_settings">Open Wi-Fi settings</string>
|
||||
<string name="add_contact">Add contact</string>
|
||||
<string name="add_calendar_event">Add calendar event</string>
|
||||
<string name="support_email">support@example.com</string>
|
||||
<string name="feature_request">Feature request form</string>
|
||||
<string name="feature_request_title">Feature request</string>
|
||||
<string name="feature_request_name">Your name</string>
|
||||
<string name="feature_request_email">Your email</string>
|
||||
<string name="feature_request_details">What do you need?</string>
|
||||
<string name="feature_request_subject">Feature request from app user</string>
|
||||
<string name="send_request">Send request</string>
|
||||
<string name="feature_request_sent">Opening email app...</string>
|
||||
</resources>
|
||||
|
||||
+3
-3
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
+1
-1
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user