Inicio Android Google Sign-In en Android

Google Sign-In en Android

por sgoliver
serie-signin

[mensaje-curso]

Aunque muchas aplicaciones siguen utilizando su propio sistema de autenticación de usuarios, son cada vez más las que proporcionan al usuario la posibilidad de loguearse utilizando una cuenta ya existente en algún otro servicio, como por ejemplo Facebook, Twitter o Google. Esto tiene varias ventajas, desde el punto de vista del desarrollador permite aliviar un poco el trabajo al no tener que desarrollar todo un nuevo sistema de creación de cuentas de usuario y autenticación para la aplicación, y de cara al usuario también alivia el trabajo de tener que recordar un nuevo usuario y contraseña :). En este artículo veremos como hacer uso del servicio de autenticación/login de Google (Google Sign-In) en nuestras aplicaciones Android, de forma que el usuario pueda acceder a ella usando cualquier cuenta de Google existente en el dispositivo (o bien crear una nueva).

Al igual que ocurría con la API de Google Maps para Android, para hacer uso del servicio de autenticación de Google es necesario crear previamente un proyecto en la consola de desarrolladores. Aunque podríamos crearlo manualmente como en el caso anterior, en esta ocasión vamos a utilizar otra herramienta que proporciona Google para alguno de sus servicios, que nos ayudará tanto en la creación del proyecto como en la configuración posterior de la aplicación Android para asociarla a éste.

Vamos a acceder para ello a este enlace, desde donde podemos agregar algunos servicios (como el de login o el de analytics) a un proyecto existente o bien, si aún no existe, hacer que la herramienta lo cree automáticamente por nosotros.

habilitar-servicio-google-1

Empezaremos por pulsar el botón «Pick a platform» para indicar qué tipo de aplicación vamos a crear. Indicaremos por supuesto «Android App».

habilitar-servicio-google-2

A continuación el asistente nos pregunta el nombre de la aplicación y el paquete java principal. Si no existe ningún proyecto con dicho nombre en la consola de desarrolladores nos indicará que el proyecto se va a crear automáticamente. Si lo deseamos, desmarcamos la opción de compartir datos con Google y seleccionamos nuestro país o región. Finalmente pulsamos «Choose and configure services» para continuar.

habilitar-servicio-google-3

En el siguiente paso tendremos que elegir el servicio que queremos añadir al proyecto. En nuestro caso vamos a seleccionar Google Sign-In.

habilitar-servicio-google-4

Como ya ocurría con la API de Google Maps, vamos a necesitar indicar la huella SHA-1 del certificado con el que firmamos la aplicación. En el primer capítulo sobre mapas ya vimos cómo obtener este dato. Introducimos la clave SHA-1 y pulsamos «Enable Google Sign-In».

habilitar-servicio-google-5

