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.
Referències:
Tenim 2 estratègies bàsiques en el què pertoca a l'adquisició d'imatges amb la càmera:
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.File
al Intent
, la App Camera ens retornarà un thumbnail que podem assignar a una ImageView
.FileProvider
. 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:
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 (complementant-se amb la callback Activity.startActivityForResult
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.
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:
MainActivity
, no cal el AddImageDialog
ImageView imageView = R.id.img;
en realitat ha de ser:
ImageView imageView = findViewById(R.id.imageView);
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
.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.
startActivityForResult
Observa les diverses opcions per cridar les aplicacions externes de galeria, en particular:
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):
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:
FileProvider
al AndroidManifest.xml
file_paths.xml
File.createTempFile
.getFilesDir
getExternalFilesDir
).File
en Uri
amb FileProvider.getUriForFile
Intent
per a la App Camera.onActivityResult
, la mateixa Uri
que ja teniem abans ens serveix per assignar-la a la ImageView.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.
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:
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:
Manifest.permission.CAMERA
→ android.Manifest.permission.CAMERA
previewView.createSurfaceProvider()
→ previewView.getSurfaceProvider()
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:
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.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);
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.getBatchDirectoryName()
getFilesDir()
(de moment guardem els fitxers a l'àrea privada).MainActivity
.
Una aplicació interessant de la càmera pot ser amb generació i escaneig de codis QR.
Aquestes llibreries funcionen (a Febrer de 2023):