Por que eu devo ler este artigo:O Google Drive é um serviço que nos permite armazenar arquivos na nuvem. Desenvolver um aplicativo conectado a este serviço abre inúmeras possibilidades de recursos, como criação de cópias de segurança, sincronização de dados, colaboração na edição de arquivos em tempo real, etc. Neste artigo aprenderemos a criar um aplicativo Android, conectá-lo ao Google Play Services e utilizar o Google Drive Android API para criarmos e recuperarmos arquivos diretamente no Google Drive a partir do nosso aplicativo.
Guia do artigo:

Google Drive é um serviço de armazenamento de arquivos que permite ao usuário organizar, fazer backups e compartilhar fotos, músicas, vídeos e outros documentos na nuvem. Atualmente o serviço fornece gratuitamente um espaço de 15 Gb, sendo que o conteúdo armazenado pode ser sincronizado entre diversos dispositivos como computadores, tablets e smartphones.

Em janeiro de 2014 o Google disponibilizou o Google Drive Android API (ver BOX 1), permitindo aos desenvolvedores criarem aplicativos que façam uso das funcionalidades disponibilizadas pelo Google Drive como sincronização, suporte off-line, experiências colaborativas, entre outros.

BOX 1. API
Uma Interface de Programação de Aplicações (API) é um conjunto de serviços disponibilizados por um software para utilização de suas funcionalidades por outros aplicativos sem que estes tenham que conhecer detalhes de implementação do software que está disponibilizando o serviço. Por exemplo, vamos supor que o leitor queira construir um aplicativo que exiba previsões do tempo. Neste caso, é possível utilizar uma API específica como a OpenWeatherMap API (veja a seção Links) para obter a temperatura ou condição do tempo em uma cidade apenas fornecendo seu nome, sem precisarmos se ater a detalhes de como este serviço coleta e/ou calcula estas informações.

Dentre algumas funcionalidades disponibilizadas pelo Google Drive Android API, podemos citar:

  • Sincronização de arquivos com a nuvem de forma transparente: Se ao tentar salvar um arquivo o usuário não possuir acesso à Internet no momento, os dados serão armazenados localmente e sincronizados de forma transparente assim que a conectividade for reestabelecida. Todo o gerenciamento de possíveis erros de conexão é feito pela própria API.
  • Componentes de Interface do Usuário: São disponibilizados controles visuais que permitem ao usuário selecionar um arquivo ou uma pasta, ou criar um arquivo, usando a mesma interface que ele já está acostumado a utilizar no aplicativo do Google Drive, conforme podemos observar na Figura 1;
  • Acesso direto às funcionalidades do Google Drive: Você também poderá manipular os metadados de um arquivo, opções de compartilhamento, etc.
Uso da interface nativa do Google
Drive para Android
Figura 1. Uso da interface nativa do Google Drive para Android.

É importante também informar que caso você queira desenvolver aplicativos para outras plataformas usando o Google Drive, existem APIs específicas para Web e iOS (veja a seção Links). São comuns também dúvidas em relação às diferenças do Google Drive Android API e do Storage Access Framework introduzido no Android KitKat. Enquanto o Google Drive Android API é uma API específica para se trabalhar com o Google Drive, o Storage Access Framework é uma API genérica que pode trabalhar com muitas tecnologias de armazenamento usando a mesma interface, porém, só funcionando nas versões do Android KitKat ou superior.

Google Play Services

O Google Play Services é um serviço executado em background em dispositivos Android. Dentre outras funcionalidades, este serviço disponibiliza diversas APIs para que os desenvolvedores possam integrar seus aplicativos com os principais produtos do Google como o Google+, Maps, Wallet, Games, Location, Drive, entre outros.

Um dos motivos pelo qual o Google fornece acesso a seus serviços através do Google Play Services é que suas atualizações são gerenciadas pela Play Store sem que haja necessidade de atualizações do sistema operacional, conforme Figura 2. Atualmente, estima-se que o Google Play Services esteja disponível em 98% dos dispositivos Android com versão 2.3 (Gingerbread) e/ou superior. Versões inferiores ao Android ou que não possuam o Google Play Store instalados não são compatíveis com o Google Play Services.

