Añadiendo fotografías en Mis Lugares

En este apartado seguiremos trabajando con el uso de intenciones aplicándolas a la aplicación Mis Lugares. En concreto, permitiremos que el usuario pueda asignar fotografías a cada lugar utilizando ficheros almacenados en su dispositivo o la cámara.

Ejercicio paso a paso: Añadiendo fotografías desde la galería

1.     Abre la clase VistaLugarActivity del proyecto MisLugares.

2.     Añade los siguientes atributos a la clase:

private ImageView imageView;
final static int RESULTADO_GALERIA= 2;
final static int RESULTADO_FOTO= 3;

Desde la actividad VistaLugarActivity llamamos a diferentes actividades y algunas de ellas nos tienen que devolver información. En estos casos llamamos a la actividad con startActivityForResult() pasándole un código que identifica la llamada. Cuando esta actividad termine, se llamará al método onActivityResult(), que nos indicará el mismo código usado en la llamada. Como vamos a hacerlo con tres actividades diferentes, hemos creado tres constantes, con los respectivos códigos de respuesta. Actuando de esta forma conseguimos un código más legible.

3.     En el método onCreate(), antes de actualizarVistas() añade:

imageView= (ImageView) findViewById(R.id.foto);

4.     Añade el siguiente método:

public void galeria(View view) {
   Intent intent = new Intent(Intent.ACTION_PICK,
           android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
   startActivityForResult(intent, RESULTADO_GALERIA);
}

Este método crea una intención indicando que queremos seleccionar contenido. El contenido será proporcionado por el Content Provider MediaStore; además le indicamos que nos interesan imágenes del almacenamiento externo. Típicamente se abrirá la aplicación galería de fotos (u otra similar). Como necesitamos una respuesta, usamos startActivityForResult() con el código adecuado.

5.     Busca en vista_lugar.xml el ImageView con descripción “logo galería” y añade el atributo:

android:onClick="galeria"

6.     En el método onActivityResult() añade la sección else if que se muestra:

if(requestCode == RESULTADO_EDITAR) {
   …
} else if (requestCode == RESULTADO_GALERIA) {
   if (resultCode == Activity.RESULT_OK) {
      lugar.setFoto(data.getDataString());
      ponerFoto(imageView, lugar.getFoto());
   } else {
      Toast.makeText(this, "Foto no cargada", Toast.LENGTH_LONG).show();
   }
}

Comenzamos verificando que volvemos de la actividad lanzada por la intención anterior y verificamos que el usuario no ha cancelado la operación. En este caso, nos tiene que haber pasado en la intención de respuesta, data, una URI con la foto seleccionada. Esta URI puede ser del tipo file://…, content://… o http://… según qué aplicación haya resuelto esta intención. El siguiente paso consiste en modificar el contenido de la vista que muestra la foto, imageView, con esta URI. Lo hacemos en el método ponerFoto():

7.     Añade el siguiente método:

protected void ponerFoto(ImageView imageView, String uri) {
    if (uri != null && !uri.isEmpty() && !uri.equals("null")) {
        imageView.setImageURI(Uri.parse(uri));
    } else{
        imageView.setImageBitmap(null);
    }
}

Este método comienza verificando que nos han pasado algún archivo válido en URI. Si es así, lo asigna a imageView. En caso contrario, se le asigna un Bitmap igual a null, que es equivalente a que no se represente ninguna imagen.

8.     Vamos a leer ficheros de la memoria externa; por lo tanto, tenemos que pedir el permiso oportuno. En AndroidManifest.xml añade dentro de la etiqueta <manifest …> </manifest> el siguiente código:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

NOTA: Si ejecutas el proyecto en un dispositivo o emulador con una versión 6.0 o superior, en el momento que la aplicación lea un fichero de la memoria externa se producirá un error. A partir de la versión 6.0 es obligatorio que el usuario de permiso de forma explícita antes de poder realizar esta acción. Más adelante mostraremos cómo solicitar este permiso desde la aplicación. De momento, para poder probar el ejercicio vamos a dar este permiso de forma manual. Para ello, si tu dispositivo tiene una versión 6 o superior, accede a Ajustes > Aplicaciones > Mis Lugares > Permisos y activa el permiso de almacenamiento:
 

9.  Ya puedes ejecutar la aplicación. Si añades una fotografía a un lugar, esta se visualizará. Sin embargo, si vuelve a la lista de lugares y seleccionas el mismo lugar al que asignaste la fotografía, esta ya no se representa. La razón es que no hemos visualizado la foto al crear la actividad.

10.  En el método actualizarVistas() añade la siguiente línea al final:

ponerFoto(imageView, lugar.getFoto());

11.  Verifica de nuevo el funcionamiento de la aplicación.

Ejercicio paso a paso: Añadiendo fotografías desde la camara

    1.     Añade el siguiente atributo a la clase VistaLugarActivity:

private Uri uriFoto;

Como veremos, necesitamos esta variable en dos métodos diferentes. Por lo tanto, la declaramos de forma global.

2.     Añade el siguiente método a la clase VistaLugarActivity:

public void tomarFoto(View view) {
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE");
    uriFoto = Uri.fromFile(new File(
       Environment.getExternalStoragePublicDirectory()+File.separator
       +"img_" + (System.currentTimeMillis() / 1000) + ".jpg"));
    intent.putExtra(MediaStore.EXTRA_OUTPUT, uriFoto);
    startActivityForResult(intent, RESULTADO_FOTO);
} 

Este método crea una intención indicando que queremos capturar una imagen desde el dispositivo. Típicamente se abrirá la aplicación cámara de fotos. A esta intención vamos a añadirle un extra con una URI al fichero donde queremos que se almacene la fotografía. Para crear este fichero, se parte del directorio de almacenamiento usado por defecto para las fotografías (en muchos dispositivos, “/sdcard”), seguido del separador de carpetas (“/”), luego “img_” y se añade un valor numérico. El método currentTimeMillis() nos da el número de milisegundos transcurridos desde 1.970. Al dividir entre 1.000, tenemos el número de segundos. El objetivo que se persigue es que, al crear un nuevo fichero, su nombre nunca coincida con uno anterior. Finalmente se añade la extensión del fichero. En la última línea llamamos a startActivityForResult() con el código adecuado.

NOTA: Observa cómo en el ejercicio anterior hemos tenido que solicitar permiso para acceder a la memoria externa, pero en este no es necesario solicitar permiso para tomar una fotografía. La razón es que realmente nuestra aplicación no toma la fotografía directamente, sino que por medio de una intención lanzamos otra aplicación, que sí que tiene este permiso.

3.     Busca en vista_lugar.xml el ImageView con descripción “logo cámara” y añade el atributo:

android:onClick="tomarFoto"

4.     En el método onActivityResult() añade la sección else if que se muestra:

} else if (requestCode == RESULTADO_GALERIA
   …
} else if(requestCode == RESULTADO_FOTO 
   if (resultCode == Activity.RESULT_OK
           && lugar!=null && uriFoto!=null) {
      lugar.setFoto(uriFoto.toString());
      ponerFoto(imageView, lugar.getFoto());
   } else {
      Toast.makeText(this, "Error en captura", Toast.LENGTH_LONG).show();
   }
}

