bytes.cat

La wiki d'FP d'informàtica

Eines de l'usuari

Eines del lloc


Barra lateral

ASIX Administració de Sistemes Informàtics i Xarxes
Tots els mòduls del cicle
MP01 Implantació de sistemes operatius
Totes les UFs del modul
MP02 Gestió de bases de dades
Totes les UFs del modul
MP03 Programació bàsica
Totes les UFs del modul
MP04 Llenguatges de marques i sistemes de gestió d'informació
Totes les UFs del modul
MP05 Fonaments de maquinari
Totes les UFs del modul
MP06 Administració de sistemes operatius
Totes les UFs del modul
MP07 Planificació i administració de xarxes
Totes les UFs del modul
MP08 Serveis de xarxa i Internet
Totes les UFs del modul
MP09 Implantació d'aplicacions web
Totes les UFs del modul
MP10 Administració de sistemes gestors de bases de dades
Totes les UFs del modul
MP11 Seguretat i alta disponibilitat
Totes les UFs del modul
MP12 Formació i orientació laboral
Totes les UFs del modul
MP13 Empresa i iniciativa emprenedora
Totes les UFs del modul
MP14 Projecte
Totes les UFs del modul
DAM Desenvolupament d’aplicacions multiplataforma
Tots els mòduls del cicle
MP01 Sistemes informàtics
Totes les UFs del modul
MP02 Bases de dades
Totes les UFs del modul
MP03 Programació bàsica
Totes les UFs del modul
MP04 Llenguatges de marques i sistemes de gestió d'informació
Totes les UFs del modul
MP05 Entorns de desenvolupament
Totes les UFs del modul
MP06 Accés a dades
Totes les UFs del modul
MP07 Desenvolupament d’interfícies
Totes les UFs del modul
MP08 Programació multimèdia i dispositius mòbils
Totes les UFs del modul
MP09 Programació de serveis i processos
Totes les UFs del modul
MP10 Sistemes de gestió empresarial
Totes les UFs del modul
MP11 Formació i orientació laboral
Totes les UFs del modul
MP12 Empresa i iniciativa emprenedora
Totes les UFs del modul
MP13 Projecte de síntesi
Totes les UFs del modul
MPDual Mòdul Dual / Projecte
DAW Desenvolupament d’aplicacions web
Tots els mòduls del cicle
MP01 Sistemes informàtics
Totes les UFs del modul
MP02 Bases de dades
Totes les UFs del modul
MP03 Programació
Totes les UFs del modul
MP04 Llenguatge de marques i sistemes de gestió d’informació
Totes les UFs del modul
MP05 Entorns de desenvolupament
Totes les UFs del modul
MP06 Desenvolupament web en entorn client
Totes les UFs del modul
MP07 Desenvolupament web en entorn servidor
Totes les UFs del modul
MP08 Desplegament d'aplicacions web
Totes les UFs del modul
MP09 Disseny d'interfícies web
Totes les UFs del modul
MP10 Formació i Orientació Laboral
Totes les UFs del modul
MP11 Empresa i iniciativa emprenedora
Totes les UFs del modul
MP12 Projecte de síntesi
Totes les UFs del modul
SMX Sistemes Microinformàtics i Xarxes
Tots els mòduls del cicle
MP01 Muntatge i manteniment d’equips
Totes les UFs del modul
MP02 Sistemes Operatius Monolloc
Totes les UFs del modul
MP03 Aplicacions ofimàtiques
Totes les UFs del modul
MP04 Sistemes operatius en xarxa
Totes les UFs del modul
MP05 Xarxes locals
Totes les UFs del modul
MP06 Seguretat informàtica
Totes les UFs del modul
MP07 Serveis de xarxa
Totes les UFs del modul
MP08 Aplicacions Web
Totes les UFs del modul
MP09 Formació i Orientació Laboral
Totes les UFs del modul
MP10 Empresa i iniciativa emprenedora
Totes les UFs del modul
MP11 Anglès
Totes les UFs del modul
MP12 Síntesi
Totes les UFs del modul
CETI Ciberseguretat en Entorns de les Tecnologies de la Informació
Tots els mòduls del cicle
CiberOT Ciberseguretat en Entorns d'Operació
Tots els mòduls del cicle
android_camera

Ús de la càmera amb Android

En aquest article veurem com fer ús del hardware de la càmera per a capturar imatges i vídeos. També tractarem sobre l'emmagatzematge dels arxius multimèdia.

android-camera.jpg

Referències:


Estratègies per a captura d'imatges