Google Play Services
Figura 2. Google Play Services.

A conexão entre o aplicativo Android e o Google Play Services é realizado através do Google Play Services Client Library (veja na seção Links) e, uma vez conectado, devemos obter uma autorização para acessar cada um dos serviços Google em questão afim de prover suas funcionalidades em nosso aplicativo. A utilização desta abordagem nos proporciona alguns benefícios como a diminuição do tamanho final do nosso aplicativo assim como menos atualizações.

No aplicativo de exemplo que desenvolveremos ao longo do artigo, aprenderemos a realizar uma conexão com o Google Play Services e a obter uma autorização do usuário para acessarmos as funcionalidades disponibilizadas pelo Google Drive Android API.

Certificados e assinaturas digitais

Todo aplicativo Android requer uma assinatura digital para que possa ser instalado em um dispositivo. Esta assinatura digital é realizada através de um certificado digital que tem por finalidade armazenar uma chave privada. É através desta chave privada que é possível identificar um desenvolvedor.

Há diferentes tipos de certificados digitais para vários propósitos. Os certificados digitais também podem ser armazenados em dispositivos criptográficos como tokens e smartcards ou em arquivos. Além disso, os certificados digitais também podem ser fornecidos por uma Autoridade Certificadora (alguns exemplos aqui no Brasil são os Correios, Serasa e Receita Federal), ou ainda, serem emitidos pelo próprio usuário, o que chamamos de certificados digitais auto assinados. Para o desenvolvimento de aplicativos Android, podemos gerar e utilizar um certificado digital auto assinado armazenado em um arquivo.

Durante a construção do nosso aplicativo de exemplo, precisaremos obter o fingerprint do nosso certificado digital. Fingerprint, ou “impressão digital”, é o resultado de uma operação criptográfica realizada através de um algoritmo de Hash (em nosso caso, o algoritmo SHA-1) em nosso certificado digital. O resultado desta operação será único para cada certificado gerado e terá sempre tamanho fixo, neste caso 160 bits, ou 40 caracteres representados na notação hexadecimal, conforme exemplo a seguir:


  Certificate fingerprint:

  SHA1: DA:39:A3:EE:5E:6B:4B:0D:32:55:BF:EF:95:60:18:90:AF:D8:07:09

Google Developers Console

Todo site ou aplicativo que fizer uso dos serviços disponibilizados pelo Google, como o Drive ou Maps, deverá ser registrado na plataforma Cloud do Google. O acesso aos recursos desta plataforma é realizado pelo Google Developers Console (veja na seção Links).

Ao registrar um aplicativo como um novo Projeto no Google Cloud, em casos de aplicativos Android, deveremos informar o Package Name (veja o BOX 2) assim como a impressão digital do certificado utilizado para assinar o aplicativo, além de informarmos quais APIs estarão disponíveis para aquele projeto.

BOX 2. Package Name
Todo aplicativo Android está relacionado a um pacote. O nome deste pacote é um identificador único na Play Store, o que significa que não haverá dois aplicativos com o mesmo nome de pacote. Por isso, geralmente é seguida uma convenção de compor o nome do pacote utilizando o DNS reverso da organização seguido de um identificador do aplicativo, por exemplo, br.com.devmedia.exemplodrive.

Desta forma, através do Package Name e do fingerprint do certificado digital, o Google pode verificar se uma determinada requisição tem como origem um aplicativo autorizado a fazer uso daquela API. Além disso, o Google pode controlar cotas e realizar o faturamento de APIs pagas ou que por ventura excederem os limites estipulados.

Criando um aplicativo Android integrado com o Google Drive

