¿Cuántas cuentas tenemos a lo largo de la vida en internet?, ¿Nos recordamos las credenciales de todas las cuentas? en muchas casos las respuestas serán bastantes y no respectivamente, para no tener que estar recordando las credenciales de todas nuestras cuentas surge el método de autenticación  con otras cuentas dando lugar a los botones populares y actualmente usados en muchas aplicaciones: Ingresa con Facebook, Twitter, Google, entre otros, que reemplazan a los tediosos formularios de registros. En este post desarrollaremos un ejemplo de inicio y cierre de sesión con Facebook usando laAPI 2.3 (lanzada el 25 de marzo del 2015) que es la red social más usada en el Perú, para ello realizaremos los siguientes pasos:

I. Establecer la base:

1. Crearemos un proyecto en Android Studio (Cómo requerimiento del SDK de facebook la versión de la API del proyecto debe ser igual o superior a la 9, el nombre del proyecto no puede contener facebook o fb)

2. Descargamos, descomprimimos, agregamos el SDK y agregamos la dependencia del módulo:

Captura de pantalla 2015-07-02 a la(s) 15.01.43

Sincronizamos los archivos (o ejecutamos)

Si al sincronizar, nos lanza este error:

Captura de pantalla 2015-07-02 a la(s) 15.03.11

Este error se produce debido a que el proyecto tiene una versión diferente del sdk para compilar con respecto al módulo importado, para solucionarlo abrimos el build.gradle del módulo importado (el nombre del directorio es el indicado en el imagen paso 3 :facebook:) así como también el de nuestro proyecto para tomar la referencia de los valores configurados y replicarlos.

Captura de pantalla 2015-07-02 a la(s) 19.31.05

