Ludovic ROLAND

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

Mettre en place la connexion Google+ dans une application Android

15 février 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 et plus récemment, un tutoriel permettant de mettre en place le Twitter OAuth toujours dans une application Android.

Comme je le disais dans l’introduction de l’article consacré à l’intégration du Twitter OAuth, c’est généralement le trio Facebook, Twitter et Google+ qui est proposé aux utilisateurs afin de pouvoir se connecter à un service via une application tierce. La connexion via un compte Google étant la dernière que nous n’ayons pas vus, je vous propose de s’y atteler au travers de cet article !

Création d’un projet Google

A l’image de ce que nous avions fait pour mettre en place le Facebook Connect et le Twitter OAuth, il convient de déclarer notre future application mobile auprès de Google et surtout, lui permettre d’accéder à la fonctionnalité de connexion au travers de la plate-forme Google+.

Pour ce faire, rendez-vous sur la console développeur de Google, et cliquez sur le bouton Créer un projet.

Un formulaire s’ouvre alors vous demandant de renseigner le nom du projet ainsi qu’un identifiant (que Google peut générer aléatoirement). Une fois le formulaire rempli, cliquez sur Créer.

Vous devriez alors automatiquement atterrir sur le tableau de bord de votre projet. Cliquez sur API et authentification pour accéder à l’écran permettant de gérer les fonctionnalités de notre projet :

Dans la liste des fonctionnalités non activées, repérez celle qui se nomme Google+ API :

Cliquez alors sur le bouton situé à droite pour activer la fonctionnalité :

Nous allons maintenant configurer la connexion Google de notre application. Pour ce faire, rendez-vous dans le sous menu Identifiants puis cliquez sur Créer un identifiant client :

La boite de dialogue suivante devrait s’ouvrir. Sélectionnez Application installée puis cliquez sur Configurer l’écran d’autorisation :

L’écran d’autorisation est l’écran qui s’affichera à votre utilisateur avec les permissions de votre application, le logo de l’application, etc. Il convient donc de le configurer avec au minimum :

  • une adresse e-mail
  • un nom de produit

Les autres champs sont facultatifs. Une fois la configuration terminée, cliquez sur Enregistrer :

La précédente boite de dialogue devrait alors se rouvrir en vous demandant cette fois plus d’informations sur votre application à savoir :

  • le type de l’application (Android dans notre cas)
  • le nom de package de l’application
  • le SHA1 de la clef qui va signer votre application

Renseignez les différents champs sans oublier d’activer les liens profonds puis cliquez sur Créer un identifiant client :

Pour rappel, voici la ligne de commande permettant de connaître le SHA1 de la clef qui va signer votre application (ici appliqué à la clef de debug Android) :

keytool -list -v -keystore "%USERPROFILE%\.android\debug.keystore" -alias androiddebugkey -storepass android -keypass android

Maintenant que notre projet Google est configuré, 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 et sélectionnez le template d’application Blank Activity with Fragment.

Les bibliothèques tierces

Lorsque nous avions mis en place le Facebook Connect ou encore le Twitter OAuth, nous avions utilisé respectivement le SDK Facebook officiel et la bibliothèque Opensource twitter4j. Pour utiliser les services de Google, nous allons devoir utiliser les Google Play Services et plus précisément le module dédié à Google+. Ajoutez alors la ligne suivante à la section dependencies de votre fichier build.gradle pour pouvoir importer et utiliser la bibliothèque dans votre projet :

compile 'com.google.android.gms:play-services-plus:6.5.+'

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

Mise en place de la connexion via un compte Google

Un peu de configuration

Maintenant que tout est en place, nous allons pouvoir nous attaquer à la mise en place de la connexion grâce à un compte Google 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 la permission suivante dans le fichier AndroidManifest.xml de votre application Android :

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

Nous allons également ajouter quelques permissions permettant à notre application Android d’accéder aux comptes Google configurés sur le téléphone cible :

<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />

L’interface graphique

Nous allons créer une interface graphique très simple avec trois bouttons :

  • Connexion dont le but sera de lancer la connexion ;
  • Déconnexion dont le but sera de déconnecter l’utilisateur ;
  • Révocation dont le but sera de révoquer l’accès du projet Google au compte de l’utilisateur.