Tras esto ya deberíamos tener correctamente añadido nuestro servicio al proyecto (aparecerá una check verde sobre el icono de Google Sign-In, por lo que podemos finalizar pulsando sobre «Generate configuration files».

habilitar-servicio-google-6

Y ésta es precisamente la principal diferencia entre lo que nos proporciona este asistente y el que ya vimos al crear el proyecto para Google Maps. Ahora tenemos la posibilidad de descargar un fichero de configuración (llamado google-services.json) que contiene todo lo necesario para configurar nuestra aplicación Android para asociarla con este proyecto que acabamos de crear. En breve veremos cómo, pero por el momento descarguemos el fichero que se nos ofrece.

habilitar-servicio-google-7

Hecho esto ya podemos ir por fin a nuestra herramienta de desarrollo principal, Android Studio. Vamos a crear un proyecto normal, utilizando la plantilla «Empty Activity» y todas las demás opciones por defecto. Y lo primero que vamos a hacer una vez creado es colocar el fichero de configuración «google-services.json» que hemos descargado anteriormente dentro de la carpeta «/app» de nuestro proyecto. Para ello puede resultarnos cómodo pulsar botón derecho sobre la carpeta «app» del proyecto en Android Studio y usar la opción «Show in Explorer» para ir directamente a dicha carpeta en el explorador de archivos de Windows.

Bien, ya tenemos el fichero de configuración colocado en la carpeta correcta, ¿pero cómo vamos a conseguir que Android Studio lo «consulte» para configurar automáticamente nuestra aplicación? Para esto vamos a añadir el plugin de Google Services para Gradle en nuestro proyecto. Esto se consigue añadiendo un par de lineas a los ficheros build.gradle de nuestro proyecto, tanto el situado a nivel de proyecto como el del módulo principal.

ficheros-build-gradle

En el primero de ellos (nivel de proyecto) añadiremos la siguiente cláusula classpath al final de la sección de dependencias:

dependencies {
        //...
        classpath 'com.google.gms:google-services:3.0.0'
    }

En el segundo (nivel de módulo) añadiremos al final del fichero la siguiente instrucción apply plugin:

apply plugin: 'com.google.gms.google-services'

Aprovecharemos que estamos en este fichero para añadir también, como ya es habitual, la referencia a la librería específica de autenticación de Google Play Services a la sección de dependencias:

dependencies {
    //...
    compile 'com.google.android.gms:play-services-auth:9.4.0'
}

Con esto ya habríamos terminado con los preparativos, por lo que podemos empezar a escribir nuestra aplicación. En este caso, la aplicación de ejemplo que vamos a crear será muy sencilla. Constará únicamente de tres botones, el primero de ellos será el de login con Google, y los otros dos nos servirán para hacer logout y para revocar o desconectar completamente la cuenta utilizada de la aplicación. Adicionalmente añadiremos un par de campos de texto para mostrar el nombre e email del usuario logueado cuando aplique. Veamos cómo quedaría el layout:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="net.sgoliver.android.signin.MainActivity">

    <LinearLayout android:id="@+id/lytBotones"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <com.google.android.gms.common.SignInButton
            android:id="@+id/sign_in_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <Button
            android:id="@+id/sign_out_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/sign_out" />

        <Button
            android:id="@+id/revoke_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/desconectar" />

    </LinearLayout>

    <TextView android:id="@+id/txtNombre"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/lytBotones" />

    <TextView android:id="@+id/txtEmail"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/txtNombre" />

</RelativeLayout>

Nos dirigiremos ahora a nuestra clase java principal. Lo primero que tendremos que hacer será crear un objeto de tipo GoogleApiClient, de forma similar a como ya vimos en el apartado sobre localización, al que añadiremos la API de Google Sign-In. Pero en este caso vamos a dar un paso más antes de eso. Vamos a construir un objeto de tipo GoogleSignInOptions, con el que vamos a configurar la información que queremos recuperar del usuario que se identifique. Lo construiremos a través de su Builder pasándole como parámetro la opción GoogleSignInOptions.DEFAULT_SIGN_IN que nos asegura la recuperación de la información básica del usuario. Sobre éste podremos llamar a otros métodos requestXYZ() para solicitar que se recupere información adicional. En nuestro caso solicitaremos también el email. El objeto GoogleSignInOptions construido lo pasaremos como parámetro en el método addApi() al construir nuestro api client. Todo esto podemos hacerlo dentro del método onCreate() de la actividad principal.

GoogleSignInOptions gso =
    new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
        .requestEmail()
        .build();

apiClient = new GoogleApiClient.Builder(this)
    .enableAutoManage(this, this)
    .addApi(Auth.GOOGLE_SIGN_IN_API, gso)
    .build();

Una vez más, como ya hicimos en el caso de la API de localización, indicamos mediante enableAutoManage() que queremos que se gestione automáticamente la conexión con los Google Play Services. Tendremos que implementar por tanto en nuestra clase la interfaz OnConnectionFailedListener y definir el método onConnectionFailed().

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
    Toast.makeText(this, "Error de conexion!", Toast.LENGTH_SHORT).show();
    Log.e("GoogleSignIn", "OnConnectionFailed: " + connectionResult);
}

Esta vez, por simplicidad, no vamos a añadir los callback de conexión mediante addConnectionCallbacks(), pero podríamos hacerlo exactamente igual que para la API de localización.

A continuación podemos personalizar un poco la apariencia del botón de login. En concreto podremos cambiar su tamaño y su esquema de colores.

Para cambiar su tamaño podemos llamar a su método setSize() pasándole como parámetro alguno de los valores siguientes:

  • SignInButton.SIZE_STANDARD. Tamaño normal (por defecto).
  • SignInButton.SIZE_WIDE. Tamaño grande.
  • SignInButton.SIZE_ICON_ONLY. Tamaño pequeño (solo el icono).

Para cambiar su esquema de colores podemos llamar a setColorScheme() pasándole como parámetro uno de los siguientes valores:

  • SignInButton.COLOR_LIGHT. Colores claros (por defecto).
  • SignInButton.COLOR_DARK. Colores oscuros.