Build.gradle del proyecto:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "23.0.0 rc2"

    defaultConfig {
        applicationId "doapps.me.loginwithtest"
        minSdkVersion 11
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

Build.gradle del módulo :facebook:

apply plugin: 'com.android.library'

repositories {
    mavenCentral()
}

project.group = 'com.facebook.android'

dependencies {
    compile 'com.android.support:support-v4:[22,23)'
    compile 'com.parse.bolts:bolts-android:1.2.0'
}

android {
    compileSdkVersion Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION)
    buildToolsVersion project.ANDROID_BUILD_TOOLS_VERSION

    defaultConfig {
        minSdkVersion Integer.parseInt(project.ANDROID_BUILD_MIN_SDK_VERSION)
        targetSdkVersion Integer.parseInt(project.ANDROID_BUILD_TARGET_SDK_VERSION)
    }

    lintOptions {
        abortOnError false
    }...

Build.gradle del módulo :facebook: después de modificar la configuración (compileSdkVersion, buildToolsVersion, minSdkVersion y targetSdkVersion)

apply plugin: 'com.android.library'

repositories {
    mavenCentral()
}

project.group = 'com.facebook.android'

dependencies {
    compile 'com.android.support:support-v4:[22,23)'
    compile 'com.parse.bolts:bolts-android:1.2.0'
}

android {
    compileSdkVersion 21
    buildToolsVersion "23.0.0 rc2"

    defaultConfig {
        minSdkVersion 11
        targetSdkVersion 22
    }

    lintOptions {
        abortOnError false
    }...

Sincronizamos los archivo y listo, ya tenemos agregado el SDK de Facebook.

Agregar la dependencia del módulo al proyecto:

click derecho en la carpeta principal del proyecto > Open Module Setting > Dependencias > + > Module Dependency > Facebook > Ok > Facebook > Ok

Captura de pantalla 2015-07-02 a la(s) 16.20.54

 

3. Crear un proyecto en facebook

My Apps > Add a new App > Android > Ingresamos el nombre del proyecto > Seleccionamos una categoría > Creamos el proyecto (Create App ID)

Luego de estos pasos, la app de desarrolladores de facebook nos mostrará un formulario donde debemos completar en nombre del paquete y la actividad  por defecto del proyecto (Android):

(Si no saben dónde se encuentran estos datos, pueden revisar su AndroidManifest.xml

Captura de pantalla 2015-07-02 a la(s) 16.17.40

Next, les pedirá que confirmen su nombre de paquete. En la siguiente nos pedirá un hash, en el mismo fragmento de la página hay una ayuda que nos muestra el comando que debemos ejecutar (en el terminal o shell) para obtenerlo, dicho hash pertenece a una llave  (las llaves sirven para firmar aplicaciones), en este caso generaré el hash de la llave de desarrollo debug que viene por defecto al instalar Android Studio .

//comando para obtener el hash, se tiene que estar en el directorio contenedor de 
keytool -exportcert -alias <RELEASE_KEY_ALIAS> -keystore <RELEASE_KEY_PATH> | openssl sha1 -binary | openssl base64
uso:
(Obtiene la ruta)
MacBook-Pro-de-William:.android William_ST$ pwd
/Users/William_ST/.android

(Listamos para verificar si contiene debug.keystore)
MacBook-Pro-de-William:.android William_ST$ ls
adb_usb.ini		adbkey.pub		avd			ddms.cfg		default.keyset		monitor-workspace	sites-settings.cfg
adbkey			androidwin.cfg		cache			debug.keystore		modem-nv-ram-5554	repositories.cfg

MacBook-Pro-de-William:.android William_ST$ keytool -exportcert -alias androiddebugkey -keystore debug.keystore | openssl sha1 -binary | openssl base64
Introduzca la contraseña del almacén de claves:  

*****************  WARNING WARNING WARNING  *****************
* La integridad de la información almacenada en el almacén de claves  *
* NO se ha comprobado.  Para comprobar dicha integridad, *
* debe proporcionar la contraseña del almacén de claves.                  *
*****************  WARNING WARNING WARNING  *****************

TxJxo7s9TvjPmErWxHNHBto5zIg=
MacBook-Pro-de-William:.android William_ST$ 

 

Captura de pantalla 2015-07-02 a la(s) 17.00.43

next, obtenemos el App ID, ahora habilitaremos la aplicación en facebook, vamos a nuestro panel de administración del proyecto > Settings (ayuda: https://developers.facebook.com/apps/App ID/settings/), es necesario agregar un correo.

Captura de pantalla 2015-07-02 a la(s) 17.03.38

luego nos dirigimos a la pestaña Status & Review y habilitamos la aplicación

Captura de pantalla 2015-07-02 a la(s) 17.04.49

II. Codificar

MenuActivity.java

package doapps.me.loginwithtest;
import android.content.Intent;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.facebook.AccessToken;
import com.facebook.CallbackManager;
import com.facebook.FacebookCallback;
import com.facebook.FacebookException;
import com.facebook.FacebookSdk;
import com.facebook.GraphRequest;
import com.facebook.GraphResponse;
import com.facebook.Profile;
import com.facebook.login.LoginManager;
import com.facebook.login.LoginResult;
import com.facebook.login.widget.LoginButton;
import com.squareup.picasso.Picasso;

import org.json.JSONObject;

public class MainActivity extends ActionBarActivity {

    LinearLayout linearLayout;
    //Botón de facebook
    LoginButton buttonLoginFacebook;
    CallbackManager callbackManager;
    ImageView imageViewPhoto;
    ImageButton imageButtonSignOut;
    TextView textViewFullName, textViewEmail;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        FacebookSdk.sdkInitialize(getApplicationContext());
        callbackManager = CallbackManager.Factory.create();
        setContentView(R.layout.activity_main);

        linearLayout = (LinearLayout) findViewById(R.id.linear_layout_sign_in);
        imageButtonSignOut = (ImageButton) findViewById(R.id.image_button_sign_out);
        //Instanciamos el botón de facebook
        buttonLoginFacebook = (LoginButton) findViewById(R.id.connectWithFbButton);
        imageViewPhoto = (ImageView) findViewById(R.id.image_view_photo);
        textViewFullName = (TextView) findViewById(R.id.text_view_full_name);
        textViewEmail = (TextView) findViewById(R.id.text_view_email);

        //Pedimos permiso para poder obtener el email
        buttonLoginFacebook.setReadPermissions("email");

        //Registramos un callback se ejecutará una vez se hace introducido las credenciales
        //de la cuenta de facebook
        buttonLoginFacebook.registerCallback(callbackManager, new FacebookCallback<LoginResult>() {
            @Override
            public void onSuccess(LoginResult loginResult) {
                //Método usado para obtener los campos o atributos solciitados
                getFaceBookProfileDetails(loginResult.getAccessToken());
            }

            @Override
            public void onCancel() {

            }

            @Override
            public void onError(FacebookException error) {

            }
        });

        imageButtonSignOut.setVisibility(View.GONE);

        imageButtonSignOut.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Cierra la sesion inciada en la aplicación
                LoginManager.getInstance().logOut();
                textViewFullName.setText(getString(R.string.full_name));
                textViewEmail.setText(getString(R.string.email));
                imageViewPhoto.setImageResource(R.mipmap.ic_place_holder_photo);
                imageButtonSignOut.setVisibility(View.GONE);
                linearLayout.setVisibility(View.VISIBLE);
            }
        });

    }

    private void getFaceBookProfileDetails(final AccessToken accessToken) {

        GraphRequest request = GraphRequest.newMeRequest(accessToken, new GraphRequest.GraphJSONObjectCallback() {
            //object retorna lo indicado en paramters.putString("fields", "email") en este caso, solo contiene el email
            @Override
            public void onCompleted(final JSONObject object, GraphResponse response) {
                try {
                    linearLayout.setVisibility(View.GONE);
                    imageButtonSignOut.setVisibility(View.VISIBLE);
                    //Profile clase que contiene las características báscias de la cuenta de facebook (No retorna email)
                    Profile profileDefault = Profile.getCurrentProfile();
                    //Librería usada para poder mostrar la foto de perfil de facebook con una transformación circular
                    Picasso.with(MainActivity.this).load(profileDefault.getProfilePictureUri(100,100)).transform(new CircleTransform()).into(imageViewPhoto);
                    textViewFullName.setText(profileDefault.getLastName()+", "+profileDefault.getFirstName());
                    textViewEmail.setText(object.getString("email"));
                } catch (Exception e) {
                    Log.e("E-MainActivity", "getFaceBook" + e.toString());
                }
            }
        });
        Bundle parameters = new Bundle();
        //solicitando el campo email
        parameters.putString("fields", "email");
        request.setParameters(parameters);
        request.executeAsync();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
		//Retorna la reppuesta después del ingreso de las credenciales de facebook
        callbackManager.onActivityResult(requestCode, resultCode, data);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

Diseño activity_main.xml

<LinearLayout 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:orientation="vertical"
    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=".MainActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:orientation="vertical">

            <ImageView
                android:id="@+id/image_view_photo"
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:layout_marginBottom="10dp"
                android:layout_marginTop="20dp"
                android:src="@mipmap/ic_place_holder_photo" />

            <TextView
                android:id="@+id/text_view_full_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="5dp"
                android:layout_marginTop="5dp"
                android:text="@string/full_name" />

            <TextView
                android:id="@+id/text_view_email"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="20dp"
                android:layout_marginTop="5dp"
                android:text="string/email" />

        </LinearLayout>

        <ImageButton
            android:id="@+id/image_button_sign_out"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:src="@mipmap/ic_sign_out"
            android:layout_alignParentRight="true"/>

    </RelativeLayout>

    <LinearLayout
        android:id="@+id/linear_layout_sign_in"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:layout_marginBottom="20dp"
        android:layout_marginTop="5dp"
        android:orientation="vertical">

        <com.facebook.login.widget.LoginButton xmlns:fb="http://schemas.android.com/apk/res-auto"
            android:id="@+id/connectWithFbButton"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:drawableLeft="@mipmap/ic_facebook"
            android:paddingBottom="15dp"
            android:paddingLeft="10dp"
            android:paddingTop="15dp"
            android:textSize="18sp"
            fb:com_facebook_login_text="Iniciar con Facebook"
            fb:com_facebook_logout_text="Cerrar sesión" />

    </LinearLayout>

</LinearLayout>

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="doapps.me.loginwithtest" >

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <meta-data
            android:name="com.facebook.sdk.ApplicationId"
            android:value="@string/facebook_app_id" />
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name="com.facebook.FacebookActivity"
            android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"
            android:label="@string/app_name"
            android:theme="@android:style/Theme.Translucent.NoTitleBar" />
        <provider
            android:name="com.facebook.FacebookContentProvider"
            android:authorities="com.facebook.app.FacebookContentProvider442630429253125"
            android:exported="true" />
    </application>

</manifest>

String.xml

<resources>
    <string name="app_name">LoginWithTest</string>

    <string name="hello_world">Hello world!</string>
    <string name="action_settings">Settings</string>
    <string name="facebook_app_id">442630429253125</string>

    <string name="full_name">Apellidos y nombres</string>
    <string name="email">Correo electrónico</string>
</resources>

CircleTransform.java

package doapps.me.loginwithtest;

import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;

import com.squareup.picasso.Transformation;

/**
 * Created by William_ST on 02/07/15.
 */
public class CircleTransform implements Transformation {

    @Override
    public Bitmap transform(Bitmap source) {
        int size = Math.min(source.getWidth(), source.getHeight());

        int x = (source.getWidth() - size) / 2;
        int y = (source.getHeight() - size) / 2;

        Bitmap squaredBitmap = Bitmap.createBitmap(source, x, y, size, size);
        if (squaredBitmap != source) {
            source.recycle();
        }

        Bitmap bitmap = Bitmap.createBitmap(size, size, source.getConfig());

        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint();
        BitmapShader shader = new BitmapShader(squaredBitmap,
                BitmapShader.TileMode.CLAMP, BitmapShader.TileMode.CLAMP);
        paint.setShader(shader);
        paint.setAntiAlias(true);

        float r = size / 2f;
        canvas.drawCircle(r, r, r, paint);

        squaredBitmap.recycle();
        return bitmap;
    }

    @Override
    public String key() {
        return "circle";
    }
}

build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 22
    buildToolsVersion "23.0.0 rc2"

    defaultConfig {
        applicationId "doapps.me.loginwithtest"
        minSdkVersion 11
        targetSdkVersion 22
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    compile 'com.android.support:appcompat-v7:22.2.0'
    compile project(':facebook')
    compile 'com.squareup.picasso:picasso:2.5.2'
}

III. Resultado

z1            z3           z4

IV. Errores

z2

No alarmarse, solo se debe colocar el hash que muestra en la configuración del proyecto de facebook
https://developers.facebook.com/apps/App ID/settings/

Código disponible en GITHUB

github-logo