Seguindo os conceitos apresentados anteriormente, vamos criar um aplicativo Android que faça uso das funcionalidades disponibilizadas pelo Google Drive Android API para salvarmos um arquivo diretamente no Google Drive do usuário. Veremos como obter um fingerprint de um certificado digital para em seguida cadastrarmos um novo projeto na plataforma Cloud do Google, informando que o projeto utilizará o Google Drive Android API. Em seguida, criaremos nosso aplicativo e aprenderemos a fazer uma conexão entre ele e o Google Play Services através do Google Play Services Client Library para obtermos acesso à API do Google Drive mediante autorização do usuário. Na etapa final, criaremos um arquivo diretamente no Google Drive, manipulando possíveis problemas, como falta de conectividade.

Para testarmos o aplicativo, sugerimos a utilização de um dispositivo real. No caso da utilização de um emulador, certifique-se de que o Google Play Store e o Google Play Services estejam instalados.

Obtendo o fingerprint de um certificado digital

A impressão digital de um certificado digital pode ser verificada através do comando keytool, disponibilizado junto com o JDK, ou Kit de Desenvolvimento Java, e que pode ser executado pelo terminal ou prompt de comando, conforme seu sistema operacional. Como parâmetros do comando, deveremos informar a localização do certificado digital, assim como sua senha, conforme a Figura 3.

Geralmente, enquanto estamos desenvolvendo, não utilizamos um certificado digital real. Mesmo assim, o sistema de build do Android Studio gerará e utilizará automaticamente um certificado de testes, que chamamos de certificado de debug, como no caso da Figura 3. Note que a senha padrão para o certificado de debug é “android”.

Uso do comando keytool para obter
um fingerprint de um certificado digital
Figura 3. Uso do comando keytool para obter um fingerprint de um certificado digital.

Criando seu projeto no Google Cloud Platform

Ao acessarmos o console da plataforma Cloud do Google através da URL disponibilizada na seção Links, podemos criar um novo Projeto fornecendo seu nome e seu identificador único.

Em seguida, devemos ir até a aba APIs e definir quais APIs serão utilizadas no projeto. Neste exemplo, habilitaremos o uso da Drive API permitindo que nosso aplicativo receba as credenciais para acessar a API logo após a autorização do usuário.

O passo a seguir é criarmos um novo Id do cliente, informando que o cliente trata-se de um aplicativo Android, e fornecendo o fingerprint do certificado digital utilizado para assinar o aplicativo, assim como o Package Name, conforme mostrado na Figura 4.

Definindo um
ID do cliente para o aplicativo Android
Figura 4. Definindo um ID do cliente para o aplicativo Android.

O último passo é ir até a tela de Consentimento e informar seu e-mail e o nome do Aplicativo, sendo que os demais dados são opcionais. Assim que o usuário instalar seu aplicativo e fizer a primeira utilização, estes dados serão apresentados a ele para que ele aceite (ou recuse) o acesso ao seu Google Drive. Portanto, estas informações, assim como um logotipo, são fundamentais para deixar seu aplicativo com um aspecto mais profissional.

Utilizando o GoogleApiClient

Conforme mencionado anteriormente, precisamos conectar nosso aplicativo ao Google Play Services para obtermos acesso às principais APIs disponibilizadas pelo Google, como Drive e Maps. O Google Play Services SDK fornece uma classe chamada GoogleApiClient que é responsável por estabelecer a conexão entre seu aplicativo e o Google Play Services (ver Figura 5), além de gerenciar a conectividade entre o dispositivo e o serviço do Google.

Acesso às
APIs do Google através do Google Play Services
Figura 5. Acesso às APIs do Google através do Google Play Services.

Usando o IDE Android Studio, podemos nos beneficiar do sistema de build Gradle (veja o BOX 3) para gerenciar as dependências, baixando a biblioteca do Play Services e integrando-a em nosso aplicativo. Gradle é um sistema de build capaz de gerenciar dependências e permitir a construção de lógica para realizar o build do seu aplicativo. O sistema de build é independente do Android Studio, possibilitando inclusive que o desenvolvedor o utilize através de linha de comando em máquinas onde o Android Studio não está instalado, como servidores de integração contínua.