Tenim 2 estratègies bàsiques en el què pertoca a l'adquisició d'imatges amb la càmera:

  1. Cridar la app Camera externa del propi sistema operatiu. És la forma més senzilla de treballar. Podria arribar a passar que el SO no tingui cap app Camera instal·lada, però sol ser poc habitual. Emprarem un Intent per cridar a l'altra app, la qual ens retornarà la foto com a dades (thumbnail) en el retorn del Intent, o en un arxiu que cal habilitar prèviament abans de cridar-la.android-camera-intent.jpg
    1. Versió amb thumbnail (miniatura): si no adjuntem cap File al Intent, la App Camera ens retornarà un thumbnail que podem assignar a una ImageView.
    2. Versió full-size: per poder disposar de la foto a tamany complert cal donar permís a la App Camera amb un FileProvider.
  2. Implementar a la nostra pròpia app la visualització de la càmera i la presa de dades. A nivell de llibreries i codi és més complicat de gestionar. Per la previsualització necessitarem disposar d'una SurfaceView o una PreviewView que podrà monitoritzar el què està captant la càmera i habilitar un botó per a realitzar la captura des del nostre codi. Per aquest mecanisme disposem de diverses llibreries:


Utilitzant la App Camera externa

Podem emprar el tutorial Photo Basics oficial d'Android. A l'inici diu que està deprecated però en realitat encara serveix. Indica que a l'article és obsolet perquè s'utilitza la class Camera (obsoleta) però no s'utilitza pas en tot el tutorial. Utilitzem la app Camera externa del sistema operatiu, que no té res a veure amb la class Camera.

El què sí que està obsolet del tutorial és com llençar una altra app externa per rebre dades. Inicialment es feia amb el mètode Activity.startActivityForResult (complementant-se amb la callback onActivityResult). Ara s'ha de fer amb Activity Results API.

Aquest tutorial per a crida d'una app externa ens clarifica com utilitzar adequadament la Activity Results API amb un Intent que crida, en aquest cas, la galeria d'imatges, i rep una foto.

De fet, l'exemple del tutorial s'assembla més al darrer exemple de l'Activity Results API, el subtítol diu «Create a custom contract» però acte seguit parla del genèric StartActivityForResult contract que és el que serveix per al cas genèric i que s'empra en el tutorial. Li falten alguns avantatges dels nous contractes que milloren algunes situacions, però és més fàcil de treballar.

Comencem accedint a la Galeria d'imatges

Exercici galeria d'imatges

Implementa el tutorial d'accés a la Galeria d'Imatges per veure com ha canviat la manera d'invocar una app externa (en aquest cas la Gallery).

També hi ha una solució indicada al primer exemple de l'Activity Results API però és lleugerament diferent i pot confondre. Millor utilitzem l'exemple del tutorial.

Tant si seguim el tutorial com l'article de l'Activity Results API, caldrà que afegeixis al layout un Button per disparar la visualització de la galeria, i un ImageView per visualitzar la foto que ens tornin.

Si utilitzes el tutorial com a referència, pensa que:

  1. Només cal que posis el codi de la MainActivity, no cal el AddImageDialog.
  2. La línia
    ImageView imageView = R.id.img;

    en realitat ha de ser:

    ImageView imageView = findViewById(R.id.imageView);
  3. L'objecte ActivityResultLauncher<Intent> someActivityResultLauncher peta si el deixem dins la funció i el cridem des d'un botó amb un OnClickListener. Una solució és posar-lo com a atribut de la classe i crear-lo en el onCreate de la MainActivity.

Al final ha de quedar més o menys així:

public class MainActivity extends AppCompatActivity {
    public static int RC_PHOTO_PICKER = 0;
    ActivityResultLauncher<Intent> someActivityResultLauncher;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // ...mes codi de inicialització...
 
        someActivityResultLauncher = registerForActivityResult(
            // ...tota la carretada per crear la callback
            // i pintar la foto a l'ImageView...
        });
 
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            // ...creem i cridem l'Intent per engegar l'app Camera externa...
        });
 
    } // onCreate
}

Accedim a la càmera i rebem una miniatura o thumbnail

Exercici Take Photo thumbnail

Segueix la primera part del tutorial Photo Basics oficial. ULL: ha de ser la versió en anglès, la traducció al castellà té errors. A més, corregirem la part obsoleta i utilitzarem la Activity Result API.

  1. El codi perquè l'Intent obri la càmera és el següent, molt senzill (abans estava al tutorial, però sembla que ho han tret). Simplement es tracta de crear un Intent que cridi l'acció genèrica MediaStore.ACTION_IMAGE_CAPTURE:
    fotoButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //Create Intent
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            cameraResultLauncher.launch(intent);
        }
  2. Inicialment només captarem el thumbnail i el mostrarem a una ImageView, similarment al tutorial de la Galeria d'Imatges.
  3. Implementa la crida a la app externa amb la Activity Results API, tal i com hem fet a l'anterior exercici de la Galeria (enlloc de l'obsolet startActivityForResult).

