---
# System prepended metadata

title: 'Cámara, galería de imágenes y reconocer texto'
tags: [kotlin]

---

# Cámara, galería de imágenes y reconocer texto

## Dependencias

Usaremos el coil y el mlkit para text-recognition por lo que en build.gradle.kts(app) debemos añadir:
```
 implementation("io.coil-kt:coil-compose:2.3.0")
    implementation("com.google.android.gms:play-services-mlkit-text-recognition:19.0.0")
```

Y usaremos mlkit para reconocer los caracteres en las imágenes. Investiga sobre el módulo: 
https://developers.google.com/ml-kit?hl=es-419
Puedes incluso hacer un codela, eso sí, en java:
https://codelabs.developers.google.com/codelabs/mlkit-android#0

También necesitamos ir al manifest y añadir el permiso para usar la cámara:
```
  <uses-permission android:name="android.permission.CAMERA"/>
```

Dará un error...arréglalo!

```
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
}

android {
    namespace = "com.example.recognitionapp"
    compileSdk = 33

    defaultConfig {
        applicationId = "com.example.recognitionapp"
        minSdk = 24
        targetSdk = 33
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
        vectorDrawables {
            useSupportLibrary = true
        }
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }
    buildFeatures {
        compose = true
    }
    composeOptions {
        kotlinCompilerExtensionVersion = "1.4.3"
    }
    packaging {
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
        }
    }
}

dependencies {

    implementation("androidx.core:core-ktx:1.9.0")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
    implementation("androidx.activity:activity-compose:1.7.2")
    implementation(platform("androidx.compose:compose-bom:2023.03.00"))
    implementation("androidx.compose.ui:ui")
    implementation("androidx.compose.ui:ui-graphics")
    implementation("androidx.compose.ui:ui-tooling-preview")
    implementation("androidx.compose.material3:material3")
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.1.5")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
    androidTestImplementation(platform("androidx.compose:compose-bom:2023.03.00"))
    androidTestImplementation("androidx.compose.ui:ui-test-junit4")
    debugImplementation("androidx.compose.ui:ui-tooling")
    debugImplementation("androidx.compose.ui:ui-test-manifest")

    implementation("io.coil-kt:coil-compose:2.3.0")
    implementation("com.google.android.gms:play-services-mlkit-text-recognition:19.0.0")


}
```

## Provider
Necesitamos en el manifest además incluir la figura del proveedor o provider que es lo que genera una conexión de nuestra aplicación con los elementos del dispositivo, en este caso, la cámara.

Dentro de application debemos incluir lo que sigue:
```
 <provider
            android:authorities="${applicationId}.provider"
            android:name="androidx.core.content.FileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/path_provider"
                />


        </provider>
```