Para isso, devemos ir até o arquivo build.gradle e na seção de dependências, devemos incluir a biblioteca com.google.android.gms:play-services:[versão atual], conforme a Listagem 1.

Listagem 1. Gerenciamento de dependências através do Gradle.

  dependencies {
      compile fileTree(dir: 'libs', include: ['*.jar'])
      compile 'com.android.support:appcompat-v7:21.0.3'
      compile 'com.google.android.gms:play-services:6.5.87'
  }

Há pelo menos dois arquivos build.gradle em seu projeto, um localizado na raiz do seu projeto <PROJETO>\build.gradle que é o “Top-Level build file” onde podemos adicionar opções de configurações para todos os módulos, e o outro dentro do módulo app <PROJETO>\app que é o arquivo específico para o módulo. É neste arquivo que as configurações da Figura 5 deverão ser adicionadas.

No método onCreate() da Listagem 2 devemos instanciar nosso objeto. Note que fazemos isso através do método estático GoogleApiClient.Builder. O método addAPI é responsável por especificar quais APIs o app deseja se conectar, em nosso exemplo o Drive.API. Por exemplo, se quiséssemos nos conectar ao Google Plus, bastaria passar Plus.API. Podemos inclusive adicionar várias APIs ao mesmo objeto. O método addScope especifica escopos OAuth 2.0 requeridos pelo App, em nosso caso, o Drive.SCOPE_FILE. Também passamos nossa Activity como parâmetro para os métodos addConnectionCallbacks e addOnConnectionFailedListener, já que nossa Activity implementa estas interfaces.

Seguindo o ciclo de vida da Activity, após o método onCreate, será chamado o método onStart, quando a Activity já está visível. Este é um bom momento para realizarmos nossa conexão chamando o método connect. Também sobrescrevemos o comportamento do método onStop para desconectarmos da API caso o usuário saia do nosso aplicativo. Já os métodos onConnected, onConnectionSuspended e onConnectionFailed serão automaticamente chamados dependendo do resultado da tentativa de conexão.

Listagem 2. Conectando ao Google Play Services.

@Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
   
    mGoogleApiClient = new GoogleApiClient.Builder(this)
      .addApi(Drive.API)
      .addScope(Drive.SCOPE_FILE)
      .addConnectionCallbacks(this)
      .addOnConnectionFailedListener(this)
      .build();
   
    setContentView(R.layout.activity_main);
  }
   
  @Override
  protected void onStart() {
    super.onStart();
    mGoogleApiClient.connect();
  }
   
  @Override
  protected void onStop() {
    super.onStop();
    mGoogleApiClient.disconnect();
  }
   
  @Override
  public void onConnected(Bundle bundle) {
    // Conexão foi realizada com sucesso!   
  }
   
  @Override
  public void onConnectionSuspended(int i) {
    // Conexão foi suspensa, desabilitar controles que dependam das APIs
  }
   
  @Override
  public void onConnectionFailed(ConnectionResult connectionResult) {
    // Conexão falhou, devemos tentar resolver o problema  
  }

Caso você queira utilizar outras APIs do Google, você pode utilizar o mesmo objeto, simplesmente adicionando chamadas para o método addApi().

Se tentarmos executar nosso aplicativo agora, provavelmente obteremos um erro de conexão, isto porque o usuário ainda não deu permissão para o aplicativo acessar o seu Google Drive. Desta forma, o método onConnectionFailed() será chamado, recebendo como parâmetro, o erro SIGN_IN_REQUIRED.

Neste caso devemos tentar resolver o problema que originou a chamada do método onConnectionFailed(). O erro que é recebido por parâmetro possui um método chamado hasResolution() que nos permite identificar se é possível resolvê-lo. Se não for possível resolvê-lo, então chamamos o código apresentado a seguir para exibirmos uma janela de diálogo do Google Play descrevendo o motivo do erro para o usuário:

GooglePlayServicesUtil.getErrorDialog(connectionResult.getErrorCode(), this, 0).show();