Voici donc le layout de notre application :

<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical"
  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/btnConnect"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:text="Connexion"
  />
  <Button
    android:id="@+id/btnDisconnect"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:text="Déconnexion"
  />
  <Button
    android:id="@+id/btnRevoke"
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    android:text="Révoquer"
  />
</LinearLayout>

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
{

  private Button btnConnect;

  private Button btnDisconnect;

  private Button btnRevoke;

  public PlaceholderFragment()
  {
  }

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

    btnConnect = (Button) rootView.findViewById(R.id.btnConnect);
    btnDisconnect = (Button) rootView.findViewById(R.id.btnDisconnect);
    btnRevoke = (Button) rootView.findViewById(R.id.btnRevoke);

    btnConnect.setOnClickListener(this);
    btnDisconnect.setOnClickListener(this);
    btnRevoke.setOnClickListener(this);

    return rootView;
  }

  @Override
  public void onClick(View view)
  {
    if (view.equals(btnConnect) == true)
    {
      //TODO : connect
    }
    else if (view.equals(btnDisconnect) == true)
    {
      //TODO : disconnect
    }
    else if (view.equals(btnRevoke) == true)
    {
      //TODO : revoke
    }
  }

}

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

La connexion via Google

Comme pour une connexion via Facebook, la connexion via un compte Google est très simple puisqu’on la délègue à la plate-forme mère. L’originalité demeure dans le cycle de vie que nous allons mettre en place.

En réalité, nous allons tenter de nous connecter immédiatement après ouverture de notre écran. L’utilisateur n’ayant pas accepté que notre projet Google accède à ses informations, la connexion va échouer, le tout de manière complètement silencieuse. Quand l’utilisateur cliquera sur le bouton de connexion, nous lancerons alors simplement une résolution des problèmes (il s’agit d’une méthode fournie par les Google Play Services) qui s’occupera automatiquement de connecter l’utilisateur en lui afficheant les écrans qui conviennent comme par exemple le choix d’un compte Google ou la confirmation d’accès aux informations relatives au compte.

Pour tenter de se connecter automatiquement, il convient d’utiliser un objet GoogleApiClient que nous allons initialiser dans la méthode onCreateView de notre fragment, grâce à un GoogleApiClient.Builder.

Nous allons donner plusieurs informations à ce GoogleApiClient.Builder comme par exemple une API, un scope, une Callback et un Listener :

final Builder googleApiClientBuilder = new GoogleApiClient.Builder(getActivity());
googleApiClientBuilder.addConnectionCallbacks(this);
googleApiClientBuilder.addOnConnectionFailedListener(this);
googleApiClientBuilder.addApi(Plus.API);
googleApiClientBuilder.addScope(Plus.SCOPE_PLUS_LOGIN);

googleApiClient = googleApiClientBuilder.build();

Afin que le code fourni ci-dessus compile, il convient que notre fragment implémente les interfaces ConnectionCallbacks et OnConnectionFailedListener. La conséquence est que nous devons maintenant implémenter les méthodes onConnected, onConnectionSuspended et onConnectionFailed.

Comme je vous le disais tout à l’heure, nous allons tenter de connecter notre utilisateur automatiquement au lancement de notre écran. Pour ce faire, nous allons surcharger la méthode onStart du fragment et utiliser la méthode connect de notre objet GoogleApiClient :

@Override
public void onStart()
{
  super.onStart();

  if (googleApiClient != null)
  {
    googleApiClient.connect();
  }
}

Cette tentative va lamentablement échouer, car nous n’avons pas encore demandé les droits nécessaires à notre utilisateur. Nous allons donc atterrir dans la méthode onConnectionFailed. Cette méthode nous fournit un paramètre de type ConnectionResult. Nous allons utiliser ce paramètre pour savoir si Google est capable de résoudre de lui-même le problème à l’origine de l’erreur de connexion grâce à la méthode hasResolution. Si le problème ne peut pas être résolu, nous allons afficher un message d’erreur à l’utilisateur :

