Ludovic ROLAND

Blog technique sur mes expériences de développeur.

Mettre en place le Twitter OAuth dans une application Android

25 janvier 2015

Il y a plusieurs mois, je vous proposais un tutoriel permettant de mettre en place le Facebook Connect au sein de votre application Android.

Généralement, le trio Facebook, Twitter et Google+ est proposé afin de pouvoir se connecter à un service via une application tierce. Aujourd’hui, nous allons donc nous attaquer l’intégration du Twitter OAuth dans une application Android.

Création d’une application Twitter

A l’image de ce que nous avions fait pour mettre en place le Facebook Connect dans le précédent article, nous allons également, dans le cas de Twitter, associer notre application Android à une application Twitter.

Pour ce faire, rendez-vous sur la partie du site de Twitter dédiée aux applications, et cliquez sur le bouton Create New App.

Un formulaire s’ouvre alors vous demandant de renseigner les premières informations relatives à votre application :

  • un nom ;
  • une description ;
  • le site internet associé à votre application ;
  • une URL de Callback.

Voici par exemple le formulaire rempli de l’application créée dans le cadre de ce tutoriel :

Une fois le formulaire rempli, acceptez les conditions d’utilisation puis cliquez sur Create your Twitter application.

Vous devriez alors être automatiquement redirigé vers le tableau de bord de votre application comme en témoigne la capture d’écran ci-dessous :

A partir de ce tableau de bord, nous allons être en mesure de modifier et/ou affiner les paramètres de notre application comme par exemple :

  • son icône ;
  • son nom ;
  • l’associée à une organisation ;
  • ses droits ;
  • etc.

La première chose à faire est de se rendre dans l’onglet Settings afin de cocher la case Allow this application to be used to Sign in with Twitter. Dans cette option cochée, nous serions pas en mesure de mettre en place le Twitter OAuth dans notre application Android. N’oubliez pas de valider le changement en cliquant que le bouton Update Settings en bas de page.

Finalement, nous allons changer les droits de notre application pour lui permettre de tout faire. Aussi, nous ne serons pas embêter par la suite. Rendez-vous dans l’onglet Permissions* et cochez l’option Read, Write And Access direct messages. Une nouvelle fois, n’oubliez pas de valider le changement en cliquant que le bouton Update Settings en bas de page.

Maintenant que notre application Twitter est configurée, nous allons pouvoir attaquer le développement de notre application Android.

Création de l’application Android

L’application Android de base

Dans Android Studio, créez alors un nouveau projet Android grâce à l’assistant graphique.

Les bibliothèques tierces

Lorsque nous avions mis en place le Facebook Connect dans un précédent article, nous avions utilisé le SDK Facebook officiel. Dans le cadre de la mise en place du Twitter OAuth, nous n’allons pas utiliser de SDK officiel, mais la bibliothèque Opensource twitter4j (comprendre Twitter for java).

Cette bibliothèque étant disponible sur Maven Central, il convient d’ajouter la ligne suivante à la section dependencies de votre fichier build.gradle pour pouvoir importer et utiliser la bibliothèque dans votre projet :

compile 'org.twitter4j:twitter4j-core:4.0.+'

Notez que si nous allons utiliser cette bibliothèque uniquement pour mettre en place le Twitter OAuth dans notre application, elle est en réalité capable de faire beaucoup plus, comme par exemple :

  • envoyer un Tweet ;
  • rapatrier la timeline ;
  • etc.

Maintenant que l’unique bibliothèque que nous allons utiliser est en place, nous allons pouvoir nous attaquer à la programmation Android !

Mise en place du Twitter OAuth

Un peu de configuration

Maintenant que notre application est en place, nous allons mettre en place le Twitter OAuth à proprement parlé au sein de notre application Android. Mais avant ça, il nous reste encore quelques petites configurations à faire.

Tout d’abord, nous devons autoriser notre application à accéder à internet. Aussi, il convient de rajouter les lignes suivantes dans le fichier AndroidManifest.xml de votre application Android :

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

Nous allons également rajouter les clefs relatives à l’application Twitter que nous avons créé plus tôt dans ce tutoriel dans le fichier strings.xml. Aussi, il convient d’ajouter les champs suivants :

  • Consumer Key (API Key) ;
  • Consumer Secret (API Key).

Ces deux informations sont disponibles dans l’onglet Keys And Access Tokens de votre application Twitter :

Au niveau du fichier strings.xml, vous devriez alors avoir quelque chose comme ça :

<string name="consumer_key">88********</string>
<string name="consumer_secret">UM*****</string>

L’interface graphique

Nous allons faire les choses très simplement, ainsi, je remplace juste la TextView affichant “Hello world” qui a été générée automatiquement un bouton :