Porém, muitos problemas podem ser resolvidos. Neste caso, devemos usar o método startResolutionForResult(), conforme a Listagem 3, disparando uma Intent para tentar solucionar o problema como, por exemplo, a tela de permissão para que o usuário conceda acesso ao seu Google Drive.

Intents (ou intenções) são mensagens assíncronas que permitem que as aplicações requisitem funcionalidades de outros componentes (e aplicações) Android que gostaríamos de executar. Por exemplo, podemos dizer que temos a “Intenção” de realizar uma ligação telefônica e, neste caso, o discador responde à nossa solicitação realizando toda a tarefa sem que o desenvolvedor precise se envolver em detalhes técnicos de como realizar uma ligação telefônica. Disponibilizamos na seção Links um endereço que possui uma lista de Intents nativas do Android. É importante ressaltar que o desenvolvedor também pode criar suas próprias Intents.

Listagem 3. Tentando resolvendo um problema de conexão.

if (connectionResult.hasResolution()) {
    try {
      connectionResult.startResolutionForResult(this, RESOLVE_CONNECTION_REQUEST_CODE);
    } catch (IntentSender.SendIntentException e) {
      // Não foi possível resolver o problema, avisar o usuário
    }
  }

Quando este código for executado (vide Figura 6), será disparada a Intent que exibirá a tela de permissão para acesso ao Google Drive para o usuário. O usuário poderá aceitar ou recusar a permissão, sendo que podemos obter o retorno deste resultado no método onActivityResult() da Activity, da mesma forma como ocorre com o método startActivityForResult().

Ao dispararmos uma intenção usando o método startResolutionForResult da Listagem 3, passamos também um REQUEST_CODE, que é um número que identifica exclusivamente aquela Intent. Sendo assim, cada Intent deverá possuir seu próprio REQUEST_CODE. Quando esta (ou outras) Intent for finalizada, será chamado o método onActivityResult() que pertence à Activity que disparou a Intent. É neste método que podemos tratar o resultado da Intent. Mas, se eu posso disparar várias Intents, e ao ser finalizada, todas chamam o método onActivityResult, como saber qual a Intent que está sendo finalizada? Simples, através do REQUEST_CODE que recebemos como parâmetro. Além do REQUEST_CODE, também recebemos um outro parâmetro chamado RESULT_CODE que indica se o usuário confirmou ou cancelou a operação, ou seja, em nosso exemplo, se ele aceitou que o aplicativo se conectasse ao seu Google Drive ou não, conforme Figura 6.

Conectando ao Google Play Services
Figura 6. Conectando ao Google Play Services.

Conforme o código da Listagem 4, se o resultado da Intent que tenta solucionar o problema for RESULT_OK, como no caso do usuário conceder acesso, devemos tentar conectar nosso objeto novamente ao Google Play Services, reiniciando o ciclo.

Listagem 4. Obtendo o resultado da tentativa de resolver um problema de conexão.

@Override
  protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
   
    switch (requestCode) {
      case RESOLVE_CONNECTION_REQUEST_CODE:
        if (resultCode == RESULT_OK) {
          mGoogleApiClient.connect();
        }
        break;
    }
  }

Criando um arquivo no Google Drive

A Google Drive Android API fornece acesso para que o desenvolvedor consiga realizar as mais diversas operações. Há inclusive um projeto com código-fonte disponibilizado no GitHub (veja a seção Links) para que o desenvolvedor possa executar rapidamente um exemplo. Neste artigo, vamos demostrar como podemos criar um arquivo diretamente na pasta-raiz do Google Drive do usuário.

Note que na maioria das chamadas às APIs, devemos passar como parâmetro nosso objeto mGoogleApiClient que está conectado ao Google Play Services:

Drive.DriveApi.newDriveContents(mGoogleApiClient).setResultCallback(driveContentsCallback);

Este método retorna um DriveContents que pode ser usado para prover o conteúdo inicial de um arquivo. Ainda definimos um callback assíncrono para recebermos o resultado. Quando o método onResult() do callback for invocado, podemos obter o resultado que, por sua vez, se retornar sucesso, indica que podemos executar as ações que desejamos. Neste caso, a ação é definir os atributos de um arquivo (Listagem 5).