Observa les diverses opcions per cridar les aplicacions externes de galeria, en particular:

  1. MediaStore.ACTION_PICK_IMAGES (a partir de la API 33)

Aquest fil parla sobre quina és més convenient de les 2 primeres.

En canvi, per a la captura d'imatges, la opció és (tal i com hem vist al tutorial):


Foto full-size i FileProvider : donant accés a l'espai privat

Demanar que una altra app faci la foto que nosaltres després utilitzarem té una dificultat: la seguretat d'Android priva que una app pugui veure en els arxius d'una altra. Per tant, la App Camera no podria escriure en el nostre espai intern.

Per solucionar aquest problema, existeix FileProvider. Aquest objecte publicarà temporalment un recurs en un objecte tipus Uri perquè la App Camera hi pugui escriure.

Així, els passos que cal fer son:

  1. Crear un FileProvider al AndroidManifest.xml
  2. Definir els directoris a un arxiu de Recursos (res) file_paths.xml
  3. Crear un arxiu temporal amb l'objecte File.createTempFile.
    1. Utilitza també les funcions d'emmagatzematge intern o extern (getFilesDir o getExternalFilesDir).
  4. Transforma el File en Uri amb FileProvider.getUriForFile
  5. Llançar el Intent per a la App Camera amb l'afegit perquè ens retorni la foto full-size. Hauriem de tenir una cosa similar a això al Button.onClickListener:
        // creem arxiu on volem guardar la imatge
        String filePath = getExternalFilesDir(Environment.DIRECTORY_PICTURES
                          ).toString() + "/tmpImg.jpg";
        File photoFile = new File( filePath );
        // li donem permisos amb FileProvider pq hi pugui escriure l'app camera
        // ULL: la authority ha de ser la del vostre package "com....fileprovider"
        //      igual que la que heu d'haver configurat al AndroidManifest.xml
        photoURI = FileProvider.getUriForFile(MainActivity.this,
                "com.enricmieza.gallery25.fileprovider",
                photoFile);
     
        // Create Intent
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        // Afegir arxiu amb permisos del FileProvider al Intent
        intent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
        cameraResultLauncher.launch(intent);
  6. Quan rebem el resultat a la callback cameraResultLauncher, podem utilitzar la mateixa Uri que ja hem fet servir per al FileProvider:
        @Override
        public void onActivityResult(ActivityResult result) {
            if (result.getResultCode() == AppCompatActivity.RESULT_OK) {
                // display foto
                ImageView imageView = findViewById(R.id.imageView);
                Intent data = result.getData();
                Bundle extras = data.getExtras();
                // si ve un bitmap, pinto el bitmap
                if( extras!=null ) {
                    Bitmap imageBitmap = (Bitmap) extras.get("data");
                    imageView.setImageBitmap(imageBitmap);
                    return;
                } else {
                    // si no em retorna res, agafo la photoURI
                    // que hem configurat pel FileProvider
                    if( photoURI!=null ) {
                        imageView.setImageURI(photoURI);
                        return;
                    }
                    Log.e("ERROR","No hi ha cap photoURI");
                }
            }
        }

Exercici take photo full size

Segueix la part «Take full size photo» del tutorial Photo Basics oficial.

Recorda ha de ser la versió en anglès, la traducció al castellà té errors.

App v0.3 - Afegeix un altre botó a l'app que hem fet a l'exercici previ que permeti agafar fotos full-size com s'explica al tutorial. Recorda que ara cal fer servir la Activity Results API. Millor crea un nou objecte ActivityLauncher per no embolicar el tractament post-camera.

Exercici MyGallery

Implementa la recuperació de la imatge a l'inici de l'aplicació. Si existeix ja un arxiu de foto, mostra'l al ImageView.

App v0.4 - Implementa la visualització de les fotos que anem prenent en l'àrea compartida de l'aplicació. Segueix les instruccions a l'article Android RecyclerView per visualitzar les fotos amb una preview en format grid.


Imatge de la càmera dins la nostra App

Tutorial Java (medium.com)

