bytes.cat

La wiki d'FP d'informàtica

Eines de l'usuari

Eines del lloc


android_camera

Diferències

Ací es mostren les diferències entre la revisió seleccionada i la versió actual de la pàgina.

Enllaç a la visualització de la comparació

Ambdós costats versió prèvia Revisió prèvia
Següent revisió
Revisió prèvia
android_camera [2023/01/23 15:39]
enric_mieza_sanchez [Imatge de la càmera dins la nostra App]
android_camera [2024/12/16 15:53] (actual)
enric_mieza_sanchez [Tutorial Kotlin]
Línia 11: Línia 11:
     * [[Android persistència]]     * [[Android persistència]]
   * [[https://ioc.xtec.cat/materials/FP/Recursos/fp_dam_m08_/web/fp_dam_m08_htmlindex/WebContent/u3/a2/continguts.html|Apunts de MP08 UF2 de DAM a l'IOC]].   * [[https://ioc.xtec.cat/materials/FP/Recursos/fp_dam_m08_/web/fp_dam_m08_htmlindex/WebContent/u3/a2/continguts.html|Apunts de MP08 UF2 de DAM a l'IOC]].
-  * [[https://developer.android.com/training/camera-deprecated/photobasics#java|Article "photo basics"]] oficial d'Android. Cal llegir-lo en anglès perquè la versió en castellà conté errors.+  * [[https://developer.android.com/training/camera-deprecated/photobasics?hl=en#java|Article "photo basics"]] oficial d'Android. Cal llegir-lo en anglès perquè la versió en castellà conté errors.
   * [[https://developer.android.com/studio/debug/device-file-explorer|Device File Explorer]] de l'Android Studio.   * [[https://developer.android.com/studio/debug/device-file-explorer|Device File Explorer]] de l'Android Studio.
  
Línia 31: Línia 31:
  
 ===== Utilitzant la App Camera externa ===== ===== Utilitzant la App Camera externa =====
-Podem emprar el [[https://developer.android.com/training/camera-deprecated/photobasics#java|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''**.+Podem emprar el [[https://developer.android.com/training/camera-deprecated/photobasics?hl=en#java|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 <del>''Activity.startActivityForResult''</del> (complementant-se amb la //callback// ''onActivityResult''). Ara s'ha de fer amb [[https://developer.android.com/training/basics/intents/result#java|Activity Results API]]. 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 <del>''Activity.startActivityForResult''</del> (complementant-se amb la //callback// ''onActivityResult''). Ara s'ha de fer amb [[https://developer.android.com/training/basics/intents/result#java|Activity Results API]].
  
 Aquest [[https://medium.com/@patelsneh18/startactvivityforresult-deprecated-alternative-and-using-it-outside-activity-class-bc9331cf896|tutorial per a crida d'una app externa]] ens clarifica com utilitzar adequadament la [[https://developer.android.com/training/basics/intents/result#java|Activity Results API]] amb un ''Intent'' que crida, en aquest cas, la galeria d'imatges, i rep una foto. Aquest [[https://medium.com/@patelsneh18/startactvivityforresult-deprecated-alternative-and-using-it-outside-activity-class-bc9331cf896|tutorial per a crida d'una app externa]] ens clarifica com utilitzar adequadament la [[https://developer.android.com/training/basics/intents/result#java|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 [[https://developer.android.com/training/basics/intents/result#custom|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 ====
  
 <WRAP todo> <WRAP todo>
 **Exercici galeria d'imatges** **Exercici galeria d'imatges**
  
-Segueix el [[https://medium.com/@patelsneh18/startactvivityforresult-deprecated-alternative-and-using-it-outside-activity-class-bc9331cf896|tutorial d'accés a la Galeria d'Imatges]].+Implementa el [[https://medium.com/@patelsneh18/startactvivityforresult-deprecated-alternative-and-using-it-outside-activity-class-bc9331cf896|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).
  
-Ens servirà per practicar amb la [[https://developer.android.com/training/basics/intents/result#java|Activity Results API]].+També hi ha una solució indicada al primer exemple de l'[[https://developer.android.com/training/basics/intents/result#java|Activity Results API]] però és lleugerament diferent i pot confondre. Millor utilitzem l'exemple del tutorial.
  
-Per implementar el tutorial exitosament:+**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**. 
 + 
 +<WRAP important> 
 +Si utilitzes el tutorial com a referència, pensa que:
   - Només cal que posis el codi de la ''MainActivity'', no cal el <del>''AddImageDialog''</del>.   - Només cal que posis el codi de la ''MainActivity'', no cal el <del>''AddImageDialog''</del>.
-  - Caldrà que afegeixis una ''Activity'' amb un botó per disparar la visualització de la galeria, i un ''ImageView'' per visualitzar la foto que ens tornin. 
   - La línia<code>ImageView imageView = R.id.img;</code> en realitat ha de ser:<code>ImageView imageView = findViewById(R.id.imageView);</code>   - La línia<code>ImageView imageView = R.id.img;</code> en realitat ha de ser:<code>ImageView imageView = findViewById(R.id.imageView);</code>
   - 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''.   - 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''.
 </WRAP> </WRAP>
 +
 +Al final ha de quedar més o menys així:
 +
 +<code java>
 +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
 +}
 +</code>
 +
 +</WRAP>
 +
 +
 +==== Accedim a la càmera i rebem una miniatura o thumbnail ====
  
 <WRAP TODO> <WRAP TODO>
 **Exercici Take Photo thumbnail** **Exercici Take Photo thumbnail**
  
-Segueix la primera part del [[https://developer.android.com/training/camera-deprecated/photobasics#java|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**.+Segueix la primera part del [[https://developer.android.com/training/camera-deprecated/photobasics?hl=en#java|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**. 
 +  - 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'':<code java> 
 +fotoButton.setOnClickListener(new View.OnClickListener() { 
 +    @Override 
 +    public void onClick(View v) { 
 +        //Create Intent 
 +        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 
 +        cameraResultLauncher.launch(intent); 
 +    }</code>
   - Inicialment només captarem el //thumbnail// i el mostrarem a una ImageView, similarment al [[https://medium.com/@patelsneh18/startactvivityforresult-deprecated-alternative-and-using-it-outside-activity-class-bc9331cf896|tutorial de la Galeria d'Imatges]].   - Inicialment només captarem el //thumbnail// i el mostrarem a una ImageView, similarment al [[https://medium.com/@patelsneh18/startactvivityforresult-deprecated-alternative-and-using-it-outside-activity-class-bc9331cf896|tutorial de la Galeria d'Imatges]].
   - Implementa la crida a la app externa amb la [[https://developer.android.com/training/basics/intents/result#java|Activity Results API]], tal i com hem fet a l'anterior exercici de la Galeria (enlloc de l'obsolet <del>''startActivityForResult''</del>).   - Implementa la crida a la app externa amb la [[https://developer.android.com/training/basics/intents/result#java|Activity Results API]], tal i com hem fet a l'anterior exercici de la Galeria (enlloc de l'obsolet <del>''startActivityForResult''</del>).
Línia 88: Línia 133:
     - Utilitza també les funcions d'emmagatzematge intern o extern (<del>''getFilesDir''</del> o ''getExternalFilesDir'').     - Utilitza també les funcions d'emmagatzematge intern o extern (<del>''getFilesDir''</del> o ''getExternalFilesDir'').
   - Transforma el ''File'' en ''Uri'' amb ''FileProvider.getUriForFile''   - Transforma el ''File'' en ''Uri'' amb ''FileProvider.getUriForFile''
-  - Llançar el ''Intent'' per a la App Camera. +  - 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'':<code java> 
-  - Quan rebem el resultat a ''onActivityResult'', la mateixa ''Uri'' que ja teniem abans ens serveix per assignar-la a la ImageView.+    // 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); 
 +</code> 
 +  - Quan rebem el resultat a la callback ''cameraResultLauncher'', podem utilitzar la mateixa ''Uri'' que ja hem fet servir per al ''FileProvider'':<code java> 
 +    @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"); 
 +            } 
 +        } 
 +    }</code>
  
 <WRAP todo> <WRAP todo>
 **Exercici take photo full size** **Exercici take photo full size**
  
-Segueix la **part "Take full size photo"** del [[https://developer.android.com/training/camera-deprecated/photobasics#TaskPath|tutorial Photo Basics]] oficial.+Segueix la **part "Take full size photo"** del [[https://developer.android.com/training/camera-deprecated/photobasics?hl=en#TaskPath|tutorial Photo Basics]] oficial.
  
 **Recorda ha de ser la versió en anglès**, la traducció al castellà té errors. **Recorda ha de ser la versió en anglès**, la traducció al castellà té errors.
  
-Adapta els fragments de codi a la nostra App amb //thumbnails// que havíem realitzat a l'exercici previ.+**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. 
 + 
 +</WRAP> 
 + 
 +<WRAP todo> 
 +**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//.
  
 </WRAP> </WRAP>
Línia 105: Línia 199:
  
 ===== Imatge de la càmera dins la nostra App ===== ===== 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''. 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''.
Línia 118: Línia 214:
  
 <WRAP todo> <WRAP todo>
-**Exercici PreviewView**+**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. 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** **Primera part: in-app preview**
  
-Utilitza aquest [[https://medium.com/swlh/introduction-to-androids-camerax-with-java-ca384c522c5|tutorial d'ús de CameraX en Java (medium.com)]]. Probablement al crear el //layout// des del XML no funcioni la preview en Android Studio, però la App funcionarà. Aquest tutorial funciona prou directament.+Referència: 
 +  * [[https://medium.com/swlh/introduction-to-androids-camerax-with-java-ca384c522c5|tutorial d'ús de CameraX en Java (medium.com)]]. 
 +  * Consulteu la [[https://developer.android.com/jetpack/androidx/releases/camera?hl=es-419#groovy|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.CAMERA'' -> ''android.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.+**Segona part: captura d'imatge**
  
-Tenim un parell de referències: +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. 
-  * [[https://developer.android.com/training/camerax/take-photo?hl=es-419|Tutorial oficial Take Photo amb ImageCapture]] + 
-  * [[https://akhilbattula.medium.com/android-camerax-java-example-aeee884f9102|Tutorial on apareix com fer-ho]] amb l'objecte ''ImageCaputure.takePicture''però cal adaptar codi que no és trivial.+Referència
 +  * [[https://akhilbattula.medium.com/android-camerax-java-example-aeee884f9102|Tutorial de captura d'imatge amb objecte ImageCapture]], caldrà adaptar codi.
  
 Pistes: Pistes:
Línia 144: Línia 249:
  
 </WRAP> </WRAP>
 +
 +\\
 +
 +==== Tutorial Kotlin ====
 +
 +Podem seguir les [[https://developer.android.com/codelabs/camerax-getting-started#0|indicacions del tutorial oficial d'Android per a CameraX]].
 +
 +Aquest tutorial empra [[https://developer.android.com/topic/libraries/view-binding|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:
 +
 +  - Punt 1: les biblioteques de codi<code kotlin>
 +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}")
 +}</code>
 +  - El punt 2 de les ''compileOptions'' no cal, podem deixar el que ve per defecte amb VERSION_11:<code kotlin>
 +compileOptions {
 +        sourceCompatibility = JavaVersion.VERSION_11
 +        targetCompatibility = JavaVersion.VERSION_11
 +    }
 +</code>
 +  - El punt 3 queda així<code kotlin>
 +buildFeatures {
 +    viewBinding = true
 +}
 +</code>
 +
 +La resta queda igual. Cal anar amb compte de llegir bé els passos i entendre'ls.
 +
 +<WRAP todo>
 +Implementa el [[https://developer.android.com/codelabs/camerax-getting-started#0|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í:
 +
 +{{ android:takePictureCameraX.png?200 }}
 +
 +
 +</WRAP>
 +
 +\\
 +
 +===== 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):
 +  * [[https://github.com/androidmads/QRGenerator|Generació de codis QR]].
 +    * Si us surt el QR borrós (blurred) caldrà [[https://stackoverflow.com/questions/4837715/how-to-resize-a-bitmap-in-android|escalar el Bitmap amb Bitmap.createScaledBitmap]].
 +  * [[https://github.com/yuriy-budiyev/code-scanner|Escaneig de codis QR]].
 +
 +
 +\\
  
android_camera.1674488362.txt.gz · Darrera modificació: 2023/01/23 15:39 per enric_mieza_sanchez