@Override
public void onConnectionFailed(ConnectionResult connectionResult)
{
  if (connectionResult.hasResolution() == false)
  {
    GooglePlayServicesUtil.getErrorDialog(connectionResult.getErrorCode(), getActivity(), 0).show();
  }
  else
  {
    //TODO
  }
}

Dans le cas contraire, nous allons vérifier que nous n’avons pas nous-même tenté de lancer une connexion et le cas échéant, sauvegarder le paramètre ConnectionResult pour l’utiliser plus tard. Il convient donc de créer deux attributs à notre fragment, un booléen googleIntentInProgress ainsi qu’un objet de type ConnectionResult portant par exemple le nom de googleConnectionResult :

@Override
public void onConnectionFailed(ConnectionResult connectionResult)
{
  if (connectionResult.hasResolution() == false)
  {
    GooglePlayServicesUtil.getErrorDialog(connectionResult.getErrorCode(), getActivity(), 0).show();
  }
  else
  {
    if (googleIntentInProgress == false)
    {
      googleConnectionResult = connectionResult;
    }
  }
}

Nous allons maintenant vérifier que cette erreur de connexion n’est pas arrivée suite au clic sur le bouton Connexion de notre application. Pour cela, il convient d’utiliser un booléen qui prendra la valeur true dès que l’utilisateur va cliquer sur le bouton. Ajoutez alors l’attribut au fragment et nommez le, par exemple, btnConnectClicked. N’oubliez pas d’enregistrer le clic sur le bouton :

@Override
public void onClick(View view)
{
  if (view.equals(btnConnect) == true)
  {
    //TODO : connect
    btnConnectClicked = true;
  }
  else if (view.equals(btnDisconnect) == true)
  {
    //TODO : disconnect
  }
  else if (view.equals(btnRevoke) == true)
  {
    //TODO : revoke
  }
}

Revenons à notre méthode onConnectionFailed. Nous allons tenter de lancer une résolution des erreurs seulement et seulement si l’utilisateur a cliqué sur le bouton de Connexion et non si le problème est survenu après une connexion automatique. Notre méthode devient donc la suivante :

@Override
public void onConnectionFailed(ConnectionResult connectionResult)
{
  if (connectionResult.hasResolution() == false)
  {
    GooglePlayServicesUtil.getErrorDialog(connectionResult.getErrorCode(), getActivity(), 0).show();
  }
  else
  {
    if (googleIntentInProgress == false)
    {
      googleConnectionResult = connectionResult;

      if (btnConnectClicked == true)
      {
        connect();
      }
    }
  }
}

Il convient donc maintenant de créer la fameuse méthode connect qui n’existe pas pour le moment. Cette méthode va tout simplement exploiter notre copie de l’objet ConnectionResult pour lancer la correction automatique des problèmes grâce à la méthode startResolutionForResult. Cette méthode va donner la main au système et, à la manière d’un Facebook, faire sa propre tambouille. Nous devrons alors vérifier que l’état de connexion de l’utilisateur dans la méthode onActivityResult. Afin de distinguer le retour suite à une tentative de connexion, j’ai déclaré une constante me permettant de donner un numéro unique à ma requête :

private static final int GOOGLE_SIGN_IN = 9000;

Finalement, dans la fameuse méthode connect, il conviendra de mettre à jour nos booléens nous permettant de savoir que nous venons de lancer une connexion :

private void connect()
{
  if (googleConnectionResult != null && googleConnectionResult.hasResolution() == true)
  {
    try
    {
      googleIntentInProgress = true;
      googleConnectionResult.startResolutionForResult(getActivity(), PlaceholderFragment.GOOGLE_SIGN_IN);
    }
    catch (SendIntentException exception)
    {
      googleIntentInProgress = false;
      googleApiClient.connect();
    }
  }
}

Comme vous pouvez le constater, si une exception survient, je retente tout de suite une connexion afin de repasser dans la méthode onConnectionFailed et obtenir un objet ConnectionResult à jour.

Le soucis avec ce que nous venons de faire, c’est qu’en réalité, on ne va pas tomber dans la méthode onActivityResult du fragment, mais dans celle de l’activité. Il convient donc, dans l’activité, de retrouver notre fragment afin de lui transmettre les informations et lui déléguer le travail :

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
  super.onActivityResult(requestCode, resultCode, data);
  getSupportFragmentManager().findFragmentById(R.id.container).onActivityResult(requestCode, resultCode, data);
}