<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:paddingLeft="@dimen/activity_horizontal_margin"
  android:paddingRight="@dimen/activity_horizontal_margin"
  android:paddingTop="@dimen/activity_vertical_margin"
  android:paddingBottom="@dimen/activity_vertical_margin"
>
  <Button
    android:id="@+id/twitterConnect"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Se connecter via Twitter"
/>
</RelativeLayout>

Il ne nous reste ensuite plus qu’à capturer l’évènement du clic au niveau du fragment en implémentant l’interface OnClickListener :

public static class PlaceholderFragment
    extends Fragment
    implements OnClickListener
{

  public PlaceholderFragment()
  {
  }

  @Override
  public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
  {
    final View rootView = inflater.inflate(R.layout.fragment_main, container, false);
    final Button twitterConnect = (Button) rootView.findViewById(R.id.twitterConnect);
    twitterConnect.setOnClickListener(this);
    return rootView;
  }

  @Override
  public void onClick(View view)
  {
    //TODO
  }

}

Il conviendra donc de lancer le processus de connexion dans la méthode onClick. En attendant, notre application ressemble à ça :

La connexion à Twitter

En soit, lancer la connexion à Twitter est très simple puisqu’on la délègue à la plate-forme Twitter. Aussi, il convient simplement de lancer une Activity un peu personnalisé. Nous allons donc dans un premier temps uniquement nous concentrer sur l’Intent.

Etape 1 : Utilisation d’un ConfigurationBuilder

La première étape consiste à utiliser un objet ConfigurationBuilder auquel il convient de renseigner deux choses : la Consumer Key et la **Consumer Secret **.

final ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder.setOAuthConsumerKey(getString(R.string.consumer_key));
configurationBuilder.setOAuthConsumerSecret(getString(R.string.consumer_secret));

Etape 2 : Utilisation d’une TwitterFactory

La seconde étape consiste à obtenir un objet Twitter. Pour ce faire, nous devons utiliser une TwitterFactory qui, dans son constructeur, accepte un objet de type Configuration. Pour obtenir cet objet, il convient Configuration il convient d’appeler la méthode build() de notre ConfigurationBuilder créé lors de l’étape précédente :

final TwitterFactory twitterFactory = new TwitterFactory(configurationBuilder.build());

Finalement, pour obtenir notre objet Twitter, il convient de faire appel à la méthode getInstance() de notre TwitterFactory :

final Twitter twitter = twitterFactory.getInstance();

Etape 3 : Construction de l’Intent

Dans cette troisième étape, nous allons afficher le formulaire de connexion à Twitter. Ce formulaire sera affiché dans une WebView au sein d’une nouvelle Activity. L’objectif de cette étape est de donc de récupérer l’URL du formulaire à afficher dans la WebView et passer l’information à la nouvelle Activity.

Pour obtenir l’URL du formulaire de connexion, il convient de récupérer un objet RequestToken puis de faire appel à la méthode getAuthenticationURL(). La récupération de l’objet RequestToken est possible grâce à la méthode getOAuthRequestToken() de l’objet Twitter que nous avons créé dans l’étape précédente.

A noter que nous allons également passer à la méthode getOAuthRequestToken() une fausse URL de callback, ce qui nous permettra plus tard dans cet article d’intercepter le résultat de la validation du formulaire d’authentification.

try
{
  final RequestToken requestToken = twitter.getOAuthRequestToken("twitter-callback:///");
  final String url = requestToken.getAuthenticationURL();
}
catch (TwitterException exception)
{
  exception.printStackTrace();
}

Maintenant que nous avons notre URL, nous pouvons construire notre Intent :

final Intent intent = new Intent(getActivity(), LoginTwitterActivity.class);
intent.putExtra("AuthenticationURL", url);

Comme vous pouvez le constater, nous passons l’URL dans les extras. L’activité LoginTwitterActivity n’existe pas pour le moment, mais nous allons la créer dans l’étape suivante.

Finalement, pour terminer cette troisième étape, il convient d’appeler la méthode startActivityForResult(). En effet, cela nous permettra de vérifier si l’utilisateur est bien connecté dans la méthode onActivityResult() du fragment.

La méthode startActivityForResult() prend deux paramètres : l’Intent et un code de requête, ce qui nous permettra d’identifier notre retour dans la méthode onActivityResult() du fragment, par exemple le nombre 3. :)

Vous devriez alors avoir quelque chose comme ça :

startActivityForResult(intent, 3);

Finalement, tout le code que nous venons d’écrire ne doit pas s’exécuter dans le thread de l’UI car des requêtes réseaux sont faites. Il convient alors d’encapsuler tout ça dans une AsyncTask ou un Thread. J’ai choisi d’utiliser un Thread. Mon code est alors le suivant :