También es posible cambiar la apariencia del botón dependiendo de la información que hemos solicitado recuperar del usuario logueado. Así, por ejemplo, si en el objeto GoogleSignInOptions añadiéramos el scope de Google+ (mediante requestScopes())para recuperar información de los círculos del usuario, el botón se mostraría con el diseño de Google+ y no con el genérico de Google. Esto lo conseguimos llamando al método setScopes() del botón, pasándole el alcance de información solicitado en nuestro objeto GoogleSignInOptions.

btnSignIn.setSize(SignInButton.SIZE_STANDARD);
btnSignIn.setColorScheme(SignInButton.COLOR_LIGHT);
btnSignIn.setScopes(gso.getScopeArray());

Y vamos por fin por la parte más importante, ¿qué hacer cuando el usuario pulse el botón de login de Google? Pues realmente será muy sencillo al estar todo el proceso bastante automatizado. En primer lugar llamaremos al método getSignInIntent() de la API de autenticación pasándole nuestro cliente API creado anteriormente. El intent obtenido lo usaremos para iniciar una nueva actividad (a través de startActivityForResult()), que será la encargada de solicitar al usuario la cuenta con la que quiere identificarse, si ya está registrada en el sistema, o bien añadir una nueva cuenta para la ocasión. Una vez seleccionada una cuenta realizará las comprobaciones de seguridad correspondientes y nos devolverá el resultado del proceso, que podremos gestionar en el método onActivityResult() de la actividad principal, filtrando como siempre por la misma constante arbitraria que hayamos utilizado en la llamada a startActivityForResult().

btnSignIn = (SignInButton)findViewById(R.id.sign_in_button);
btnSignIn.setOnClickListener(new View.OnClickListener() {
     @Override
     public void onClick(View v) {
         Intent signInIntent = Auth.GoogleSignInApi.getSignInIntent(apiClient);
         startActivityForResult(signInIntent, RC_SIGN_IN);
     }
});

//...

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

    if (requestCode == RC_SIGN_IN) {
        GoogleSignInResult result =
            Auth.GoogleSignInApi.getSignInResultFromIntent(data);

        handleSignInResult(result);
    }
}

Como podéis observar en el código anterior, el resultado del proceso lo obtenemos llamando a getSignInResultFromInten() a partir del intent recibido como parámetro. Y en nuestro caso, gestionaremos los posibles resultados llamando a un método auxiliar handleSignInResult(). En este método auxiliar revisaremos si el resultado del login ha sido correcto llamando a isSuccess(), en cuyo caso ya podremos obtener algunos datos del usuario logueado, o si bien no se ha finalizado el proceso de login por cualquier motivo. En cualquiera de los dos casos actualizaremos la interfaz de la aplicación según corresponda (por ejemplo ocultando el botón de login y mostrando los de logout si la identificación ha sido correcta) para lo que en nuestro caso usaremos otro método auxiliar updateUI().

private void handleSignInResult(GoogleSignInResult result) {
    if (result.isSuccess()) {
        //Usuario logueado --> Mostramos sus datos
        GoogleSignInAccount acct = result.getSignInAccount();
        txtNombre.setText(acct.getDisplayName());
        txtEmail.setText(acct.getEmail());
        updateUI(true);
    } else {
        //Usuario no logueado --> Lo mostramos como "Desconectado"
        updateUI(false);
    }
}

private void updateUI(boolean signedIn) {
    if (signedIn) {
        btnSignIn.setVisibility(View.GONE);
        btnSignOut.setVisibility(View.VISIBLE);
        btnRevoke.setVisibility(View.VISIBLE);
    } else {
        txtNombre.setText("Desconectado");
        txtEmail.setText("Desconectado");

        btnSignIn.setVisibility(View.VISIBLE);
        btnSignOut.setVisibility(View.GONE);
        btnRevoke.setVisibility(View.GONE);
    }
}

Y sólo nos queda comentar cómo acceder a los datos del usuario una vez que éste se ha logueado con éxito. Como vemos en el fragmento de código anterior también es muy sencillo, basta con obtener la cuenta activa mediante getSignInAccount() y obtener los datos que necesitemos mediante su método getXYZ() correspondiente, por ejemplo getDisplayName() para obtener el nombre público, o getEmail() para el correo electrónico.