Nous allons donc maintenant pouvoir traiter le retour d’information dans la méthode onActivityResult du fragment. En réalité, ce que nous allons faire c’est tout simplement mettre à jour nos booléens et appeler la méthode connect de notre objet GoogleApiClient. Ce coup-ci, nous devrions plus tomber dans la méthode onConnectionFailed. Cependant, nous allons sécuriser notre appel à la méthode connect en vérifiant qu’une connexion n’est pas déjà en cours grâce à la méthode isConnecting du même objet :

private static final int GOOGLE_SIGN_IN = 9000;

Finalement, dans la fameuse méthode connect il conviendra de mettre à jour nos booléens nous permettant de savoir que nous venons de lancer une connexion :

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

  if (requestCode == PlaceholderFragment.GOOGLE_SIGN_IN)
  {
    btnConnectClicked = false;
    googleIntentInProgress = false;

    if (googleApiClient.isConnecting() == false)
    {
      googleApiClient.connect();
    }
  }
}

Pour le moment, nous n’avons traité que le cas d’une tentative de connexion automatique, mais si nous appuyons sur notre bouton de connexion, il ne se passe rien ! Nous allons corriger ça tout de suite. Il convient donc d’appeler notre méthode connect. Une nouvelle fois, son appel est protégé en vérifiant qu’une connexion n’est pas déjà en cours grâce à la méthode isConnecting de l’objet GoogleApiClient :

@Override
public void onClick(View view)
{
  if (view.equals(btnConnect) == true)
  {
    if (googleApiClient.isConnecting() == false)
    {
      btnConnectClicked = true;
      connect();
    }
  }
  else if (view.equals(btnDisconnect) == true)
  {
    //TODO : disconnect
  }
  else if (view.equals(btnRevoke) == true)
  {
    //TODO : revoke
  }
}

Comme je vous le disais tout à l’heure, nous devrions plus tomber dans la méthode onConnectionFailed. Nous allons maintenant tomber dans la méthode onConnected dans laquelle il convient de mettre à jour quelques booléens :

@Override
public void onConnected(Bundle bundle)
{
  btnConnectClicked = false;
}

Il ne nous reste plus qu’à compléter la méthode onConnectionSuspended. Cette méthode est appelée quand la connexion a été suspendue, nous allons donc automatiquement tenter une reconnexion :

@Override
public void onConnectionSuspended(int i)
{
  googleApiClient.connect();
}

Si vous exécutez l’application Android et que vous appuyez sur le bouton Connexion, vous devriez alors voir les boites de dialogues suivantes s’afficher à l’écran :

La déconnexion

Pour la déconnexion, nous allons créer une méthode disconnect que nous allons appeler depuis le clic sur le bouton Déconnexion de notre application :

@Override
public void onClick(View view)
{
  if (view.equals(btnConnect) == true)
  {
    if (googleApiClient.isConnecting() == false)
    {
      btnConnectClicked = true;
      connect();
    }
  }
  else if (view.equals(btnDisconnect) == true)
  {
    disconnect();
  }
  else if (view.equals(btnRevoke) == true)
  {
    //TODO : revoke
  }
}

Dans cette méthode, nous allons faire deux choses :

  • nous déconnecter ;
  • supprimer l’utilisation d’un compte par défaut.

La déconnexion se fait grâce à la méthode disconnect de l’objet GoogleApiClient tandis que la suppression de l’utilisation d’un compte par défaut se fait grâce à la méthode statique clearDefaultAccount de l’objet AccountApi. Cette méthode statique prend pour paramètre notre objet GoogleApiClient. Bien évidemment, avant de déconnecter l’utilisateur, il convient de vérifier qu’il est bien connecté :

private void disconnect()
{
  if (googleApiClient.isConnected() == true)
  {
    Plus.AccountApi.clearDefaultAccount(googleApiClient);
    googleApiClient.disconnect();
  
    Toast.makeText(getActivity(), "Deconnexion réussie", Toast.LENGTH_SHORT).show();
  }
}