new Thread(new Runnable()
{
  @Override
  public void run()
  {
    final ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
    configurationBuilder.setOAuthConsumerKey(getString(R.string.consumer_key));
    configurationBuilder.setOAuthConsumerSecret(getString(R.string.consumer_secret));

    final TwitterFactory twitterFactory = new TwitterFactory(configurationBuilder.build());
    final Twitter twitter = twitterFactory.getInstance();

    try
    {
      final RequestToken requestToken = twitter.getOAuthRequestToken("twitter-callback:///");
      final String url = requestToken.getAuthenticationURL();
      final Intent intent = new Intent(getActivity(), LoginTwitterActivity.class);
      intent.putExtra("AuthenticationURL", url);

      startActivityForResult(intent, 3);
    }
    catch (TwitterException exception)
    {
      exception.printStackTrace();
    }
  }
}).start();

Etape 4 : Le formulaire d’authentification

Nous allons donc nous attaquer à la création du formulaire d’authentification. Comme je vous le disais précédemment, il s’agit en réalité tout simplement d’une Activity (ou d’un Fragment) qui affiche le fameux formulaire dans une WebView.

A l’image de notre MainActivity, je vous propose de créer une LoginTwitterActivity qui porte un LoginTwitterFragment :

public class LoginTwitterActivity
    extends Activity
{

  public static class LoginTwitterFragment
      extends Fragment
  {

    public LoginTwitterFragment()
    {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
      final View rootView = inflater.inflate(R.layout.fragment_login_twitter, container, false);
      return rootView;
    }

  }

  @Override
  protected void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    if (savedInstanceState == null)
    {
      getFragmentManager().beginTransaction().add(R.id.container, new LoginTwitterFragment()).commit();
    }
  }

}

Concernant les layouts, celui de LoginTwitterActivity est le même que MainActivity, à savoir :

<FrameLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:id="@+id/container"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
/>

tandis que celui de LoginTwitterFragment ressemble à ça :

<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
>
  <WebView
    android:id="@+id/loginWebView"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
/>
</RelativeLayout>

Finalement, n’oubliez pas de déclarer cette nouvelle Activity dans votre fichier AndroidManifest.xml :

<activity android:name=".LoginTwitterActivity" />

Dans le LoginTwitterFragment, il convient maintenant de récupérer la référence de notre WebView et de lui demander de charger l’URL permettant d’accéder au formulaire d’authentification. Pour cela, rendez-vous dans la méthode onCreateView() du fragment :

final View rootView = inflater.inflate(R.layout.fragment_login_twitter, container, false);
final WebView webview = (WebView) rootView.findViewById(R.id.loginWebView);

final String url = getActivity().getIntent().getStringExtra("AuthenticationURL");
webview.loadUrl(url);

return rootView;

Finalement, avant de lancer le chargement de l’URL, nous allons donner à notre webView un WebViewClient pour empêcher que le formulaire ne s’ouvre en dehors de l’application. Dans un premier temps, nous n’allons pas surcharger de méthodes :

webview.setWebViewClient(new WebViewClient()
{
});

Si nous exécutions notre application à ce stade et que nous cliquons sur le bouton Se connecter via Twitter, vous devriez voir apparaître le formulaire à l’écran :

Notre URL de callback définie plus tôt dans le cas sera maintenant appelée lorsque l’utilisateur sera identifié ou lorsqu’il cliquera sur le bouton Annuler du formulaire. Il convient donc de surcharger la méthode shouldOverrideUrlLoading() de notre WebViewClient pour intercepter l’appel à la callback et rediriger l’utilisateur vers le précédent écran avec le résultat de l’authentification. La surchage de la méthode shouldOverrideUrlLoading() doit alors ressembler à ça :

webview.setWebViewClient(new WebViewClient()
{
  @Override
  public boolean shouldOverrideUrlLoading(WebView view, String url)
  {
    if (url.contains("twitter-callback:///") == true)
    {
      //TODO
      return true;
    }
    return false;
  }
});

Dans l’url que nous venons de filtrer, il convient maintenant de récupérer le paramètre oauth_verifier :

final Uri uri = Uri.parse(url);
final String oauthVerifierParam = uri.getQueryParameter("oauth_verifier");

Nous allons maintenant créer une Intent à retourner à l’écran précédent dans laquelle il convient de donner en extra le paramètre oauth_verifier. Une fois l’Intent renvoyé, nous pouvons fermer l’écran en cours.

final Intent intent = new Intent();
intent.putExtra("oauth_verifier", oauthVerifierParam);
getActivity().setResult(RESULT_OK, intent);
getActivity().finish();

Le code complet devrait donc être le suivant :