Vamos a ver por último cómo implementar los botones de Sign Out y Revocar. La diferencia entre ambas opciones radica en que «sign out» simplemente desloguea al usuario de la aplicación, y «revocar» no solo nos deslogueará sino que además eliminará cualquier asociación de la cuenta del usuario con nuestra aplicación.

Para ambos el proceso será análogo, diferenciándose tan sólo en el método que llamaremos inicialmente. En el caso de Sign Out llamaremos al método signOut() de la API de autenticación, lo que iniciará un proceso asíncrono cuyo resultado obtendremos asignando un callback al objeto PendingResult que nos devuelve. En este callback definiremos el método onResult() que se llamará cuando el proceso haya finalizado. En nuestro caso simplemente actualizaremos los botones de la interfaz en consecuencia llamando de nuevo a updateUI(). Por su parte, en el caso de revocar el acceso, el método al que llamaremos será revokeAccess().

btnSignOut.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Auth.GoogleSignInApi.signOut(apiClient).setResultCallback(
             new ResultCallback<Status>() {
                 @Override
                 public void onResult(Status status) {
                      updateUI(false);
                 }
             });
    }
});

btnRevoke.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
         Auth.GoogleSignInApi.revokeAccess(apiClient).setResultCallback(
              new ResultCallback<Status>() {
                  @Override
                  public void onResult(Status status) {
                       updateUI(false);
                  }
              });
    }
});

En principio, con esto habríamos finalizado nuestra aplicación. Mi recomendación en este caso sería ejecutarla en un dispositivo real para poder verificar toda la funcionalidad, ya que es posible que en el emular no tengáis configuradas varias cuentas de usuario o el proceso de creación de una sea más laborioso. En cualquier caso no habría problema en hacerlo a través del emulador.

Al ejecutar la aplicación, la pantalla inicial nos mostrará tan sólo el botón de login de Google, y los dos campos de texto de información del usuario indicando que aún no se encuentra ninguno conectado.

demo-google-signin-1

Al pulsar sobre el botón de login la aplicación nos mostrará las cuentas de Google registradas ya en el sistema y la opción de crear una nueva si se necesita. En mi caso seleccionaré una de las cuentas existentes.

demo-google-signin-2

Al hacerlo, el sistema hará las comprobaciones necesarias y en caso de no encontrar ningún problema se mostrará la información del usuario logueado y los botones de sign out y revocar acceso (desconectar):

demo-google-signin-3

Sin embargo hay un problema. Si cerramos la aplicación y volvemos a abrir comprobaremos que tendremos que pulsar de nuevo el botón de Iniciar Sesión, aunque ya no nos pedirá seleccionar la cuenta de usuario, sino que se logueará directamente con el que ya elegimos.

Si queremos evitar este problema, es decir, si queremos que cuando el usuario vuelva a la aplicación no tenga que pulsar de nuevo sobre el botón de iniciar sesión sino que se conecte automáticamente con el usuario que ya lo había hecho anteriormente (por supuesto salvo que cerrara sesión la última vez) tendremos que implementar esta funcionalidad de forma explícita en nuestra aplicación.

Este proceso de login automático recibe el nombre de Silent Sign-In, y para implementarlo aprovecharemos el evento onStart() de nuestra actividad principal. En este evento, lanzado cuando se inicia la actividad, tendremos que llamar al método silentSignIn() de la API de autenticación pasándole como siempre una referencia a nuestro cliente API. Esta llamada nos devuelve un objeto OptionalPendingResult que utilizaremos de la siguiente forma: llamaremos a su método isDone() que nos indicará si ya existen credenciales del usuario «cacheadas» y éstas son válidas. En este caso podremos obtener el resultado de forma inmediata y lo gestionaremos llamando a nuestro método auxiliar handleSignInResult(), igual que en el proceso de login normal ya comentado. En caso contrario (es decir, si el usuario no ha iniciado sesión anteriormente en el dispositivo o si la sesión ha expirado o se ha cerrado) se iniciará un proceso asíncrono en el que se intentará loguear al usuario de forma silenciosa por otros medios (por ejemplo, si ha iniciado sesión en la misma aplicación pero en otro dispositivo u otra plataforma, aunque esto se sale del alcance de este artículo). Al tratarse de un proceso asíncrono que puede llevar algún tiempo es recomendable mostrar un indicador de progreso mientras se obtiene el resultado, lo que conseguiremos mostrando un ProgressDialog de tipo indeterminado. El resultado del proceso lo obtendremos una vez más definiendo el método onResult() del ResultCallback que asignaremos al objeto OptionalPendingResult. En caso de no conseguirse iniciar sesión de forma automática, se acabará mostrando el botón de login al usuario para que lo haga de forma manual. Veamos cómo quedaría el código:

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

    OptionalPendingResult<GoogleSignInResult> opr = Auth.GoogleSignInApi.silentSignIn(apiClient);
    if (opr.isDone()) {
        GoogleSignInResult result = opr.get();
        handleSignInResult(result);
    } else {
        showProgressDialog();
        opr.setResultCallback(new ResultCallback<GoogleSignInResult>() {
            @Override
            public void onResult(GoogleSignInResult googleSignInResult) {
                hideProgressDialog();
                handleSignInResult(googleSignInResult);
            }
        });
    }
}

