quarta-feira, 16 de maio de 2012

Facebook API no Android

Olá povo,

Até a presente data, eu não tenho conta (de verdade) no Facebook. Por isso que até agora não tinha colocado nada relacionado aqui no blog. Mas depois de tantos alunos me perguntando como integrar a aplicação com o Facebook, resolvi deixar registrado como utilizar essa rede social.

Registrando a aplicação do Facebook

O processo é bem similar ao que mostrei no post sobre integração com o Twitter. Devemos primeiro criar/registrar nossa aplicação no Facebook. Para tal, acesse: https://developers.facebook.com/apps, faça o login com uma conta do facebook (aconselho criar uma conta de teste) e clique na opção Create New App. A janela abaixo será exibida:
Digite o nome da aplicação e clique em Continue.

Nota: Por questões de segurança o Facebook pede que você coloque uma informação que te identifique. No meu caso, eu tive que preencher o número do meu celular. Uma vez que cadastrei, o Facebook envia um código de ativação via SMS. Clique no link que foi enviado para o seu aparelho ou digite o código de confirmação para concluir o seu cadastro.

Uma vez que a aplicação está cadastrada, habilite a opção Native Android App.

Nós precisaremos preencher o campo Android Key Hash que é o identificador da nossa aplicação. Ele é gerado a partir do arquivo *.keystore que é usado pra assinar nossa aplicações Android. Aqui, utilizaremos o debug.keystore.
Para gerar esse hash, precisaremos do OpenSSL. Se você estiver no Windows, baixe o openssl-for-windows (http://code.google.com/p/openssl-for-windows/) e descompacte o ZIP na unidade C:

Ainda se você estiver no Windows, abra o prompt de comando, e digite o comando abaixo para adicionar o diretório do openssl no path.
set PATH=%PATH%;C:\openssl-0.9.8k_WIN32\bin

Para gerar a chave digite esse comando:
keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64

Nota 1: o comando keytool é do JDK. Dessa forma, a pasta bin do JDK deve estar no seu PATH, se não tiver, adicione ou digite esse comando dentro da pasta bin do JDK.
Nota 2: se você estiver usando Windows, substitua ~/.android/debug.keystore por C:\Users\seuusuario\.android\debug.keystore (se o nome do seu usuário tiver espaço, coloque o caminho entre aspas)

Copie a hash que foi gerada e cole no campo Android Key Hash e conclua o cadastro da aplicação no Facebook.

Importanto o projeto do Facebook SDK

O código do Facebook SDK está disponível em um repositório GIT, mas você pode baixá-lo no formato ZIP. Acesse https://github.com/facebook/facebook-android-sdk/ e selecione a opção Download this repository as a ZIP file". Terminado o download, descompacte onde desejar.

Vamos criar agora um novo projeto Android com o código que acabamos de baixar. No Eclipse, selecione File | New | Android Project, coloque o nome do projeto de facebooksdk e selecione a opção Create project from existing code. Selecione o diretório onde você descompactou o código do facebooksdk e clique em Finish.

Nota: Se o projeto der erros em relação a anotações @Override, você pode removê-las.

Criando um projeto de exemplo

Vamos criar uma aplicação simples que realizará o login no facebook e atualizará o status do usuário. Crie um novo projeto Android e ao concluir o assistente, vamos adicionar a referência do projeto facebooksdk ao nosso projeto. Para tal, clique com o botão direito sobre o projeto e selecione Properties. No lado esquerdo, selecione Android, e na parte inferior adicione a referência ao projeto clicando em Add... e selecionando o facebooksdk.

Abra o AndroidManifest.xml e adicione a permissão de INTERNET.
<uses-permission 
  android:name="android.permission.INTERNET"/>

Agora, deixe a Activity da sua aplicação da seguinte forma:
public class ExFacebookActivity extends Activity {
 
  private static final String APP_ID = "ID_SUA_APP";
 
  private static final String
    ACCESS_EXPIRES = "access_expires";
  private static final String
    ACCESS_TOKEN = "access_token";

  private Facebook facebook;
  private SharedPreferences prefs;

  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    facebook = new Facebook(APP_ID);
    prefs = getPreferences(MODE_PRIVATE);
    // Carrega a accessToken pra saber se o usuário
    // já se autenticou.
    loadAccessToken();

    // Caso não tenha se autenticado, abre o login
    if(!facebook.isSessionValid()) {

      // Chama a tela de login do facebook
      facebook.authorize(this, 
        new String[] {"publish_stream"},
          new DialogListener() {
            // Login com sucesso, salva o accessToken
            public void onComplete(Bundle values) {
              saveAccessToken();
            }
            // Os métodos abaixo devem ser 
            // implementados para tratatmento dos
            // fluxos alternativos
            public void onFacebookError(
              FacebookError error) {}
    
            public void onError(DialogError e) {}
    
            public void onCancel() {}
          });
    }
  }
    
  public void onActivityResult(
    int requestCode, int resultCode, Intent data) {
    super.onActivityResult(
      requestCode, resultCode, data);
    // A API do Facebook exige essa chamada para 
    // concluir o processo de login.
    facebook.authorizeCallback(
      requestCode, resultCode, data);
  }
  
  // Evento de clique do botão para atualizar o status
  public void updateStatusClick(View v){
    EditText edt = (EditText)
      findViewById(R.id.editText1);
    updateStatus(edt.getText().toString()); 
  }

  private RequestListener requestListener = 
    new RequestListener() {
      public void onMalformedURLException(
        MalformedURLException e, Object state) {
        showToast("URL mal formada");
      }
      public void onIOException(
        IOException e, Object state) {
        showToast("Problema de comunicação");
      }
      public void onFileNotFoundException(
        FileNotFoundException e, Object state) {
        showToast("Recurso não existe");
      }
      public void onFacebookError(
        FacebookError e, Object state) {
        showToast("Erro no Facebook: "+ 
          e.getLocalizedMessage());
      }
      public void onComplete(
        String response, Object state) {
        showToast("Status atualizado");
      }
    };

  // Método que efetivamente atualiza o status
  private void updateStatus(String status) {
    AsyncFacebookRunner runner = 
      new AsyncFacebookRunner(facebook);
  
    Bundle params = new Bundle(); 
    params.putString("message", status);
    runner.request("me/feed", params, "POST", 
      requestListener, null);
  }
 
  private void showToast(final String s){
    final Context ctx = ExemploFacebookActivity.this;
    runOnUiThread(new Runnable() {
      public void run() {
        Toast.makeText(ctx, s, 
          Toast.LENGTH_SHORT).show();
      }
    });
  }

  private void saveAccessToken() {
    SharedPreferences.Editor editor = prefs.edit();
    editor.putString(
      ACCESS_TOKEN, facebook.getAccessToken());
    editor.putLong(
      ACCESS_EXPIRES, facebook.getAccessExpires());
    editor.commit();
  }

  private void loadAccessToken() {
    String access_token = 
      prefs.getString(ACCESS_TOKEN, null);
    long expires = prefs.getLong(ACCESS_EXPIRES, 0);
    if(access_token != null) {
      facebook.setAccessToken(access_token);
    }
    if(expires != 0) {
      facebook.setAccessExpires(expires);
    }
  }
}
A classe acima está comentada, então vou apenas ressaltar os pontos principais. No onCreate da Activity verificamos se o usuário está autenticado. Essa verificação é feita através do AccessToken que é salvo em uma SharedPreferences (veja mais aqui). Caso o usuário não esteja autenticado, chamamos o método autorize passando as permissões que a nossa aplicação requer para acessar determinados recursos. A lista completa de permissões está aqui. Como essa comunicação é feita em uma Thread separada (para não travar a GUI) é utilizado um RequestListener para saber o status da solicitação (sucesso ou falha).
O método updateStatus vai enviar uma mensagem para seu "wall" do Facebook. Nesse método, criamos uma AsyncFacebookRunner que realizará o processo em uma Thread separada. Criamos um objeto Bundle para passar os parâmetros da solicitação, que no nosso caso é apenas a mensagem.
O método request recebe como parâmetro:
- o recurso do facebook que queremos acessar, nesse caso "me/feed" (a lista completa pode ser vista aqui);
- o bundle com os parâmetros; - o método HTTP (GET ou POST); - um RequestListener similar ao que declaramos no login.

