Añadida la clase conexion y la apertura del lector de codigos.
This commit is contained in:
@@ -35,6 +35,11 @@ android {
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@@ -46,6 +51,20 @@ dependencies {
|
||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||
implementation(libs.androidx.core.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)
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
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
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
|
||||
@@ -1,17 +1,35 @@
|
||||
package com.example.saipp
|
||||
|
||||
import android.Manifest
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.compose.setContent
|
||||
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.padding
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
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.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 kotlinx.coroutines.launch
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -19,29 +37,78 @@ class MainActivity : ComponentActivity() {
|
||||
enableEdgeToEdge()
|
||||
setContent {
|
||||
SAIppTheme {
|
||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
||||
Greeting(
|
||||
name = "Android",
|
||||
modifier = Modifier.padding(innerPadding)
|
||||
)
|
||||
}
|
||||
MainScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Greeting(name: String, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
text = "Hello $name!",
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun GreetingPreview() {
|
||||
SAIppTheme {
|
||||
Greeting("Android")
|
||||
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 ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.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 {
|
||||
Text(
|
||||
text = "Se requiere permiso de cámara para escanear.",
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user