Mostrando postagens com marcador OAuth. Mostrar todas as postagens
Mostrando postagens com marcador OAuth. Mostrar todas as postagens

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

quinta-feira, 8 de setembro de 2011

Twitter API no Android

Olá povo,

Nunca me interessei em testar APIs para redes sociais por dois motivos: não gosto muito dessa febre; e pensava que era bem trivial ao ponto de ter milhões de tutoriais por aí. Grande engano. Quando fui adicionar suporte ao Twitter em uma aplicação que estou trabalhando, notei que existem algumas etapas 'chatinhas' para realizar essa integração. Não sei se a forma que estou utilizando aqui é a melhor, inclusive, se vocês tiverem sugestões, deixem seus comentários :)

Sem mais "delongas", vamos começar! Nesse post, vou mostrar como realizar o processo de autenticação no Twitter e 'twitar' uma mensagem. Para começar, obviamente você deve ter uma conta comum no Twitter. Para ter acesso aos recursos da API, você (como desenvolvedor) deverá cadastrar sua aplicação no site https://dev.twitter.com. Esse cadastro servirá para a equipe do Twitter saber qual aplicação (e consequentemente o login) responsável pelo cliente que está acessando o Twitter. Isso é similar ao que é feito quando utilizamos a API de Mapas do Google, onde você deve usar sua conta do Google para obter uma chave, para então utilizar o serviço.



O cadastro é bem simples, basta informar: o nome da aplicação, descrição da mesma, URL para contato e uma URL de callback. Este último é mais importante para aplicações web, onde após efetuar o login, a chave de acesso será direcionada.
Uma vez cadastrada, serão geradas alguma informações que serão utilizadas para integração da nossa aplicação com o Twitter. Nós precisaremos da "Consumer Key" e "Consumer Secret".

Como funciona a autenticação no Twitter?
Até meados do segundo semestre de 2010, o acesso ao twitter funcionava basicamente passando o login e a senha em texto plano. Por motivos óbvios de segurança, essa forma foi descontinuada, e passou-se a utilizar a forma de autenticação OAuth. Esse processo é ilustrado na figura abaixo:

Não vou entrar em muitos detalhes sobre esse protocolo, mas basicamente o cliente envia um solicitação de acesso utilizando a consumerKey e consumerSecret para o servidor, o servidor então retorna uma URL para que o usuário se autentique. Nesse momento o usuário insere seu usuário e senha do Twitter. Uma vez passadas essas informações, o servidor valida esses dados e gera um token de acesso, que será usada para toda a comunicação com o Twitter. Esse tone deve ser salvo no cliente para que ele possa se comunicar com o servidor sem ter passar o login e a senha a cada transacção.

Mãos à obra!
De posse do conceito sobre a autenticação do Twitter, vamos implementar nosso exemplo. Ele constará de apenas uma tela, onde o usuário poderá fazer o login/logout no Twitter e atualizar o seu status a partir da mesma. A tela da aplicação é exibida abaixo:


Você pode implementar a comunicação com o Twitter do zero, utilizando JSON. Mas existem algumas APIs prontas que facilitam nosso trabalho. A que utilizarei aqui é a Twitter4J. Baixe a última versão estável, descompacte em algum local do seu HD. Crie um novo projeto Android, e copie o arquivo twitter4j-core-android-X.X.X.jar que está na pasta lib do Twitter4J para a pasta lib do seu projeto Android (essa pasta não é criada por padrão, então crie na raiz do seu projeto). Em seguida, adicione esse JAR no Build-Path do Eclipe clicando com o botão direito sobre o arquivo, e depois selecionando Build Path > Add to Build path. Seu projeto deve ficar como na figura abaixo.



Vamos ao código :) Vou começar pelo AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="ngvl.android.twitter"
android:versionCode="1"
android:versionName="1.0">

<uses-sdk android:minSdkVersion="10" />
 
<uses-permission android:name="android.permission.INTERNET"/>