Nosso callback chama-se driveContentsCallback, que é exatamente o mesmo callback passado por parâmetro na chamada do método newDriveContents. Além disto, observando a declaração do callback, notamos que ele é da interface ResultCallback, que por sua vez possui a assinatura do método onResult forçando-nos a implementar este método aqui. Note também que a Interface ainda pode receber qualquer classe que estenda com.google.android.gms.common.api.Result. Isto porque ResultCallback não representa o resultado final de uma operação exclusiva do Google Drive, mas também de outras APIs do Google Play Services. O mesmo tipo que passarmos na declaração do callback, neste caso, um DriveApi.DriveContentsResult, será o tipo de receberemos como parâmetro na chamada do método onResult.

Listagem 5. Recuperando a instância de um DriveContent usado para prover o conteúdo inicial de um arquivo.

final private ResultCallback<DriveApi.DriveContentsResult> driveContentsCallback =
    new ResultCallback<DriveApi.DriveContentsResult>() {
      @Override
      public void onResult(DriveApi.DriveContentsResult result) {
        if (!result.getStatus().isSuccess()) {
          Toast.makeText(MainActivity.this, "Não foi possível obter um DriveContent",Toast.LENGTH_SHORT).show();
          return;
        }
      }
    };

Porém, caso o retorno do resultado seja um sucesso, podemos continuar informando o conteúdo do arquivo assim como seus atributos.

Note que ao chamarmos o método createFile() (veja a Listagem 6), definimos um novo callback que nos retornará se o arquivo foi criado com sucesso ou se houve algum erro (como demonstra a Listagem 7). Esta definição de callbacks para as diversas operações são comuns na API do Google Drive, pois as operações devem ser realizadas de forma assíncrona.

Na Listagem 6 criamos um objeto que ao ser iniciado através do método start() chamará imediatamente o método run(). Devemos executar operações de I/O em uma nova thread, pois uma operação possivelmente demorada bloquearia a interface do usuário e dispararia um erro conhecido como ANR (Application Not Responding) caso não fosse concluída em cinco segundos. Mesmo que sua operação não demore este tempo, saiba que um tempo de 100 a 200 milissegundos já é o suficiente para um usuário detectar falta de fluidez no seu aplicativo. Para saber mais sobre ANR e como manter seu aplicativo responsivo, consulte a seção Links.

No método run() criamos um OutputStreamWriter que tem o conteúdo “Olá Google Drive”. Em seguida, instanciamos um objeto do tipo MetadataChangeSet, onde podemos definir os atributos do arquivo que será criado. Neste caso, usamos o método setTitle para definirmos o título do arquivo como “Mobile Magazine”, o método setMimeType para definirmos que se trata de um arquivo texto, e o método setStarred para definirmos que este é um arquivo já favoritado. Depois, chamamos o método createFile para criarmos o arquivo na pasta-raiz (root). As operações no Google Drive são feitas de forma assíncrona, pois não sabemos quanto tempo pode demorar para a operação ser concluída. Se a programação fosse sequencial, o aplicativo seria bloqueado até que a operação fosse concluída, travando a interface do usuário. Além disso, se a operação demorasse muito, obteríamos um erro ANR. Desta forma, vamos novamente definir um callback que será chamado quando a operação for concluída, seja ela concluída com sucesso ou não. Em nosso código definimos o callback fileCallback cuja implementação será apresentada mais adiante.

Listagem 6. Criando um arquivo no Google Drive do usuário.

final DriveContents driveContents = result.getDriveContents();
   
  // Executar I/O na UI Thread
  new Thread() {
    @Override
    public void run() {
      OutputStream outputStream = driveContents.getOutputStream();
      Writer writer = new OutputStreamWriter(outputStream);
      try {
        writer.write("Olá Google Drive!");
        writer.close();
      } catch (IOException e) {
        Log.e(TAG, e.getMessage());
      }
   
      // Definindo atributos do arquivo a ser criado
      MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
        .setTitle("Mobile Magazine")
        .setMimeType("text/plain")
        .setStarred(true).build();
   
      // Criar arquivo na pasta raiz
      Drive.DriveApi.getRootFolder(mGoogleApiClient)
        .createFile(mGoogleApiClient, changeSet, driveContents)
        .setResultCallback(fileCallback);
    }
  }.start();