Por fim, vou colocar o layout da aplicação:
<LinearLayout 
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent"
  android:orientation="vertical" >

  <EditText
    android:id="@+id/editText1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />

  <Button
    android:id="@+id/button1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Atualizar Status" 
    android:onClick="updateStatusClick"/<

</LinearLayout>
Agora é só executar a aplicação. Ao iniciar pela primeira vez, será exibida a tela de login. Após fazer a autenticação, a tela de login é fechada e voltará automaticamente para aplicação. Aí é só digitar sua mensagem e postar no Facebook :)
Se quiser postar uma foto, basta substituir o método updateStatus por esse aqui:
private void sendPhoto(String status) {
  AsyncFacebookRunner runner = 
    new AsyncFacebookRunner(facebook);
  
  Bitmap image = BitmapFactory.decodeResource(
    getResources(), R.drawable.ic_launcher);

  ByteArrayOutputStream baos = 
    new ByteArrayOutputStream();   

  image.compress(
    Bitmap.CompressFormat.PNG, 100, baos); 

  byte[] bytes = baos.toByteArray();  

  Bundle params = new Bundle(); 
  params.putByteArray("picture", bytes);  
  params.putString("message", status);
     
  runner.request("me/photos", params, 
    "POST", requestListener, null);
} 
Como vocês devem conhecer o Facebook melhor que eu, vocês já devem imaginar que existem muitas outras possibilidades nessa API. Eu só quis mostrar o suficiente para começar. Quem quiser se aprofundar, dá uma olhada aqui.

Qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber

30 comentários:

Adriano Avelar disse...

Post show de bola como sempre. Vim aqui no blog pegar o exemplo de splash screen que vc fez e me deparei com esse exemplo do facebook. Parabéns, continue com seu excelente trabalho Glauber.

Adriano Avelar

Neto Lobo disse...

Parabéns pelo post, foi de grande valia para mim.

Anônimo disse...

Estava procurando alguns exemplos e acabei achando seu blog, fiz os exemplo disponível no site do facebook e também o seu em ambos quando há o aplicativo do facebook instalado no aplicativo não aparece a opção de instalar o aplicativo (nosso teste) você teve este problema ?
leandromilani@gmail.com

Nelson Glauber disse...

Oi Anônimo,

Em nenhum momento esse exemplo abre ou manda instalar a aplicação do Facebook...

4br4ç05,
nglauber

Unknown disse...

Olá, tudo bem?
Muito obrigado pelas dicas ajudaram muito para meus estudos...
Queria saber pode me ajudar numa implementação para aparecer as fotos dos amigos no facebook...
Fiz um código, para exibir os amigos e as fotos dos meus amigos no facebook, o problema que quando estou recebendo as fotos acontece um erro:
java.net.MalformedURLException: Protocol not found: " in android
Se poder me ajudar

Nelson Glauber disse...

Oi Ricardo,

Isso pode ser pq o seu amigo não colocou nenhuma imagem no perfil. Logo, a URL da foto vem vazia.

4br4ç05,
nglauber

Unknown disse...

Nossa, fico muito grato por tentar me ajudar, mas não pode ser só isso, pq ele tras o URL da imagens porém ele tras desse jeito
07-26 12:34:44.840: W/System.err(6708): java.net.MalformedURLException: Protocol not found: {"data":{"is_silhouette":false,"url":"http:\/\/profile.ak.fbcdn.net\/hprofile-ak-snc4\/OMITIDO PELA DUVIDA.jpg"}}

Sabe o que pode ser feito?

Nelson Glauber disse...

Oi Ricardo,

Como você pode observar, esse valor que você está querendo usar como URL, na verdade é um JSON. A propriedade "url" desse JSON é que tem o caminho da foto do usuário.
Faz o seguinte:
String s = url_que_voce_esta_usando;
JSONObject json = new JSONObject(s);
String urlDaFoto = json.getString("url");

4br4ç05,
nglauber

Alysson Bispo disse...

Glauber, quando implementei e testei no emulador do pc pegou beleza. Entretanto, quando testo no meu galaxy s2 e no meu galaxy tab...não funciona...abre a tela do face..e antes que apareça para pedir login e senha...e fecha e volta para a tela principal....alguma ideia ?

Alysson Bispo disse...

Glauber....no emulador pegou direitinho...quando eu testo em um celular que ja tenha o facebook instalado e logado...ele da erro e não aparece a tela de login...e nem consigo recuperar o token...alguma solução ?

José Cláudio disse...

Pow professor, massa. Seguinte, E SE caso eu queira carregar um conteudo string, tipo uma especificação de um telefone celular. E quando o cara fizesse o login no facebook ele ja viesse com o texto do status preenchido com essa especificação trazida de outra activity? É possivel já fazer esse copy and paste "automagicamente"?

Nelson Glauber disse...

Oi Alysson,

Cara, acabei de copiar e colar o código do tutorial acima e fiz o cenário que você falou. Abrir a aplicação do post depois de instalar a aplicação do Facebook e fazer login. Funcionou ok...
Você colocou o ID da sua aplicação na constante APP_ID? E a Android Key Hash você gerou conforme o post?

4br4ç05,
nglauber

Nelson Glauber disse...

Oi Junior,

Acho que não entendi bem a dúvida. Mas você pode colocar qualquer texto no mural do usuário logado. Uma vez que você está logado, pode postar a informação a partir de qualquer tela...

4br4ç05,
nglauber

Neto Lobo disse...

Olá Glauber, o seu exemplo funciona perfeitamente no Android 2.3.7, agora no ICS (Galaxy Note), chama a tela de login, fica carregando depois volta para a activity e não acontece nada.

Alysson Bispo disse...

