[mensaje-curso]
En el artículo anterior de esta serie sobre los servicios de Google Drive en Android ya vimos cómo configurar un nuevo proyecto en la consola de desarrolladores añadiendo la API de Drive para Android, y cómo realizar en nuestra aplicación las operaciones básicas de creación y eliminación de ficheros y carpetas.
En este segundo artículo vamos a seguir profundizando en las funcionalidades que proporciona este servicio, y empezaremos por cómo consultar y modificar los metadatos de un fichero o carpeta ya existente.
La consulta de los metadatos de un fichero o carpeta seguirá una vez más el mismo esquema que las operaciones que ya conocemos. En primer lugar llamaremos al método getMetadata() sobre el objeto DriveFile o DriveFolder que queramos consultar. Asignaremos un callback (ResultCallback) e implementaremos su método onResult() para ser notificado cuando finalice la operación. Si el resultado es correcto (getStatus().isSuccess()) podremos obtener los metadatos mediante el método getMetadata() del objeto MetadataResult recibido como parámetro, y a partir de éste cualquier dato concreto que necesitemos, por ejemplo su nombre (con getTitle()) o su fecha de última actualización (con getModifiedDate()). Podéis consultar el listado completo de métodos disponibles para recuperar cada metadato en la documentación de la clase MetadataResult.
Veamos cómo quedaría un método que recupere los metadatos de un fichero a partir de su DriveId:
private void getMetadata(DriveId fileDriveId) { DriveFile file = fileDriveId.asDriveFile(); file.getMetadata(apiClient).setResultCallback( new ResultCallback<DriveResource.MetadataResult>() { @Override public void onResult(DriveResource.MetadataResult metadataResult) { if (metadataResult.getStatus().isSuccess()) { Metadata metadata = metadataResult.getMetadata(); Log.i(LOGTAG, "Metadatos obtenidos correctamente." + " Title: " + metadata.getTitle() + " LastUpdated: " + metadata.getModifiedDate()); } else { Log.e(LOGTAG, "Error al obtener metadatos"); } } }); }
Modificar los metadatos de un fichero/carpeta es tan sencillo como consultarlos. Crearemos en primer lugar un objeto MetadataChangeSet con los cambios que queramos realizar en los metadatos, igual que hacíamos para crear un fichero nuevo. Una vez definidos los metadatos que queremos actualizar, llamaremos al método updateMetadata() pasándole como parámetros el conjunto de metadatos creados, asignaremos el callback habitual y revisaremos dentro del método onResult() que la operación ha finalizado correctamente.
Un método que actualizará por ejemplo el título de un fichero a partir de su DriveId quedaría de la siguiente forma:
private void updateMetadata(DriveId fileDriveId) { DriveFile file = fileDriveId.asDriveFile(); MetadataChangeSet changeSet = new MetadataChangeSet.Builder() .setTitle("TituloModificado.txt") .build(); file.updateMetadata(apiClient, changeSet).setResultCallback( new ResultCallback<DriveResource.MetadataResult>() { @Override public void onResult(DriveResource.MetadataResult metadataResult) { if (metadataResult.getStatus().isSuccess()) { Metadata metadata = metadataResult.getMetadata(); Log.i(LOGTAG, "Metadatos actualizados correctamente."); } else { Log.e(LOGTAG, "Error al actualizar metadatos"); } } }); }
Al igual que podemos consultar y modificar los metadatos también podremos, por supuesto, consultar y modificar el contenido de un fichero ya existente.
Tanto para leer como para escribir un fichero de Google Drive tendremos primero que abrirlo, indicando el modo de apertura correspondiente (apertura para leer o para escribir). Tras la apertura del fichero podremos acceder a su contenido (recordemos, objeto DriveContents), a partir del cual obtendremos su stream en entrada o salida asociado, sobre el que podremos utilizar la API estándar de Java para la lectura/escritura de ficheros.
Detallemos en primer lugar el proceso de lectura de un fichero. Comenzaremos llamando al método open() sobre el fichero (DriveFile). Como parámetro del método open() pasaremos el modo de acceso de lectura (DriveFile.MODE_READ_ONLY). Asignaremos el callback correspondiente y en el método onResult(), tras comprobar que la operación de apertura se ha realizado correctamente, llamaremos a getDriveContents() sobre el resultado recibido para obtener una referencia al contenido del fichero. Tras esto construiremos un objeto BufferedReader (api estandar java.io) a partir del stream de entrada asociado al contenido, que obtenemos llamando a getInputStream(). En mi caso de ejemplo accederé a ficheros de texto, por lo que podemos leer el contenido linea a linea utilizando el método readLine(). Por último, llamamos al método discard() del objeto DriveContents para cerrar todos los recursos abiertos.
Se muestra a continuación el código completo de un método de lectura de ficheros de texto almacenados en Drive a partir de su DriveId:
private void readFile(DriveId fileDriveId) { DriveFile file = fileDriveId.asDriveFile(); file.open(apiClient, DriveFile.MODE_READ_ONLY, null) .setResultCallback(new ResultCallback<DriveApi.DriveContentsResult>() { @Override public void onResult(DriveApi.DriveContentsResult result) { if (!result.getStatus().isSuccess()) { Log.e(LOGTAG,"Error al abrir fichero (readFile)"); return; } DriveContents contents = result.getDriveContents(); BufferedReader reader = new BufferedReader( new InputStreamReader(contents.getInputStream())); StringBuilder builder = new StringBuilder(); try { String line; while ((line = reader.readLine()) != null) { builder.append(line); } } catch (IOException e) { Log.e(LOGTAG,"Error al leer fichero"); } contents.discard(apiClient); Log.i(LOGTAG, "Fichero leido: " + builder.toString()); } }); }
La escritura sobre un fichero ya existente es prácticamente análoga. Comenzamos nuevamente abriendo el fichero llamando al método open() pasándole esta vez el modo de acceso de escritura DriveFile.MODE_WRITE_ONLY. En el onResult() del callback asignado obtenemos una referencia al contenido del fichero mediante getDriveContents() y creamos un objeto BufferedWriter a partir de su stream de salida asociado, que obtenemos mediante getOutputStream(). Escribimos el texto deseado al fichero (insisto, en mi caso de ejemplo es texto, pero podría ser cualquier contenido), y por último llamamos al método commit() sobre el objeto DriveContents para confirmar los cambios realizados. Sobre el método commit() también podemos asignar un nuevo callback para verificar que se realiza correctamente. También indicar que opcionalmente, junto con el cambio de contenido del fichero, también podremos actualizar los metadatos del mismo definiendo previamente un objeto MetadadataChangeSet y pasándoselo como parametro al método commit(). Veamos un ejemplo completo que actualiza el contenido de un fichero almacenado en Google Drive a partir de su DriveId:
private void writeFile(DriveId fileDriveId) { DriveFile file = fileDriveId.asDriveFile(); file.open(apiClient, DriveFile.MODE_WRITE_ONLY, null) .setResultCallback(new ResultCallback<DriveApi.DriveContentsResult>() { @Override public void onResult(DriveApi.DriveContentsResult result) { if (!result.getStatus().isSuccess()) { Log.e(LOGTAG,"Error al abrir fichero (writeFile)"); return; } DriveContents contents = result.getDriveContents(); BufferedWriter writer = new BufferedWriter( new OutputStreamWriter(contents.getOutputStream())); try { writer.write("Contenido del fichero modificado!"); writer.flush(); } catch (IOException e) { Log.e(LOGTAG,"Error al escribir fichero"); } //Opcional: cambio de metadatos MetadataChangeSet changeSet = new MetadataChangeSet.Builder() .setMimeType("text/plain") .build(); contents.commit(apiClient, changeSet).setResultCallback( new ResultCallback<Status>() { @Override public void onResult(Status result) { if(result.getStatus().isSuccess()) Log.i(LOGTAG, "Fichero escrito correctamente"); else Log.e(LOGTAG, "Error al escribir fichero"); } }); } }); }
Por último vamos comentar una alternativa a la hora de abrir ficheros de Drive. Al igual que ocurre para crear ficheros, la API de Google Drive para Android también nos ofrece una actividad predefinida para poder seleccionar un fichero de la cuenta de Google Drive del usuario logueado. Esta actividad permitirá al usuario seleccionar un fichero de su cuenta y nos devolverá como resultado, entre otras cosas, su DriveId asociado. Con este DriveId podremos leer o escribir el contenido del fichero con cualquiera de los dos métodos implementados anteriormente. Veamos cómo hacerlo.
De forma análoga a la actividad de creación de ficheros, lo primero que haremos será el IntentSender con el que lanzaremos la actividad. Utilizaremos para ello el método newOpenFileActivityBuilder() de la API de Drive. Tenemos la posibilidad de filtrar los ficheros que se mostrarán al usuario por su tipo de contenido (MIME Type) mediante el método setMimeType() del builder, que recibi un array con los tipos visualizables. Así, si por ejemplo queremos que sólo se puedan seleccionar fichero de texto plano podemos llamar a setMimeType(new String[] { «text/plain» }). También podemos personalizar el título de la actividad mostrada mediante setActivityTitle() o la carpeta inicial mediante setActivityStartFolder().
Construido el IntentSender lanzamos la actividad llamando a startIntentSenderForResult() pasándole como parámetro una constante arbitraria identificativa de la operación, en mi caso llamada REQ_OPEN_FILE. El resultado de esta acción lo recibiremos en el método onActivityResult() de la actividad principal y lo identificaremos por su constante identificativa. Dentro de dicho método podremos obtener el DriveId del fichero seleccionado accediento al extra del intent recibido como parámetro con el identificador OpenFileActivityBuilder.EXTRA_RESPONSE_DRIVE_ID. Una vez obtenido el DriveId del fichero podremos por ejemplo leer su contenido llamando a nuestro método auxiliar readFile() que ya describimos anteriormente:
private void openFileWithActivity() { IntentSender intentSender = Drive.DriveApi .newOpenFileActivityBuilder() .setMimeType(new String[] { "text/plain" }) .build(apiClient); try { startIntentSenderForResult( intentSender, REQ_OPEN_FILE, null, 0, 0, 0); } catch (IntentSender.SendIntentException e) { Log.e(LOGTAG, "Error al iniciar actividad: Open File", e); } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { //... case REQ_OPEN_FILE: if (resultCode == RESULT_OK) { DriveId driveId = data.getParcelableExtra( OpenFileActivityBuilder.EXTRA_RESPONSE_DRIVE_ID); Log.i(LOGTAG, "Fichero seleccionado ID = " + driveId); readFile(driveId); } break; default: super.onActivityResult(requestCode, resultCode, data); break; } }
Si ejecutamos la aplicación de ejemplo y lanzamos el método anterior podremos ver cómo la aplicación nos muestra la actividad siguiente de selección de ficheros:
Y con esto terminaríamos con el apartado de operaciones de lectura y escritura de metadatos y contenido de ficheros almacenados en Drive desde Android.
Puedes consultar y/o descargar el código completo de los ejemplos desarrollados en este artículo accediendo a la página del curso en GitHub.
[mensaje-curso]
5 comentarios
[…] Google Drive en Android (2) [Act. Septiembre 2016] […]
Hola. Para trabajar con archivos sqlite (.db) deberia utilizar los metodos descritos o hay algunos especificos? Tengo creada una aplicacion con la API de android para sqlite, (es una pequeña app )pero quisiera guardar la .db en DRIVE en lugar de la memoria interna del terminal. Gracias
Es posible crear un Socket con los ficheros de google drive.
Existe alguna manera de poder abrir el contenido de un fichero que se encuentra en la nube desde un archivo común desde la clase java.nio.file otra. Gracias
Hello,
I should like to download a Zip file but » reader.readLine()» only accept text files. How can I do to achieve ?
Thanks a lot.
Fabrice