Comenzamos verificando que volvemos de la actividad lanzada por la intención anterior, que el usuario no ha cancelado la operación y que los objetos con los que trabajamos siguen existiendo (NOTA: En la siguiente unidad veremos cuándo puede ocurrir esto). En este caso, se nos pasa información en la intención de respuesta, pero sabemos que en uriFoto está almacenada la URI con el fichero donde se ha almacenado la foto. Guardamos esta URI en el campo adecuado de lugar e indicamos que se represente en imageView.

5.     Verifica de nuevo el funcionamiento de la aplicación.

NOTA: En algunos dispositivos puede aparecer un error de memoria si la cámara está configurada con mucha resolución. En estos casos puedes probar con la cámara delantera.

Ejercicio paso a paso: Añadiendo un botón para eliminar fotografías.

1.     En el layout vista_lugar.xml añade el siguiente botón dentro del LinearLayout donde están los botones para la cámara y para la galería:

<ImageView
    android:layout_width="40dp"
    android:layout_height="40dp"
    android:contentDescription="Eliminar foto"
    android:onClick="eliminarFoto"
    android:src="@android:drawable/ic_menu_close_clear_cancel" />

2.     Añade el siguiente método a la clase VistaLugarActivity:

public void eliminarFoto(View view) {
   lugar.setFoto(null);
   ponerFoto(imageView, null);
}

3.     Verifica el funcionamiento del nuevo botón.

NOTA: Si lo deseas, puedes poner un cuadro de diálogo para confirmar la eliminación. Véase la práctica “Un cuadro de diálogo para confirmar el borrado”. No obstante, es una operación reversible; se puede volver a asignar la fotografía buscándola en la galería.