<application
 android:icon="@drawable/icon"
 android:label="@string/app_name">
 <activity
   android:name=".ExemploTwitterActivity"
   android:label="@string/app_name"
   android:noHistory="true">
   <intent-filter>
     <action 
      android:name="android.intent.action.MAIN"/>
     <category 
       android:name="android.intent.category.LAUNCHER"/>
   </intent-filter>
   <intent-filter>
    <action 
      android:name="android.intent.action.VIEW"/>
     <category 
       android:name="android.intent.category.DEFAULT"/>
     <category 
       android:name="android.intent.category.BROWSABLE"/>
     <data android:scheme="nglauber-android"/>
   </intent-filter>
 </activity>
</application>
</manifest>

Aqui nesse arquivo temos algumas coisas muito importantes:
- A tag uses-permission para nossa aplicação poder acessar internet;
- Foi adicionada a propriedade andróide:noHistory à tag activity, para que quando o Browser seja aberto para o usuário fazer o login, ao voltar, ele não crie outra instância dessa activity.
- Essa activity tem dois intent-filter. O primeiro é pra execução normal da aplicação e o segundo é usado pela página do Twitter. Após a autenticação, ele redirecionará para o endereço nglauber-android://?oauthverifier=sua_chave. Estamos informando que essa atividade trata esse "protocolo", dessa forma nossa activity será re-executada.

Agora vamos ver o código da Activity.
package ngvl.android.twitter;

import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.auth.AccessToken;
import twitter4j.auth.RequestToken;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

public class ExemploTwitterActivity extends Activity {

  private final String 
    consumerKey = "SUA CONSUMER KEY";
  private final String 
    consumerSecret = "SUA CONSUMER SECRET";
  private final String 
    CALLBACKURL = "nglauber-android:///";

  private Twitter twitter;

No código acima, as constantes cosumerKey e consumerSecret devem ser preenchidas com as informações do cadastro da sua aplicação no site do Twitter. Já a constante CALLBACKURL deve estar igual a que foi declarada no AndroidManifest.xml.
O atributo twitter proverá acesso ao processo de login e de atualização do status.

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  
  twitter = new TwitterFactory().getInstance();
  twitter.setOAuthConsumer(
    consumerKey, consumerSecret);
}

No onCreate apenas inicializamos o layout da tela, obtemos a instância do Twitter através da classe TwitterFactory e setamos a consumer key e secret.
 public void clickLogin(View v) {
  try {
    AccessToken accessToken = loadAccessToken();
    if (accessToken == null) {
      twitter = new TwitterFactory().getInstance();
      twitter.setOAuthConsumer(
        consumerKey, consumerSecret);
    
      RequestToken requestToken = 
        twitter.getOAuthRequestToken(CALLBACKURL);
 
      String url = requestToken.getAuthenticationURL();
      Intent it = new Intent(
        Intent.ACTION_VIEW, Uri.parse(url));
      it.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
      startActivity(it);
    
      saveRequestData(requestToken.getToken(), 
        requestToken.getTokenSecret());
   
    } else {
      twitter.setOAuthAccessToken(accessToken);
    }
  } catch (Exception e) {
    e.printStackTrace();
    showToast(e.getMessage());
  }
} 

O método acima, iniciará o processo de login e será chamado ao clicar no botão de login. Através do método loadAccessToken (definido mais adiante), ele tenta obter um objeto AccessToken salvo anteriormente. Caso não exista, o processo de login inicia com a obtenção da url para fazermos o login no Twitter. Notem que é passada a constante CALLBACKURL como parâmetro, isso indica qual endereço será chamado quando o login for concluído. Essa é uma URL "fake" que será chamada quando o login for efetuado. Neste momento, o browser redireciona a chave para nglauber-android://?oauthverifier=sua_chave, e o "protocolo" nglauber-android está sendo tratado por nossa atividade, como definido no AndroidManifest.xml.
A flag FLAG_ACTIVITY_NO_HISTORY evita que, ao pressionar back após o login, o browser seja exibido novamente. Após chamar o browser para o login, o token e o token secret da requisição são salvos pelo método saveRequestData (veremos esse método mais adiante).

