====== Android Multimèdia ======
Per multimèdia s'entenen els diferents mitjans que permeten a la persona humana percebre l'entorn. Els més destacats son la visió i la oïda, però també podrien incloure's el tacte, el gust i l'olfacte. I també la orientació, sabem si estem cap per avall mercès als canals semicirculars de la oïda (no és ni una brúixola ni un GPS però algo és algo).
{{ android:sunset-camera-musica.png?450 }}
Tot el relacionat amb sensors és susceptible d'entrar en aquesta categoria, però per raons d'extensió ens centrarem en:
* Imatge i video: captura d'imatges i vídeo, tractament i visualització
* Àudio: gravació, reproducció, TTS (Text to Speech)
* Sensors: acceleròmetre, giròscop (brúixola 3D)
Apunts de referència:
* [[Android]]
{{tag> #Dam #DamMp08 #DamMp08Uf2 #DamMp08Uf02 Android mobile java }}
\\
===== Seguretat i permisos =====
{{ android:android-permisos.png?120}}
Els sistemes operatius cada cop es tornen més exigents amb la seguretat, i per tant més restrictius. Això fa que totes les operacions tinguin força passes prèvies abans de poder-se realitzar, per impedir que aplicacions malicioses vulnerin els nostres drets de privacitat de les dades.
Podeu llegir més sobre el [[https://ioc.xtec.cat/materials/FP/Recursos/ActivityUtil/documents_fp_docencia_activitats_cib_m03_cib_m03_u4/documents_fp_docencia_activitats_cib_m03_cib_m03_u4_cib_m03_u4l1_oa2/index.html|model de seguretat dels dispostius mòbils]].
Els accessos als arxius compartits requereixen diversos nivells de permisos:
* **Permisos estàtics** (sistema antic) a l'''AndroidManifest.xml'' que declaren abans d'instal·lar els recursos del dispositiu als que es voldrà accedir.
* **Permisos dinàmics** (veure foto) son més granulars i permeten que provis l'aplicació fins que arriba el moment d'accedir a les dades compartides. Es va implementar des d'Android 6 Marshmallow (API 23), i s'ha reforçat a partir d'Android 13 (API 33).
\\
===== Activity for result =====
Moltes gestions en la nostra app es poden **delegar en una aplicació externa**, com accedir a la galeria multimèdia, o permetre que l'aplicació de fotos del sistema operatiu faci la foto per nosaltres.
Aquesta delegació es fa amb un ''Intent'', similarment a quan canviem d'''Activity''.
{{ android-camera-intent.jpg?400 }}
Inicialment aquesta operació era més senzilla i implicava una simple crida ''startActivityForResult()'' i una //callback// ''onActivityResult()'', però amb el reforçament de les directives de seguretat s'ha passat a emprar la anomenada [[https://developer.android.com/training/basics/intents/result|Activity Result API]] que afegeix algunes sofisticacions (i complicacions) més.
\\
===== Exercicis =====
Necessitarem algunes fotos dins el dispositiu de l'emulador quan treballem amb Android Studio. Ves a l'aplicació de fotos i fes algunes perquè el carret de la càmera tingui alguns exemples.
Per simular la captura de fotos podem **ajustar les càmeres frontal i interna de l'emulador** amb diverses opcions:
* **Virtual Scene**: escena 3D (l'habitació amb el gat, el gos, la TV i la cuina). Ets pots moure dins l'estança i apuntar en diferents llocs.
* **Emulated**: un dibuix ultrapixelat molt bàsic.
* **Webcam**: enllaç a la càmera real del dispositiu (si estàs en un PC, la webcam).
\\
**Accés a galeria multimèdia**
Mireu el [[https://developer.android.com/training/basics/intents/result#launch|codi "Launch an Activity for result" de la documentació oficial d'Android]]. En realitat és un codi molt curt, tot i que conté implícit el ''Intent'' i d'altres recursos.
Caldrà afegir l'acció de la //callback// on diu "Handle the returned Uri", i aplicar la imatge que ens arriba en forma de Uri al ''ImageView'' que tinguem.
{{android:android-gallery0.png?100}}
{{android:android-gallery1.png?100}}
**Captura de imatge en miniatura amb la càmera (versió //thumbnail//)**
El mateix mecanisme que heu emprat per demanar la càrrega d'imatge des de la galeria multimèdia es pot aplicar per a capturar imatges en versió //thumbnail//.
Enlloc del contracte ''ActivityResultContracts.GetContent()'' que hem emprat, investiga com aplicar ''ActivityResultContracts.TakePicturePreview()''.
Afegeix un botó a l'aplicació anterior amb el què capturis el //thumbnail// directament de l'aplicació càmera del sistema operatiu.
\\
===== Foto full-size i FileProvider : donant accés a l'espai privat =====
Demanar una miniatura (//thumbnail//) a una altra ''Activity'' és senzill perquè no son gaires dades que s'han de passar d'una a l'altra a través del ''Intent''. En canvi, demanar que facin una foto d'alta resolució o un vídeo ja és més complicat, ja que el ''Intent'' no permet transferir tanta informació.
Un mètode força segur per a fer la foto en alta resolució és obrir els permisos temporalment d'un arxiu privat de l'aplicació que demana la foto perquè l'App Càmera externa la pugui escriure. Això es fa a través del [[https://developer.android.com/reference/androidx/core/content/FileProvider|FileProvider]].
{{ android:external-camera-app.png }}
Per a realitzar la foto //full-size// i guardar-la a l'espai privat caldrà:
- Afegir un arxiu ''res/xml/file_paths.xml'' on s'indiquen carpetes i arxius compartibles.
- Declarar el ''FileProvider'' al ''AndroidManifest.xml''.
- Al codi de l'''Activity'' transformar el ''File'' estàndard a ''Uri'' (recurs compartible) amb el ''FileProvider''.
- Engegar l'App Camera amb la ''Activity Result API'' (similar al fet fins ara per la galeria i el //thumbnail//).
==== Arxius XML ====
==== Codi MainActivity.kt ====
Preparació del recurs compartit i inici de l'App Camera:
val photoButton = findViewById
I la //callback// implementada amb la Results Activity API:
val takeFullPic = registerForActivityResult(
ActivityResultContracts.TakePicture()) { success ->
if (success) {
// netejar imatge i pintar nova
imageView.setImageDrawable(null)
imageView.setImageURI(fotoUri)
} else {
// notifiquem error
Toast.makeText(this, "Error al guardar la foto", Toast.LENGTH_SHORT).show()
}
}
\\
===== Àrea privada i àrea compartida =====
L'exemple anterior s'ha fet compartint un arxiu a la carpeta "fotos" de l'**espai privat de l'aplicació**, que està a ''/data/data/{AppId}/files''.
L'**àrea compartida** d'arxius està a ''/storage/emulated/0'' on trobarem carpetes conegudes com:
* Pictures
* Music
* Movies
* **Android/data/{AppId}** : aquesta és l'**àrea compartida d'aplicació** (Android 10+)
On ''{AppId}'' és l'identificador d'aplicació corresponent al //package//, per exemple ''com.ieti.multimedia''
Els directoris on s'emmagatzema la info son diferents:
^ ^ Funció d'accés ^ Directori ^ XML ^
| Arxius privats de l'app | filesDir | /data/data/{AppId}/files | files-path |
| Arxius compartits de l'app | getExternalFilesDir( null ) | /storage/emulated/0/Android/data/{AppId}/files | external-files-path |
| Arxius compartits de fotos | Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES ) | /storage/emulated/0/Pictures/ | external-path |
\\
==== Àrea compartida d'aplicació ====
Si volem fer servir l'àrea compartida de l'aplicació, caldrà fer algunes modificacions.
Al ''file_paths.xml'' caldrà emprar ''external-files-path'' enlloc de ''files-path'':
A ''MainActivity.kt'' caldrà emprar ''getExternalFilesDir(...)'' enlloc de ''filesDir''. Amb el paràmetre //null// ens escriurà en la carpeta compartida d'aplicacions.
val carpeta = File(getExternalFilesDir(null).toString(),"fotos")
...
val file = File(getExternalFilesDir(null).toString(),"fotos/tmpimage.jpg")
Utilitzeu el **Device Explorer** del Android Studio per visualitzar les carpetes i arxius.
\\
==== Carpetes compartides generals públiques ====
Si volem fer servir l'àrea compartida de l'aplicació, caldrà fer algunes modificacions.
Al ''file_paths.xml'' caldrà emprar ''external-path'' enlloc de ''files-path'' o ''external-files-path'' que hem emprat anteriorment:
A ''MainActivity.kt'' caldrà emprar ''Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES))'' :
val carpeta = File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES).toString(),"fotos")
...
val file = File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES).toString(),"fotos/tmpimage.jpg")
\\
===== Exercicis =====
**Captura de fotos //full-size//**
Implementa la captura de fotos //full-size// en l'app de la galeria i el thumb, afegint un nou botó per la captura actual.
**Exercici MyGallery**
Implementa una app de captura de fotos i conserva tots els arxius amb un nom que eviti col·lisions, com afegir un //timestamp//.
Visualització les fotos que anem prenent en l'àrea compartida de l'aplicació i visualitza-les en una nova ''Activity''. Segueix les instruccions a l'article [[Android RecyclerView]] per visualitzar les fotos amb una //preview// en format //grid//.
\\
===== Mes coses =====
[[Android Galeria]] per accedir als arxius multimèdia a través de l'''Activity Result API''.
[[Android Camera]] per utilitzar la càmera, fer fotos i enregistrar-les. Cal tenir en compte que hi ha 2 maneres principals de tractar aquest tema:
* Delegació de la captura de la foto en l'app càmera del sistema operatiu.
* Captura directa de la imatge a la nostra app amb la llibreria CameraX.
[[Android Sensors]] per utilitzar l'acceleròmetre.
[[Android Speech]] per realitzar síntesi i reconeixement de veu.