private void showProgressDialog() {
    if (progressDialog == null) {
        progressDialog = new ProgressDialog(this);
        progressDialog.setMessage("Silent SignI-In");
        progressDialog.setIndeterminate(true);
    }

    progressDialog.show();
}

Podemos probar ahora de nuevo la aplicación para comprobar que mientras no cerremos sesión el usuario conseguirá loguearse de forma automática cuando volvamos a la aplicación tras haberla cerrado.

Y con esto sí habríamos terminado con la configuración básica del proceso de login en nuestras aplicaciones mediante el uso de una cuenta de Google.

Puedes consultar y/o descargar el código completo de los ejemplos desarrollados en este artículo accediendo a la página del curso en GitHub.

[mensaje-curso]

También te puede interesar

9 comentarios

Integración con Google+ (I): Google+ Sign-In | sgoliver.net 19/09/2016 - 16:43

[…] Puedes consultar información actualizada en el nuevo artículo sobre Google Sign-In. […]

Responder
Integración con Google+ (II): Datos de perfil y Círculos | sgoliver.net 19/09/2016 - 16:44

[…] Puedes consultar información actualizada en el nuevo artículo sobre Google Sign-In. […]

Responder
Alvaro 21/09/2016 - 16:42

Excelente aporte! Sigo muchas paginas de tutoriales pero ninguna como esta, lejos la mejor!

Responder
Google Drive en Android (1) | sgoliver.net 27/09/2016 - 15:18

[…] la api de Google Drive añadiendo Drive.Api, y como ocurría por ejemplo cuando describimos el servicio de autenticación de Google, añadiremos también un scope determinado para los servicios. El scope utilizado determinará el […]

Responder
Santi 17/10/2016 - 12:27

Hola, una pregunta tengo:

Si quiero cerrar la conexion desde un menuItem como se haria? Es decir revocarlo…

Gracias

Responder
Firebase para Android: Base de Datos en Tiempo Real (1) | sgoliver.net 10/11/2016 - 21:12

[…] En el segundo paso podrás descargar un fichero de configuración, en formato JSON, que tendremos que añadir a la aplicación Android una vez creemos el proyecto en Android Studio. Al pulsar el botón CONTINUAR se descargará el fichero, más adelante veremos qué hacer con él (aunque ya hicimos algo muy similar cuando tratamos el tema de autenticación mediante Google Sign-In). […]

Responder
Notificaciones Push en Android: Firebase Cloud Messaging (1) | sgoliver.net 10/01/2017 - 19:49

[…] En el segundo paso podrás descargar un fichero de configuración, en formato JSON, que tendremos que añadir a la aplicación Android una vez creemos el proyecto en Android Studio. Al pulsar el botón CONTINUAR se descargará el fichero, más adelante veremos qué hacer con él (aunque ya hicimos algo muy similar cuando tratamos el tema de autenticación mediante Google Sign-In). […]

Responder
Sincero319 13/07/2017 - 22:43

Dios te bendiga por tus buenos aportes. Muchos como yo hemos aprendido enormemente gracias a tus explicaciones. !!Te lo agradezco mucho!!

Responder
Sincero319 14/07/2017 - 2:02

Excelente aporte hermano. solo quiero señalar que falto la definicion del metodo hideProgressDialog(). aqui la agrego por si alguien la necesita.

private void hideProgressDialog(){

if (progressDialog !=null){
progressDialog.cancel();
}
}

Responder

Responder a Santi Cancelar respuesta

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información. Aceptar Más Información

Política de Privacidad y Cookies