Añadida la clase conexion y la apertura del lector de codigos.
This commit is contained in:
Generated
+11
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="deploymentTargetSelector">
|
||||||
|
<selectionStates>
|
||||||
|
<SelectionState runConfigName="app">
|
||||||
|
<option name="selectionMode" value="DROPDOWN" />
|
||||||
|
<DialogSelection />
|
||||||
|
</SelectionState>
|
||||||
|
</selectionStates>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
Generated
+10
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="PlanningModeManager">
|
||||||
|
<option name="approvalStates">
|
||||||
|
<map>
|
||||||
|
<entry key="0ddb545e-9913-4e63-9012-965c11ed4f60" value="false" />
|
||||||
|
</map>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
# SAIpp - Barcode Scanner & MariaDB Connector
|
||||||
|
|
||||||
|
Android application developed in Kotlin using Jetpack Compose that scans barcodes and saves them directly to a self-hosted MariaDB database.
|
||||||
|
|
||||||
|
## 🚀 Features
|
||||||
|
|
||||||
|
- **Instant Scan**: The camera opens automatically upon application startup.
|
||||||
|
- **ML Kit Integration**: High-performance barcode detection using Google ML Kit.
|
||||||
|
- **MariaDB Connectivity**: Direct connection to a remote database via JDBC.
|
||||||
|
- **Modern UI**: Built with Jetpack Compose and Material 3.
|
||||||
|
- **Permission Handling**: Automatic runtime camera permission requests.
|
||||||
|
|
||||||
|
## 🛠️ Requirements
|
||||||
|
|
||||||
|
- Android device with API 34+ (Android 14+).
|
||||||
|
- MariaDB Server accessible from the mobile device network.
|
||||||
|
- Camera hardware.
|
||||||
|
|
||||||
|
## ⚙️ Configuration
|
||||||
|
|
||||||
|
### 1. Database Setup
|
||||||
|
Ensure your MariaDB database has a table named `scans`. You can use the following SQL:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE scans (
|
||||||
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
barcode VARCHAR(255) NOT NULL,
|
||||||
|
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. App Credentials
|
||||||
|
Update your database connection details in:
|
||||||
|
`app/src/main/java/com/example/saipp/data/MariaDbConnection.kt`
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
private const val HOST = "YOUR_SERVER_IP"
|
||||||
|
private const val DATABASE = "YOUR_DB_NAME"
|
||||||
|
private const val USER = "YOUR_USER"
|
||||||
|
private const val PASSWORD = "YOUR_PASSWORD"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Network Access
|
||||||
|
- Ensure the MariaDB port (default **3306**) is open in your firewall.
|
||||||
|
- Your MariaDB user must be allowed to connect from remote IPs:
|
||||||
|
```sql
|
||||||
|
GRANT ALL PRIVILEGES ON YOUR_DB_NAME.* TO 'YOUR_USER'@'%' IDENTIFIED BY 'YOUR_PASSWORD';
|
||||||
|
FLUSH PRIVILEGES;
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📦 Dependencies
|
||||||
|
|
||||||
|
- **CameraX**: For camera preview and frame capture.
|
||||||
|
- **ML Kit Barcode Scanning**: For processing barcodes.
|
||||||
|
- **MariaDB Java Client**: JDBC driver for database communication.
|
||||||
|
- **Coroutines**: For asynchronous database operations.
|
||||||
|
|
||||||
|
## ⚠️ Security Warning
|
||||||
|
|
||||||
|
> [!CAUTION]
|
||||||
|
> This project uses a **direct JDBC connection** from the mobile app to a remote database. This is suitable for personal projects or internal tools, but is **not recommended for production apps** exposed to the public, as it requires storing database credentials in the code and exposing database ports to the internet.
|
||||||
|
|
||||||
|
## 📄 License
|
||||||
|
This project is for educational/personal use.
|
||||||
@@ -35,6 +35,11 @@ android {
|
|||||||
buildFeatures {
|
buildFeatures {
|
||||||
compose = true
|
compose = true
|
||||||
}
|
}
|
||||||
|
packaging {
|
||||||
|
resources {
|
||||||
|
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@@ -46,6 +51,20 @@ dependencies {
|
|||||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||||
implementation(libs.androidx.core.ktx)
|
implementation(libs.androidx.core.ktx)
|
||||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||||
|
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.7.0")
|
||||||
|
|
||||||
|
// CameraX
|
||||||
|
implementation(libs.androidx.camera.core)
|
||||||
|
implementation(libs.androidx.camera.camera2)
|
||||||
|
implementation(libs.androidx.camera.lifecycle)
|
||||||
|
implementation(libs.androidx.camera.view)
|
||||||
|
|
||||||
|
// ML Kit Barcode Scanning
|
||||||
|
implementation(libs.mlkit.barcode.scanning)
|
||||||
|
|
||||||
|
// MariaDB JDBC Driver
|
||||||
|
implementation(libs.mariadb.java.client)
|
||||||
|
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||||
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
||||||
|
|||||||
@@ -2,6 +2,10 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||||
|
|||||||
@@ -1,17 +1,35 @@
|
|||||||
package com.example.saipp
|
package com.example.saipp
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.pm.PackageManager
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import com.example.saipp.data.ScannerRepository
|
||||||
|
import com.example.saipp.ui.scanner.BarcodeScannerView
|
||||||
import com.example.saipp.ui.theme.SAIppTheme
|
import com.example.saipp.ui.theme.SAIppTheme
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@@ -19,29 +37,78 @@ class MainActivity : ComponentActivity() {
|
|||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
setContent {
|
setContent {
|
||||||
SAIppTheme {
|
SAIppTheme {
|
||||||
|
MainScreen()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun MainScreen() {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val repository = remember { ScannerRepository() }
|
||||||
|
|
||||||
|
var hasCameraPermission by remember {
|
||||||
|
mutableStateOf(
|
||||||
|
ContextCompat.checkSelfPermission(
|
||||||
|
context,
|
||||||
|
Manifest.permission.CAMERA
|
||||||
|
) == PackageManager.PERMISSION_GRANTED
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val launcher = rememberLauncherForActivityResult(
|
||||||
|
contract = ActivityResultContracts.RequestPermission(),
|
||||||
|
onResult = { granted ->
|
||||||
|
hasCameraPermission = granted
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
LaunchedEffect(key1 = true) {
|
||||||
|
if (!hasCameraPermission) {
|
||||||
|
launcher.launch(Manifest.permission.CAMERA)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
||||||
Greeting(
|
Box(
|
||||||
name = "Android",
|
modifier = Modifier
|
||||||
modifier = Modifier.padding(innerPadding)
|
.fillMaxSize()
|
||||||
|
.padding(innerPadding)
|
||||||
|
) {
|
||||||
|
if (hasCameraPermission) {
|
||||||
|
var isProcessing by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
BarcodeScannerView(onBarcodeDetected = { barcode ->
|
||||||
|
if (!isProcessing) {
|
||||||
|
isProcessing = true
|
||||||
|
scope.launch {
|
||||||
|
val result = repository.saveBarcode(barcode)
|
||||||
|
result.onSuccess {
|
||||||
|
Toast.makeText(context, "Guardado: $barcode", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
result.onFailure { e ->
|
||||||
|
Toast.makeText(context, "Error: ${e.message}", Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
// Add a small delay to avoid multiple triggers for the same barcode
|
||||||
|
kotlinx.coroutines.delay(2000)
|
||||||
|
isProcessing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (isProcessing) {
|
||||||
|
CircularProgressIndicator(
|
||||||
|
modifier = Modifier.align(Alignment.Center)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun Greeting(name: String, modifier: Modifier = Modifier) {
|
|
||||||
Text(
|
Text(
|
||||||
text = "Hello $name!",
|
text = "Se requiere permiso de cámara para escanear.",
|
||||||
modifier = modifier
|
modifier = Modifier.align(Alignment.Center)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@Preview(showBackground = true)
|
|
||||||
@Composable
|
|
||||||
fun GreetingPreview() {
|
|
||||||
SAIppTheme {
|
|
||||||
Greeting("Android")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package com.example.saipp.data
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import java.sql.Connection
|
||||||
|
import java.sql.DriverManager
|
||||||
|
import java.sql.SQLException
|
||||||
|
import java.util.Properties
|
||||||
|
|
||||||
|
object MariaDbConnection {
|
||||||
|
private const val TAG = "MariaDbConnection"
|
||||||
|
|
||||||
|
// TODO: Replace with your actual database details
|
||||||
|
private const val HOST = "YOUR_HOST_IP"
|
||||||
|
private const val PORT = "3306"
|
||||||
|
private const val DATABASE = "YOUR_DATABASE_NAME"
|
||||||
|
private const val USER = "YOUR_USER"
|
||||||
|
private const val PASSWORD = "YOUR_PASSWORD"
|
||||||
|
|
||||||
|
private const val URL = "jdbc:mariadb://$HOST:$PORT/$DATABASE"
|
||||||
|
|
||||||
|
fun getConnection(): Connection? {
|
||||||
|
return try {
|
||||||
|
// Load the driver explicitly if needed
|
||||||
|
Class.forName("org.mariadb.jdbc.Driver")
|
||||||
|
|
||||||
|
val props = Properties()
|
||||||
|
props.setProperty("user", USER)
|
||||||
|
props.setProperty("password", PASSWORD)
|
||||||
|
props.setProperty("connectTimeout", "5000") // 5 seconds timeout
|
||||||
|
|
||||||
|
DriverManager.getConnection(URL, props)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Error connecting to MariaDB: ${e.message}", e)
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
package com.example.saipp.data
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.sql.SQLException
|
||||||
|
|
||||||
|
class ScannerRepository {
|
||||||
|
companion object {
|
||||||
|
private const val TAG = "ScannerRepository"
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun saveBarcode(barcode: String): Result<Unit> = withContext(Dispatchers.IO) {
|
||||||
|
val connection = MariaDbConnection.getConnection()
|
||||||
|
if (connection == null) {
|
||||||
|
return@withContext Result.failure(Exception("Failed to connect to database"))
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// TODO: Customize table and column names
|
||||||
|
val sql = "INSERT INTO scans (barcode, timestamp) VALUES (?, CURRENT_TIMESTAMP)"
|
||||||
|
val statement = connection.prepareStatement(sql)
|
||||||
|
statement.setString(1, barcode)
|
||||||
|
|
||||||
|
val rowsInserted = statement.executeUpdate()
|
||||||
|
if (rowsInserted > 0) {
|
||||||
|
Log.d(TAG, "Barcode saved successfully: $barcode")
|
||||||
|
Result.success(Unit)
|
||||||
|
} else {
|
||||||
|
Result.failure(Exception("No rows inserted"))
|
||||||
|
}
|
||||||
|
} catch (e: SQLException) {
|
||||||
|
Log.e(TAG, "Database error: ${e.message}", e)
|
||||||
|
Result.failure(e)
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
connection.close()
|
||||||
|
} catch (e: SQLException) {
|
||||||
|
Log.e(TAG, "Error closing connection: ${e.message}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
package com.example.saipp.ui.scanner
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.annotation.OptIn
|
||||||
|
import androidx.camera.core.CameraSelector
|
||||||
|
import androidx.camera.core.ExperimentalGetImage
|
||||||
|
import androidx.camera.core.ImageAnalysis
|
||||||
|
import androidx.camera.core.Preview
|
||||||
|
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||||
|
import androidx.camera.view.PreviewView
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import com.google.mlkit.vision.barcode.BarcodeScanning
|
||||||
|
import com.google.mlkit.vision.common.InputImage
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
|
@OptIn(ExperimentalGetImage::class)
|
||||||
|
@Composable
|
||||||
|
fun BarcodeScannerView(
|
||||||
|
onBarcodeDetected: (String) -> Unit
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val lifecycleOwner = LocalLifecycleOwner.current
|
||||||
|
val cameraExecutor = remember { Executors.newSingleThreadExecutor() }
|
||||||
|
val barcodeScanner = remember { BarcodeScanning.getClient() }
|
||||||
|
|
||||||
|
AndroidView(
|
||||||
|
factory = { ctx ->
|
||||||
|
PreviewView(ctx).apply {
|
||||||
|
scaleType = PreviewView.ScaleType.FILL_CENTER
|
||||||
|
layoutParams = ViewGroup.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
|
ViewGroup.LayoutParams.MATCH_PARENT
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
update = { previewView ->
|
||||||
|
val cameraProviderFuture = ProcessCameraProvider.getInstance(context)
|
||||||
|
|
||||||
|
cameraProviderFuture.addListener({
|
||||||
|
val cameraProvider = cameraProviderFuture.get()
|
||||||
|
|
||||||
|
val preview = Preview.Builder().build().also {
|
||||||
|
it.setSurfaceProvider(previewView.surfaceProvider)
|
||||||
|
}
|
||||||
|
|
||||||
|
val imageAnalysis = ImageAnalysis.Builder()
|
||||||
|
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
|
||||||
|
.build()
|
||||||
|
.also { analysis ->
|
||||||
|
analysis.setAnalyzer(cameraExecutor) { imageProxy ->
|
||||||
|
val mediaImage = imageProxy.image
|
||||||
|
if (mediaImage != null) {
|
||||||
|
val image = InputImage.fromMediaImage(
|
||||||
|
mediaImage,
|
||||||
|
imageProxy.imageInfo.rotationDegrees
|
||||||
|
)
|
||||||
|
|
||||||
|
barcodeScanner.process(image)
|
||||||
|
.addOnSuccessListener { barcodes ->
|
||||||
|
for (barcode in barcodes) {
|
||||||
|
barcode.rawValue?.let { value ->
|
||||||
|
onBarcodeDetected(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.addOnFailureListener { e ->
|
||||||
|
Log.e("BarcodeScanner", "Scanning failed", e)
|
||||||
|
}
|
||||||
|
.addOnCompleteListener {
|
||||||
|
imageProxy.close()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
imageProxy.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
|
||||||
|
|
||||||
|
try {
|
||||||
|
cameraProvider.unbindAll()
|
||||||
|
cameraProvider.bindToLifecycle(
|
||||||
|
lifecycleOwner,
|
||||||
|
cameraSelector,
|
||||||
|
preview,
|
||||||
|
imageAnalysis
|
||||||
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("BarcodeScanner", "Use case binding failed", e)
|
||||||
|
}
|
||||||
|
}, ContextCompat.getMainExecutor(context))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -8,6 +8,9 @@ lifecycleRuntimeKtx = "2.6.1"
|
|||||||
activityCompose = "1.8.0"
|
activityCompose = "1.8.0"
|
||||||
kotlin = "2.2.10"
|
kotlin = "2.2.10"
|
||||||
composeBom = "2026.02.01"
|
composeBom = "2026.02.01"
|
||||||
|
cameraX = "1.6.1"
|
||||||
|
mlkitBarcode = "17.3.0"
|
||||||
|
mariadbDriver = "3.5.1"
|
||||||
|
|
||||||
[libraries]
|
[libraries]
|
||||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||||
@@ -24,6 +27,12 @@ androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "u
|
|||||||
androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
||||||
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||||
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||||
|
androidx-camera-core = { group = "androidx.camera", name = "camera-core", version.ref = "cameraX" }
|
||||||
|
androidx-camera-camera2 = { group = "androidx.camera", name = "camera-camera2", version.ref = "cameraX" }
|
||||||
|
androidx-camera-lifecycle = { group = "androidx.camera", name = "camera-lifecycle", version.ref = "cameraX" }
|
||||||
|
androidx-camera-view = { group = "androidx.camera", name = "camera-view", version.ref = "cameraX" }
|
||||||
|
mlkit-barcode-scanning = { group = "com.google.mlkit", name = "barcode-scanning", version.ref = "mlkitBarcode" }
|
||||||
|
mariadb-java-client = { group = "org.mariadb.jdbc", name = "mariadb-java-client", version.ref = "mariadbDriver" }
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||||
|
|||||||
Reference in New Issue
Block a user