Els objectes SurfaceView i PreviewView (widgets que podem incorporar al nostre layout de l'Activity) ens permeten veure el què captura la càmera directament sobre la nostra App. Sembla ser, però, que la gestió d'una SurfaceView és força complicada. Si emprem CameraX, serà més fàcil la gestió amb PreviewView.

Llibreries Android disponibles:

  1. Camera: obsoleta des de la API 21. Cal emprar Camera2. Ull amb els tutorials obsolets.
  2. Camera2: la llibreria oficial actual.
  3. CameraX: una sèria d'utilitats que ens facilita (relativament) el codi. Per sota utilitza Camera2.

Exercici PreviewView amb CameraX

Farem una captura d'imatge dins la nostra pròpia App sobre una PreviewView. Després afegirem la captura d'imatge sobre un arxiu.


Primera part: in-app preview

Referència:

  • Consulteu la doc oficial de CameraX per saber quina és la versió de la llibreria que convé utilitzar.
  • Alguns hacks que cal fer, ja que el tutorial utilitza la v1.0.0 de CameraX, i les versions successives tenen alguns canvis:
    • Manifest.permission.CAMERAandroid.Manifest.permission.CAMERA
    • previewView.createSurfaceProvider()previewView.getSurfaceProvider()
    • Probablement al crear el layout des del XML no funcioni la vista prèvia en Android Studio, però la App funcionarà.


Segona part: captura d'imatge

Implementa la captura d'imatge i enregistr-la en un arxiu. En aquest cas, al treballar internament a la nostra app, no caldrà FileProvider ja que l'accés als arxius interns de la app és directe.

Referència:

Pistes:

  1. Primer caldrà afegir un Button addicional per prendre la foto, dins el layout de la mateixa CameraActivity creada al tutorial de la 1a part. Hauràs de fer-ho sobre el XML perquè la vista prèvia gràfica no funciona.
  2. Caldrà crear un objecte ImageCapture i vincular-lo adientment a l'objecte Camera. Fixeu-vos que el 2n tutorial utilitza un mètode sobrecarregat amb diferents arguments per poder introduir el ImageCapture:
    Camera camera = cameraProvider.bindToLifecycle(
         (LifecycleOwner)this, cameraSelector,
         preview, imageAnalysis, imageCapture);
  3. executor té a veure amb el MainLoop (explicat a Android Threads). Enlloc de fer servir getMainExecutor(this); que requereix la MinSdk = 28 (molt alta, baixa compatibilitat), pots fer servir ContextCompat.getMainExecutor(this); que et permetrà compatibilitat amb versions anteriors d'Android.
  4. Canvia l'accés a arxius del tutorial getBatchDirectoryName() per getFilesDir() (de moment guardem els fitxers a l'àrea privada).
  5. Visualitza la foto capturada en un ImageView de la MainActivity.


Tutorial Kotlin

Podem seguir les indicacions del tutorial oficial d'Android per a CameraX.

Aquest tutorial empra View Binding per accedir d'una forma més moderna a les views. Llegeix l'article abans.

I com no, no es pot seguir al peu de la lletra, cal adaptar algunes coses. En concret al pas 2 «Crear el projecte». Resulta que l'arxiu build.gradle ara ha passat a ser build.gradle.kt especificat en format Kotlin també, pel què hi haurà algunes modificacions que indiquem tot seguit:

  1. Punt 1: les biblioteques de codi
    dependencies {
        // ...
     
        val camerax_version = "1.4.1"
        implementation("androidx.camera:camera-core:${camerax_version}")
        implementation("androidx.camera:camera-camera2:${camerax_version}")
        implementation("androidx.camera:camera-lifecycle:${camerax_version}")
        implementation("androidx.camera:camera-video:${camerax_version}")
        implementation("androidx.camera:camera-view:${camerax_version}")
        implementation("androidx.camera:camera-extensions:${camerax_version}")
    }
  2. El punt 2 de les compileOptions no cal, podem deixar el que ve per defecte amb VERSION_11:
    compileOptions {
            sourceCompatibility = JavaVersion.VERSION_11
            targetCompatibility = JavaVersion.VERSION_11
        }
  3. El punt 3 queda així
    buildFeatures {
        viewBinding = true
    }

La resta queda igual. Cal anar amb compte de llegir bé els passos i entendre'ls.

Implementa el tutorial oficial d'Android per a CameraX. Arriba a la part 5 del takePhoto. No avancis un pas fins que no et funcioni com s'indica al final de cadascun d'ells.

Redueix la PreviewView a mitja pantalla i afegeix una ImageView. Quan es faci una captura d'imatge, visualitza la foto en la ImageView. Hauria de quedar aproximadament així:


Codis QR

Una aplicació interessant de la càmera pot ser amb generació i escaneig de codis QR.

Aquestes llibreries funcionen (a Febrer de 2023):


android_camera.txt · Darrera modificació: 2024/12/16 15:53 per enric_mieza_sanchez