La révocation

Pour la révocation, nous allons créer une méthode revoke que nous allons appeler depuis le clic sur le bouton Revoquer de notre application :

@Override
public void onClick(View view)
{
  if (view.equals(btnConnect) == true)
  {
    if (googleApiClient.isConnecting() == false)
    {
      btnConnectClicked = true;
      connect();
    }
  }
  else if (view.equals(btnDisconnect) == true)
  {
    disconnect();
  }
  else if (view.equals(btnRevoke) == true)
  {
    revoke();
  }
}

Dans cette méthode, nous allons faire trois choses :

  • nous déconnecter ;
  • révoquer l’accès du projet Google à notre compte ;
  • supprimer l’utilisation d’un compte par défaut.

La déconnexion et la révocation se font en même temps, grâce à la méthode statique revokeAccessAndDisconnect de l’objet AccountApi. La suppression d’un compte par défaut quant à elle, se fait toujours grâce à la méthode statique clearDefaultAccount de l’objet AccountApi. Ces deux méthodes statiques prennent pour paramètre notre objet GoogleApiClient. Une nouvelle fois, il convient de vérifier qu’il est bien connecté :

private void revoke()
{
  if (googleApiClient.isConnected() == true)
  {
    Plus.AccountApi.clearDefaultAccount(googleApiClient);
    Plus.AccountApi.revokeAccessAndDisconnect(googleApiClient);
    
    Toast.makeText(getActivity(), "Révocation réussie", Toast.LENGTH_SHORT).show();
  }
}

Accéder à l’e-mail de l’utilisateur

Je vous propose ici un petit bonus. Une fois l’utilisateur connecté, nous allons afficher son adresse e-mail grâce un Toast. Pour récupérer l’adresse e-mail de l’utilisateur, nous allons créer une méthode getProfile que nous allons appeler depuis la méthode onConnected, qui devient donc :

@Override
public void onConnected(Bundle bundle)
{
  btnConnectClicked = false;
  getProfile();
}

La récupération de l’adresse e-mail de l’utilisateur est en réalité très simple et se fait en deux étapes :

  • récupération du profil de l’utilisateur ;
  • récupération du nom du compte qui est en réalité l’adresse e-mail de l’utilisateur.

La récupération du profil de l’utilisateur se fait via la méthode statique getCurrentPerson de l’objet PeopleApi qui prend pour paramètre un objet GoogleApiClient. Il convient bien évidemment de traiter correctement les potentielles erreurs et exceptions :

private void getProfile()
{
  try
  {
    if (Plus.PeopleApi.getCurrentPerson(googleApiClient) != null)
    {
      //TODO
    }
    else
    {
      Toast.makeText(getActivity(), "Impossible de récupérer le profil", Toast.LENGTH_SHORT).show();
    }
  }
  catch (Exception exception)
  {
    Toast.makeText(getActivity(), "Une erreur est survenue", Toast.LENGTH_SHORT).show();
  }
}

Pour récupérer le nom de compte Google, il convient d’utiliser la méthode statique getAccountName de l’objet AccountApi. Pour ne pas changer, cette méthode prend pour paramètre un objet GoogleApiClient. Vous êtes maintenant en mesure d’afficher l’adresse e-mail de l’utilisateur :

private void getProfile()
{
  try
  {
    if (Plus.PeopleApi.getCurrentPerson(googleApiClient) != null)
    {
      final String email = Plus.AccountApi.getAccountName(googleApiClient);

      if (email != null && "".equals(email) == false)
      {
        Toast.makeText(getActivity(), email, Toast.LENGTH_SHORT).show();
      }
      else
      {
        Toast.makeText(getActivity(), "E-mail vide", Toast.LENGTH_SHORT).show();
      }
    }
    else
    {
      Toast.makeText(getActivity(), "Impossible de récupérer le profil", Toast.LENGTH_SHORT).show();
    }
  }
  catch (Exception exception)
  {
    Toast.makeText(getActivity(), "Une erreur est survenue", Toast.LENGTH_SHORT).show();
  }
}

Voici une capture d’écran de ce que vous devriez avoir :

Télécharger le projet

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

Commentaires