Nos dará error porque debemos crear el @xml/path_provider, sigue las instrucciones para la creación automática y llegaremos a esta pantalla: 
![image](https://hackmd.io/_uploads/Sk9Hspvtp.png)

Como ves es el generador de vistas XML pero vamos a escribir sólo el código:
```
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-cache-path name="my_images" path="/" />
</paths>
```
Este path nos va a permitir la caché de las imágenes que generemos con la cámara. 

## TabsView

Vamos a usar un nuevo tipo de navegación por tabs, así que creamos los típicos package: views y viewModel.
En viewModel creamos la clase ScannerViewModel y la dejamos vacía por ahora. 

Empezamos con las Views, vamos a crearlas vacías sólo para ver cómo funciona el tab.

Creamos los siguientes files: GalleryView, CameraView, CollectionGalleryView y TabsView, en este último crearemos los tabs pero en los anteriores para simplemente ver cómo funciona pondremos el código siguiente, sustituyendo nombres por los correspondientes:
```
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import com.anluisa.recognitionapp1.viewModel.ScannerViewModel

@Composable
fun GalleryView(viewModel:ScannerViewModel){
    Text(text = "GalleryView")
}
```
En el TabsView programamos la navegación: 
```
@Composable
fun TabsView(viewModel: ScannerViewModel){
    var selectedTab by remember { mutableStateOf(0) }
    val tabs = listOf("Galeria","Camara","Coleccion")

    Column {
        TabRow(selectedTabIndex = selectedTab,
            contentColor = Color.Black,
            indicator = { tabPositions ->
                TabRowDefaults.Indicator(
                    Modifier.tabIndicatorOffset(tabPositions[selectedTab])
                )
            }
            ) {
            tabs.forEachIndexed { index, title ->
                Tab(selected = selectedTab == index,
                    onClick = { selectedTab = index },
                    text = { Text(text = title) }
                    )
            }
        }
        when(selectedTab){
            0 -> GalleryView(viewModel)
            1 -> CameraView(viewModel)
            2 -> CollectionGalleryView()
        }
    }

}
```
Modificamos el MainActivity para que podamos ver este nuevo componente. Intenta hacerlo por ti mismo. 

## Galería
Vamos a trabajar sobre la galería, para ello lo primero es añadir un recurso drawable, recuerda como llegar a esta pantalla: 

![image](https://hackmd.io/_uploads/SyZWHJdKa.png)

Este sería el código:
```

@Composable
fun GalleryView(viewModel: ScannerViewModel) {
    val context = LocalContext.current
    val clipboard = LocalClipboardManager.current

    var image: Any? by remember { mutableStateOf(R.drawable.gallery) }

    val photoPicker = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.PickVisualMedia()
    ){
        if (it != null){
            image = it

        }else{
           //Mostrará algo si hay errores
        }
    }

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Image(
            modifier = Modifier
                .clickable {
                    photoPicker.launch(
                        PickVisualMediaRequest(
                            ActivityResultContracts.PickVisualMedia.ImageOnly
                        )
                    )
                }
                .padding(16.dp, 8.dp),
            painter = rememberAsyncImagePainter(image),
            contentDescription = null
        )

      
    }

}
```
Ahora mismo deberíamos poder ejecutar la aplicación y abrir la Galería. 

## TextRecognizer viewModel

El código es bastante sencillo, investiga cómo funciona: 
```
class ScannerViewModel:ViewModel() {

    var recognizedText by mutableStateOf("")
        private set

    fun cleanText(){
        recognizedText = ""
    }

    fun onRecognizedText(text: Any?, context: Context){
        var image: InputImage? = null
        try {
            image = InputImage.fromFilePath(context, text as Uri)
        } catch (e: IOException){
            e.printStackTrace()
        }

        image?.let {
            TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS).process(it)
                .addOnSuccessListener { text ->
                    recognizedText = text.text
                }.addOnFailureListener{
                    showToast(context, "Error al leer la imagen")
                }
        }
    }

    fun showToast(context: Context, message : String){
        Toast.makeText(context, message, Toast.LENGTH_LONG).show()
    }

}
```
## Tomar texto de la imagen

Para poder coger el texto de la imagen debemos modificar el GalleryView como sigue:

```
@Composable
fun GalleryView(viewModel: ScannerViewModel) {
    val context = LocalContext.current
    val clipboard = LocalClipboardManager.current

    var image: Any? by remember { mutableStateOf(R.drawable.gallery) }

    val photoPicker = rememberLauncherForActivityResult(
        contract = ActivityResultContracts.PickVisualMedia()
    ){
        if (it != null){
            image = it
            viewModel.onRecognizedText(image, context)
        }else{
            viewModel.showToast(context,"No se ha seleccionado ninguna imagen")
        }
    }

    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Image(
            modifier = Modifier
                .clickable {
                    photoPicker.launch(
                        PickVisualMediaRequest(
                            ActivityResultContracts.PickVisualMedia.ImageOnly
                        )
                    )
                }
                .padding(16.dp, 8.dp),
            painter = rememberAsyncImagePainter(image),
            contentDescription = null
        )

        Spacer(modifier = Modifier.height(25.dp))

        val scrollState = rememberScrollState()
        Text(text = viewModel.recognizedText,
            textAlign = TextAlign.Center,
            modifier = Modifier.fillMaxWidth()
                .verticalScroll(scrollState)
                .clickable {
                    clipboard.setText(AnnotatedString(viewModel.recognizedText))
                    viewModel.showToast(context,"Copiado")
                }
            )

    }

}


```