Glauber, meu problema agora é que eu preciso logar duas vezes naquela tela de login do face que abre para poder entrar...
Na primeira vez ele não loca, zera tudo e pede os dados de novo
Na segunda vez ele loga legal

Alguem ja passou por isso, de ter que logar duas vezes para poder pegar?

Nelson Glauber disse...

Oi Neto,

Testei no meu Tablet Asus com ICS e funcionou.

4br4ç05,
nglauber

Neto Lobo disse...

Olá Glauber acho que o problema aqui é que eu gerei o hash do facebook com a debug.kestore, sendo que minha app leva minha assinatura de desenvolvedor. A solução foi gerar outra key com minha keystore. Deixei a key antiga lá na configuração do Facebook, porque senão dá problema em alguns dispositivos.

Heudes disse...

Hey.. seguinte.. o meu caso é um pouco diferente.. eu gostaria de saber como postar uma foto a partir de um diretório. Ou seja, eu coloco um link do meu computador e o emulador do android posta a foto. Isso é estranho, mas será um teste, pra depois pegar uma foto da galeria de fotos do android, e compartilhar, entendeu?

Nelson Glauber disse...

Oi Heudes,

Cara, já que é só um teste, coloca a foto dentro do emulador (através do file explorer do DDMS) e envia a imagem a partir do diretório do emulador.

4br4ç05,
nglauber

Unknown disse...

parabéns pelo post cara, implementamos no nosso projeto de rede social do 6º periodo da unibratec e ficou massa, pois temos que integrar o mesmo com as principais redes sociais... Obrigado pelo post cara abraços....

Unknown disse...

Cara isso funciona ainda? fui testar agora e não está funcionando por causa do SDK 3.0

Nelson Glauber disse...

Oi Ricardo,

Fiquei sabendo que a API do Facebook mudou. Mas não tive tempo de testar o SDK novo. Mas acho que seguindo o tutorial do site do Facebook dá pra desenrolar.

4br4ç05,
nglauber

Anônimo disse...

Olá Glauber, muito bom o tutorial. Tenho uma dúvida, não sei se vc vai poder me responder, mas é o seguinte: Fiz o passo a passo do seu tutorial, fiz também uma parte a mais para exibir todos os meus amigos do facebook e selecioná-los, só que agora eu queria depois de selecionar um amigo, mencionar o amigo que foi selecionado em um post do facebook. Isso seria possível?

Nelson Glauber disse...

Oi Anônimo,

Já existe uma versão mais nova da API do Facebook. Dá uma olhada nesse tutorial aqui:
https://developers.facebook.com/docs/getting-started/facebook-sdk-for-android/3.0/

4br4ç05,
nglauber

Unknown disse...

Boa tarde, excelente tutorial, mas tive um problema, quando começo a fazer o login e começa a carregar a tela de login fecha e acaba não compartilhando. Da o seguinte erro:

Redirect URL: fbconnect://success?error_code=901&error_message=This+app+is+in+sandbox+mode.++Edit+the+app+configuration+at+http%3A%2F%2Fdevelopers.facebook.com%2Fapps+to+make+the+app+publicly+visible.

Nelson Glauber disse...

Oi Guilherme,

Olha o comentário antes do teu :)

4br4ç05,
nglauber

Hadson Marcelo Gomes disse...

BOm dia ótimo tutorial...
Estou com um problema que nem na china encontrei resposta... e isso está me arrancando os cabelos.
Fiz tudo como manda o figurino, hashkeys, id e o diabo a 4.
Funciona perfeitamente, tanto no emulador, tanto no celular, tablet, com facebook nativo instalado ou nao... uma beleza. Mas vejam a surpresa, quando posto meu app no google play e instalo diretamente de lá, oq acontece? nada... ele chega a abrir uma janelinha linda informanque o app tah querendo permissao e tal.. quando clico em OK ele simplesmente nao faz nada.. como se nem entrasse no callback. Alguem me ajuda? to com o app pronto e garrado com essa merda... com o perdão da palavra... :( To frustrado... vou virar hipiie

Nelson Glauber disse...

Oi Hadson,

Você gerou sua chave a partir do *.keystore da Google Play? Pq se você estiver usando o debug.keystore, realmente não vai funcionar.
Outra coisa que me lembro que tem que configurar é dizer no Facebook que sua app está no Google Play.

4br4ç05,
nglauber

rlaf disse...

Parabéns excelente post.

Eu segui seu post e estou tento problema com um app Android que está usando tanto o FacebookSDK quanto o Twitter4J.

Você já passou por esse problema?

Nelson Glauber disse...

oi rlaf,

Cara, esse post tem quase 2 anos e pelo que eu vi ambas as APIs mudaram desde então, consequentemente o seu uso também. Espero atualizar esse post em breve.

4br4ç05,
nglauber