Agora, na Listagem 7vamos implementar o callback que será executado quando a operação de criação do arquivo texto chamado “Mobile Magazine” na pasta raiz for concluído. Assim como na Listagem 5, vamos definir um ResultCallback, mas desta vez passando um DriveFolder.DriveFileResult. Assim, no método onResult, temos o resultado da operação através do result.getStatus(). No código da Listagem 7, somente mostramos uma mensagem informando se o arquivo foi ou não criado com sucesso. Se, além disso, o desenvolvedor quiser ter acesso ao arquivo recém criado, pode fazer isso através do método result.getDriveFile().

Listagem 7. Definindo um callback para receber o resultado acerca da operação de criação do arquivo.

final
private ResultCallback<DriveFolder.DriveFileResult> fileCallback = new
ResultCallback<DriveFolder.DriveFileResult>()
{

  @Override  

  public void onResult(DriveFolder.DriveFileResult result) {

   if (!result.getStatus().isSuccess()) {


    Toast.makeText(MainActivity.this,"Erro ao tentar criar arquivo",Toast.LENGTH_SHORT).show();

   return;

 }

  Toast.makeText(MainActivity.this,"Arquivo criado com
sucesso!",Toast.LENGTH_SHORT).show();

}
};

Devemos executar operações de I/O em uma nova thread, pois uma operação possivelmente demorada bloquearia a interface do usuário e dispararia um erro conhecido como ANR (Application Not Responding) caso não fosse concluída em cinco segundos. Mesmo que sua operação não demore este tempo, saiba que um tempo de 100 a 200 milissegundos já é o suficiente para um usuário detectar falta de fluidez no seu aplicativo. Para saber mais sobre ANR e como manter seu aplicativo responsivo, consulte a seção Links.

Lendo um arquivo do Google Drive

Agora vamos ler o arquivo que acabamos de criar. O processo de leitura é simples porque o Google Drive Android API automaticamente realiza o download do conteúdo solicitado caso ele ainda não tenha sido sincronizado localmente. Primeiramente vamos definir uma consulta para buscar arquivos no Google Drive usando seu nome, conforme o código a seguir:

Query query = new Query.Builder()

    .addFilter(Filters.eq(SearchableField.TITLE, "Mobile Magazine"))
    .build();

Agora vamos definir um callback para obtermos o resultado da consulta. Uma consulta conforme a utilizada anteriormente poderia retornar vários arquivos como resultado, de tal forma que nós poderíamos iterar sobre eles. Na Listagem 8 vamos obter os metadados do primeiro arquivo retornado, e posteriormente, vamos obter o arquivo propriamente dito através do seu driveId. Em seguida, invocaremos o método open do arquivo e criaremos um callback para sermos notificado quando o arquivo for aberto, para finalmente lermos o seu conteúdo e exibirmos na tela (vide Figura 7).

Na Listagem 8 chamamos o método DriveApi.query para executar a consulta que definimos anteriormente. Na mesma chamada do método já definimos um callback que será executado quando a consulta for concluída. Nas listagens anteriores, para facilitar a compreensão, criamos o objeto callback separado e depois passávamos como parâmetro para o método setResultCallback. Trata-se do mesmo mecanismo, mas agora, ao invés de criarmos o callback separado, já estamos o instanciado na chamada do método setResultCallback.

Assim que o método run() for chamado, ou seja, quando a consulta for concluída, verificamos se a mesma foi concluída com sucesso ou não, sendo que, se não foi, mostramos uma mensagem de erro e saímos do método. Caso a consulta tenha sido realizada com sucesso, avançamos no código obtendo um MetaDataBuffer que nos permite analisar o resultado da consulta como, por exemplo, quantos arquivos o usuário possui com os critérios estabelecidos pela consulta. Se o usuário possuir mais que um arquivo com os critérios estabelecidos, recuperamos os metadados do primeiro arquivo. Este metadado, além de várias informações a respeito do arquivo, possui o seu DriveId que é um identificador único de cada arquivo. É através dele que podemos recuperar exatamente um arquivo. Então, após obtermos um arquivo usando o seu DriveId, abrimos um arquivo chamando o método open() e definimos um callback que será executado quando houver um resultado para esta operação, seja o arquivo aberto com sucesso ou não. Por sua vez, neste callback, se o arquivo foi aberto com sucesso, podemos obter o seu conteúdo lendo-o linha a linha através de um BufferedReader para então, em seguida, exibi-lo ao usuário através de um Toast.

Listagem 8. Processando o resultado de uma consulta, abrindo um arquivo e lendo seu conteúdo.

Drive.DriveApi.query(mGoogleApiClient, query)
    .setResultCallback(new ResultCallback<DriveApi.MetadataBufferResult>() {
    @Override
    public void onResult(DriveApi.MetadataBufferResult resultadoConsulta) {
      if (!resultadoConsulta.getStatus().isSuccess()) {
        Toast.makeText(MainActivity.this, "Erro ao tentar executar consulta", Toast.LENGTH_SHORT).show();
        return;
      }
   
      // Obtendo lista de metaDatas compatíveis com a busca (Título = Mobile Magazine)
      MetadataBuffer metadataBuffer = resultadoConsulta.getMetadataBuffer();
   
      if(metadataBuffer.getCount() == 0) {
        Log.i(TAG, "Busca não retornou nenhum resultado, abortar leitura...");
        return;
      }
   
      // Vários resultados podem ser retornados, vamos processar o primeiro
      Metadata metaData = metadataBuffer.get(0);
   
      // Obter um arquivo através do seu driveId
      DriveFile arquivo = Drive.DriveApi.getFile(mGoogleApiClient, metaData.getDriveId());
   
      // Comando para abrir o arquivo, definindo callback para ser executado de forma assíncrona
      arquivo.open(mGoogleApiClient, DriveFile.MODE_READ_ONLY, null).setResultCallback(new ResultCallback<DriveApi.DriveContentsResult>() {
        @Override
        public void onResult(DriveApi.DriveContentsResult resultadoAberturaArquivo) {
          if (!resultadoAberturaArquivo.getStatus().isSuccess()) {
            Toast.makeText(MainActivity.this, "Erro ao tentar abrir arquivo", Toast.LENGTH_SHORT).show();
            return;
          }
   
          DriveContents conteudo = resultadoAberturaArquivo.getDriveContents();
          BufferedReader reader = new BufferedReader(new InputStreamReader(conteudo.getInputStream()));
          StringBuilder builder = new StringBuilder();
          String linha;
          try {
            // Lendo o conteúdo do arquivo, linha a linha
            while ((linha = reader.readLine()) != null) {
              builder.append(linha);
            }
          } catch (IOException e) {
            e.printStackTrace();
          }
   
          String conteudoArquivo = builder.toString();
          Toast.makeText(MainActivity.this, conteudoArquivo, Toast.LENGTH_SHORT).show();
        }
      });
    }
  });
Exibindo conteúdo do arquivo para o
usuário
Figura 7. Exibindo conteúdo do arquivo para o usuário.

Neste artigo aprendemos a construir um aplicativo Android integrado com o Google Drive. Assim como utilizamos o Google Drive Android API, o usuário pode se conectar a várias APIs fornecidas pelo Google através do Google Play Services e da classe GoogleApiClient, como Google Maps, Wallet, Maps, Google+, Cast, Ads, Games, Location, etc. Esperamos que, desta forma, este artigo abra um leque de possibilidades para o leitor integrar seu aplicativo com diversos serviços Google, possibilitando a criação de apps mais funcionais e poderosos.

Links úteis

Confira também