Ao clicar no botão de login, será exibida a tela de autenticação do Twitter. Aqui é solicitado autorização para que a aplicação que cadastramos no começo desse post tenha acesso a sua conta do Twitter. Digite seu login e senha e clique em "Sign In".


Após realizar o login, a página do Twitter envia a chave para nossa Activity. Tratamos o recebimento dessa chave no método onResume conforme abaixo:
 @Override
protected void onResume() {
  super.onResume();
  
  Uri uri = getIntent().getData();
  if (uri != null) {
    String oauthVerifier = 
      uri.getQueryParameter("oauth_verifier");
 
    try {
      RequestToken requestToken = loadRequestToken();
      AccessToken at = twitter.getOAuthAccessToken(
        requestToken, oauthVerifier);

      saveAccessToken(
        at.getToken(), at.getTokenSecret());

    } catch (TwitterException e) {
      e.printStackTrace();
      showToast(e.getMessage());
    }
  }
}

Quando a atividade é chamada pela página do twitter, uma chave de verificação da requisição de acesso é retornada. Com a requisição e a chave de verificação podemos criar um objeto AccessToken para acessar os recursos do Twitter. O token e o token secret do objeto AccessToken são salvos no método saveAccessToken (definido mais adiante) e, como vimos anteriormente podem ser recuperados quando clicamos no botão de login.

Abaixo temos o método que é chamado ao clicar no botão Tweet.

public void clickTweet(View v) {
  try {
    if (loadAccessToken() != null){
      EditText edt = 
        (EditText) findViewById(R.id.editText1);
      String tweet = edt.getText().toString();

      twitter.updateStatus(tweet);
      showToast("Status atualizado com sucesso!");

    } else {
      showToast("Faça o login antes de Twittar");
    }
  } catch (TwitterException e) {
      e.printStackTrace();
      showToast(e.getMessage());
  }
}

Nada de mais aqui. Apenas pega o conteúdo da caixa de texto e manda alterar o status do objeto Twitter.

Abaixo temos método auxiliares que utilizamos no nosso código. O primeiro exibe um Toast na tela.
private void showToast(String s){
  Toast.makeText(this, s, Toast.LENGTH_LONG).show();
}

Os métodos que salvam e recuperam as chaves de solicitação (RequestToken) e de acesso (AccessToken) estão definidos abaixo. Para ambos são utilizados SharedPreferences.
 
private RequestToken loadRequestToken(){
  SharedPreferences prefs = PreferenceManager.
    getDefaultSharedPreferences(this);
  String reqToken = 
    prefs.getString("request_token", null);
  String reqTokenSecret = 
    prefs.getString("request_tokensecret", null);
  
  return new RequestToken(reqToken, reqTokenSecret);
}
 
private void saveRequestData(
  String requestToken, String requestTokenSecret){

  SharedPreferences prefs = PreferenceManager.
    getDefaultSharedPreferences(this);
  SharedPreferences.Editor editor = prefs.edit();

  editor.putString(
    "request_token", requestToken);
  editor.putString(
    "request_tokensecret", requestTokenSecret);

  editor.commit();  
}
 
private AccessToken loadAccessToken() {
  SharedPreferences prefs = PreferenceManager.
    getDefaultSharedPreferences(this);
  String acToken = 
    prefs.getString("access_token", null);
  String acTokenSecret = 
    prefs.getString("access_tokensecret", null);

  if (acToken != null || acTokenSecret != null){
    return new AccessToken(acToken, acTokenSecret);
  }
  return null;
}
 
private void saveAccessToken(
  String accessToken, String accessTokenSecret) {

  SharedPreferences prefs =
    PreferenceManager.getDefaultSharedPreferences(this);

  SharedPreferences.Editor editor = prefs.edit();

  editor.putString(
    "access_token", accessToken);
  editor.putString(
    "access_tokensecret", accessTokenSecret);

  editor.commit();
}

Por fim, o método que faz o logout, limpa os valores da sharedpreference de AccessToken, o que vai forçar a realização de um novo login.
public void clickLogout(View v) {
  saveAccessToken(null, null);
}


É isso pessoal, qualquer dúvida, deixem seus comentários.

4br4ç05,
nglauber