webview.setWebViewClient(new WebViewClient()
{
  @Override
  public boolean shouldOverrideUrlLoading(WebView view, String url)
  {
    if (url.contains("twitter-callback:///") == true)
    {
      final Uri uri = Uri.parse(url);
      final String oauthVerifierParam = uri.getQueryParameter("oauth_verifier");
      final Intent intent = new Intent();
      intent.putExtra("oauth_verifier", oauthVerifierParam);
      getActivity().setResult(RESULT_OK, intent);
      getActivity().finish();

      return true;
    }
    return false;
  }
});

Etape 5 : Vérifier la connexion ou non de l’utilisateur

Nous allons maintenant vérifier que notre utilisateur s’est connecté ou non via son compte Twitter. Pour ce faire, il convient de revenir dans notre MainActivity et plus précisément dans notre PlaceholderFragment dans lequel nous allons surcharger la méthode onActivityResult() :

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
  super.onActivityResult(requestCode, resultCode, data);
}

La première étape consiste à vérifier que le requestCode est bien celui que nous avions envoyé à savoir 3 et que le resultCode est OK :

if(resultCode == Activity.RESULT_OK && requestCode == 3)
{
  //TODO
}

Nous allons maintenant récupérer l’extra oauth_verifier. S’il est différent de null, c’est que l’authentification de l’utilisateur a réussi :

if (oauthVerifierParam != null)
{
  //TODO
}
else
{
  Toast.makeText(getActivity(), "Erreur d'identification", Toast.LENGTH_SHORT).show();
}

Si l’extra oauth_verifier n’est pas null, nous sommes alors en mesure de récupérer des tokens qui nous permettront par la suite de récupérer les informations de l’utilisateur. Pour le moment, afin de récupérer les tokens, il convient que nous puissons réutiliser notre précédent objet Twitter. Il convient donc d’en faire un attribut de notre fragment. Ensuite, nous pourrons appeler la méthode getOAuthAccessToken() qui prend en paramètre notre fameux oauth_verifier. Elle nous renvoie alors un objet AccessToken :

try
{
  final AccessToken accessToken = twitter.getOAuthAccessToken(oauthVerifierParam);
}
catch (TwitterException exception)
{
  exception.printStackTrace();
}

Depuis ce nouvel objet AccessToken nous sommes alors en mesure de récupérer le nom de l’utilisateur grâce à la méthode getScreenName() :

final String name = accessToken.getScreenName();
Toast.makeText(getActivity(), "Bonjour " + name, Toast.LENGTH_SHORT).show();

Une nouvelle fois, il convient de faire quelque modification au code écrit précédemment pour ne pas faire d’appels réseaux dans le thread de l’UI. Notre méthode onActivityResult devient alors :

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
  if (resultCode == Activity.RESULT_OK && requestCode == 3)
  {
    final String oauthVerifierParam = data.getStringExtra("oauth_verifier");

    if (oauthVerifierParam != null)
    {
      new Thread(new Runnable()
      {
        @Override
        public void run()
        {
          try
          {
            final AccessToken accessToken = twitter.getOAuthAccessToken(oauthVerifierParam);
            final String name = accessToken.getScreenName();

            getActivity().runOnUiThread(new Runnable()
            {
              @Override
              public void run()
              {
                Toast.makeText(getActivity(), "Bonjour " + name, Toast.LENGTH_SHORT).show();

              }
            });
            
          }
          catch (TwitterException exception)
          {
            exception.printStackTrace();
          }
        }
      }).start();

    }
    else
    {
      Toast.makeText(getActivity(), "Erreur d'identification", Toast.LENGTH_SHORT).show();
    }
  }

  super.onActivityResult(requestCode, resultCode, data);
}

Si vous lancez l’application et que vous vous identifiez via le formulaire, vous devriez alors voir votre nom d’utilisateur s’afficher à l’écran comme en témoigne la capture d’écran ci-dessous :

La reconnexion automatique

Il est bien évidemment possible de reconnecter automatiquement un utilisateur au démarrage de votre application par exemple. Pour ce faire, il convient en réalité de construire un objet Twitter comme nous l’avons fait un début de ce tutoriel à un détail prêt : il convient de fournir au ConfigurationBuilder un AccessToken ainsi qu’un AccessTokenSecret. Ces deux tokens sont récupérables directement après la dernière connexion de l’utilisateur, grâce aux méthodes getToken() et getTokenSecret() de l’objet AccessToken. Il convient donc de stocker des informations dans les préférences de l’application pour connecter automatiquement votre utilisateur et récupérer ses informations de profil.

La déconnexion

Pour la déconnexion de l’utilisateur, il convient de supprimer des préférences les fameux AccessToken et AccessTokenSecret et de recommencer le processus de conexion pour en générer de nouveaux.

Télécharger le projet

Le projet Android créé pour la rédaction de cet article est disponible sur Github.

Commentaires