Twitter en tu App Android… cambios para que funcione con la nueva API 1.1

Hola!

Varias personas me avisaron de que no podían ejecutar el código que había subido a otras entradas al respecto:

Twitter en Android

No os preocupéis, ya me di cuenta por mi mismo porque me dejó de funcionar el PFC…. y además la solución es muy sencilla (como todo, a toro pasado).

El problema radica en dos cosas:

  • La API ha cambiado: Esto nos debería de dar igual, pues usamos una librería, luego con reemplazarlo por una versión más moderna de la librería, debería valernos…
  • La librería tiene cambios en su propia API: El tío cutre de Twitter4j ha actualizado la librería apra que funcione con el API nuevo pero ha incluido cambios innecesarios como eliminar clases (como la clase Tweet que ahora es Status…).

Por tanto, deberemos hacer dos cositas (no me extiendo porque puedes ver el código y salvo esto que marco, es igual que el anterior):

Cambiar la librería por la versión 3.0.3 (la más actual mientras escribo esto).

En el código, cuando hacíamos

Twitter twitter = TwitterFactory.getInstance();

Ahora deberemos utilizar el ConfigurationBuilder de Twitter4j. Yo lo que he hecho es crearme una funcioncilla que me de la instancia de twitter, que antes obtenía como está escrito en la linea anterior, de manera que me ahorro escribirlo todas las veces (para una linea no me complicaba, pero ahora son más lineas) y también me ayudará si el código vuelve a cambiar.

La función es como sigue

public static Twitter getTwitterInstance( SharedPreferences prefs ){

	String token = prefs.getString(OAuth.OAUTH_TOKEN, "");
	String secret = prefs.getString(OAuth.OAUTH_TOKEN_SECRET, "");
        ConfigurationBuilder cb = new ConfigurationBuilder();
        cb.setDebugEnabled(true);
        cb.setOAuthConsumerKey(TwitterData.CONSUMER_KEY);
        cb.setOAuthConsumerSecret(TwitterData.CONSUMER_SECRET);
        cb.setOAuthAccessToken(token);
        cb.setOAuthAccessTokenSecret(secret);

	Twitter twitter = new TwitterFactory( cb.build() ).getInstance();

    	return twitter;

}

Aqui solamente le damos los datos para Twitter de otra manera.

El código final y que me ha funcionado ahora mismo está disponible en BitBucket

puedes hacer un pull de esto

https://mamoreno@bitbucket.org/mamoreno/twittertest-para-api-1.1.git

 

Ciau!

 

Cómo crear un validador de campos de formulario con JQuery

En cualquier proyecto web lo más normal es que tengamos que validar los datos que el usuario nos introduce en los formularios. Aunque esto hay que hacerlo tanto desde el lado del servidor como del lado del cliente, este post va sobre el lado del cliente y está pensado para gente que empieza en esto. Vamos a ver cómo hacer un validador de datos con jQuery.

Aunque hay librerías que hacen esto por nosotros, no siempre queremos podemos utilizarlas por lo que sea (tu jefe no te deja… por ejemplo) y nos vemos validando los datos en cada una de las páginas utilizando un montonazo de Javascript que además siempre es parecido. Esto no solamente gasta mucho tiempo sino que el día que descubramos un error, tendremos que cambiarlos en todas las páginas.

Si hubiéramos hecho esto bien y hubiéramos tenido un fichero javascript solo para estas funciones, tal vez el esfuerzo habría sido menor… pero bueno, no siempre es así y además a veces lo que queremos es hacer algo que nos parezca “chulo” (aunque en realidad no lo sea…).

Pues bien, vamos a ver las bases para crear un validador de entrada de usuarios con jQuery. Vamos a aceptar solamente un pequeño tipo de datos/entradas que tendrás que ir ampliando poco a poco y bueno, si ves problemas en el código, es bienvenido que lo digas.

Lo que haremos es, una vez tengamos nuestro validador, pasarle un JSON con los campos que queremos validar, su tipo…y opciones que acepte nuestro validades y éste nos devolverá un objeto que indicará si hay éxito o no.

La entrada será algo como esto por cada campo:


{
	id: 		"#lugarConcierto",
	msg: 		"Pon un lugar/recinto",
	tipo: 		"text",
	mandatory: 	true
},

El id nos servirá para identificar el campo
El msg para poner un mensaje que mostrar en caso de error
El tipo nos dirá qué tipo de validación queremos llevar a cabo (de las aceptadas)
El campo “mandatory” dirá si es obligatorio que tenga valor. De esta manera, si el usuario ingresa datos, se verifican para comprobar su formato aunque el campo no sea obligatorio.

La función podría empezar algo como esto:


/**
*
* Valida campos y los reformatea si procede.
* De momento solo formatea numeros
*/
validaCampos = function( elems_array ){

	var errores = false;

	for( var i in elems_array ){
		var e = elems_array[i]
		var _id = e.id;
		var _valor = jQuery(_id).val().trim();
		var mv = e.minVal != undefined? e.minVal : 0;
		var hayMaxVal = e.maxVal != undefined? true : false;
		var mxv = e.maxVal;

	}//for
}//validador

Dentro del for iremos validando cada una de las entradas del usuario

Una de las opciones que primero vamos a implementar es la de “checkMandatory()” para comprobar que un campo que tiene que tener valor, realmente lo tiene (aunque sea incorrecto…)

Lo que vamos a hacer es definir la función dentro de la función, como una variable


var checkMandatory = function(errorValues){
	//if the value is ErrorValue is considered wrong as well
	var success=true;
	var ev_arr = errorValues != undefined? errorValues : [""];
	if( e.mandatory ){
		//si no hay nada o esta en el array de valores de error.... chasca
		if(_valor.length == 0 || jQuery.inArray(_valor, ev_arr) != -1 ){
			success=false;
			alert( e.msg );
		}
	}
	return success;
};

Le pasaremos un array con valores erróneos. Esto e spara poder decirle: Si tiene un valor que sea uno de estos que te doy, es como si no tuviera nada… Esto es muy útil para los select por ejemplo. Si no nos pasan ningún array, pues le decimos que lo que no nos gusta es la cadena vacía. Entonces, en caso de que el elemento sea obigatorio, miramos que no sea la cadena vacía. Si es así, pues marcamos que no ha habido éxito y alertamos al usuario con el mensaje.

Luego lo que haremos es verificar que el campo tiene un valor aceptado en cuanto a lo que contiene el JSON. Por ejemplo, para tipo “text”, si es obligatorio, nos vale con que tenga algo… pero podríamos haberle definido un minLength, maxLength… etc.

Vamos a meter la verificación del tipo text


switch( e.tipo ){
	case 'text':
		if( !checkMandatory() ) errores=true;

		break;
}

Como se ve, es muy sencillo! solo miramos que tenga contenido o no, según diga el campo “mandatory”. Con esta tónica, también nos vale para los selectores / desplegables. Lo que suelo hacer yo es, cuando creo un select en html es darle a la opción de “–seleccione–” el valor de 0 o de cadena vacía según sea el select… cada uno tiene sus manías. Pues apra eso nos viene bien el array de errores.

Si queremos validar un select, podemos hacer este JSON


{
	id: 		"#selectorDePaises",
	msg: 		"Selecciona un país para el disco",
	tipo: 		"select",
	errorVals 	:['0'],
	mandatory: 	true
}

y la evaluación en el switch podría ser esta


case 'select':
	var ev_arr = e.errorVals != undefined? e.errorVals : ['', '0'];
	if(!checkMandatory(ev_arr)) errores=true;
	break;

Nos aseguramos de que el array tenga un valor, por si acaso… aunque la función checkMandatory() ya lo haga también.

Y así nos podemos eternizar creando tipos de campos…. Por ejemplo, para comprobar que se ha introducido un entero:


{
	id: 		"#capacidad",
	msg: 		"Pon una capacidad",
	tipo: 		"int",
	mandatory: 	true,
	minVal: 	1
}

Aceptamos un minVal para indicar que nos vale un mínimo de valor… podríamos considerar maxVal por ejemplo. Para ver que es positivo, nos vale un minVal de 0 o de 1.


case 'int':
	if( !checkMandatory() ) errores=true;

	if ( isNaN(_valor) ) {
		errores = true;
		alert( e.msg + " - No es un número válido");
	}else{
		//es numero
		_valor = parseInt( _valor );
		if( _valor < mv ){
			errores = true;
			alert( e.msg + " - El valor mínimo es de " + mv);
		}
		if( hayMaxVal && _valor > mxv ){
			errores = true;
			alert( e.msg + " - El valor máximo es de " + mv);
		}

	}
	if(!errores){
		jQuery(_id).val( parseInt(_valor ) );
	}

	break;

En este caso, reasigno el valor al campo a validar. Igual, por paranoia…

Y ya para acabar el ejemplo, podemos meter un tipo “money” para validar que el usuario ha introducido una cantidad que se asemeje a una cantidad de dinero.


case 'money':
	if( !checkMandatory() ) errores=true;

	//reemplazo coma por punto, en españa ponemos coma en lugar de punto
	_valor = _valor.replace(",", ".");
	if( isNaN(_valor) ){
		errores=true;
		alert(e.msg);
	}else{
		//es un numero, le dejo a 'e.decimals' decimales. Por defecto, 2
		var d = e.decimals != undefined? e.decimals : 2;
		_valor = parseFloat(_valor);
		_valor = _valor.toFixed(d);
		if( _valor < mv ){
			alert( e.msg + " - El valor minimo es de " + mv);
			errores = true;
		}
	}
	if(!errores){
		jQuery(_id).val( parseInt(_valor ) );
	}

	break;

Después del switch, vemos si hubo errores. Si los hubo, hacemos lo que haya que hacer (en este caso, resetLoaders() lo que haría es quitar los típicos spinners que dan vueltas mientras hacemos comprobaciones, cargas…) y devolvemos si hubo éxito o no


if(errores) {
	resetLoaders();
	return {success: false };
}

y poco más, luego nos queda ejecutarlo… para ello, definiríamos lo que queremos validar y llamaríamos a la función, por ejemplo:


validaConcierto = function(){

	var elems_array =
		[
			{
				id: 		"#concerts_artist",
				msg: 		"Pon un artista",
				tipo: 		"text",
				mandatory: 	true
			},
			{
				id: 		"#concerts_place",
				msg: 		"Pon un lugar/recinto",
				tipo: 		"text",
				mandatory: 	true
			},
			{
				id: 		"#concerts_capacity",
				msg: 		"Pon una capacidad",
				tipo: 		"int",
				mandatory: 	true,
				minVal: 	1
			},
			{
				id: 		"#concerts_city",
				msg: 		"Pon una ciudad",
				tipo: 		"text",
				mandatory: 	true
			},
			{
				id: 		"#concerts_price",
				msg: 		"Ponle un precio válido. Pon 0 para GRATIS",
				tipo: 		"money",
				mandatory: 	true,
				decimals: 	3,
				minVal: 	0
			},
			{
				id: 		"#concerts_ticket_types",
				msg: 		"Pon el tipo de entradas disponibles",
				tipo: 		"text",
				mandatory: 	true
			},
			{
				id: 		"#concerts_date",
				msg: 		"La fecha no puede estar vacía, utiliza el calendario",
				tipo: 		"text",
				mandatory: 	true
			}
		];

	return validaCampos( elems_array );

}

Qué flecos quedan? Pues validar fechas, por ejemplo y a lo mejor se puede refactorizar el código un poquito para mejorarlo.
El código sería


/**
*
* Valida campos y los reformatea si procede.
* De momento solo formatea numeros
*/
validaCampos = function( elems_array ){

	var errores = false;

	for( var i in elems_array ){
		var e = elems_array[i]
		var _id = e.id;
		var _valor = jQuery(_id).val().trim();
		var mv = e.minVal != undefined? e.minVal : 0;
		var hayMaxVal = e.maxVal != undefined? true : false;
		var mxv = e.maxVal;

		var checkMandatory = function(errorValues){
			//if the value is ErrorValue is considered wrong as well
			var success=true;
			var ev_arr = errorValues != undefined? errorValues : [""];
			if( e.mandatory ){
				//si no hay nada o esta en el array de valores de error.... chasca
				if(_valor.length == 0 || jQuery.inArray(_valor, ev_arr) != -1 ){
					success=false;
					alert( e.msg );
				}
			}
			return success;
		};

		switch( e.tipo ){
			case 'text':
				if( !checkMandatory() ) errores=true;

				break;

			case 'select':
				var ev_arr = e.errorVals != undefined? e.errorVals : [""];
				if(!checkMandatory(ev_arr)) errores=true;

				break;

			case 'int':
				if( !checkMandatory() ) errores=true;

				if ( isNaN(_valor) ) {
					errores = true;
					alert( e.msg + " - No es un número válido");
				}else{
					//es numero
					_valor = parseInt( _valor );
					if( _valor < mv ){
						errores = true;
						alert( e.msg + " - El valor mínimo es de " + mv);
					}
					if( hayMaxVal && _valor > mxv ){
						errores = true;
						alert( e.msg + " - El valor máximo es de " + mv);
					}

				}
				if(!errores){
					jQuery(_id).val( parseInt(_valor ) );
				}

				break;
			case 'money':
				if( !checkMandatory() ) errores=true;

				//reemplazo coma por punto
				_valor = _valor.replace(",", ".");
				if( isNaN(_valor) ){
					errores=true;
					alert(e.msg);
				}else{
					//es un numero, le dejo a 'e.decimals' decimales
					var d = e.decimals != undefined? e.decimals : 2;
					_valor = parseFloat(_valor);
					_valor = _valor.toFixed(d);
					if( _valor < mv ){
						alert( e.msg + " - El valor minimo es de " + mv);
						errores = true;
					}
				}
				if(!errores){
					jQuery(_id).val( parseInt(_valor ) );
				}

				break;
		}
		if(errores) {
			resetLoaders();
			return {success: false };
		}
	}
	return {success: true};
}

Bájatelo de GitHub 🙂

Utilizar Twitter en nuestra APP Android (Parte 3)

Bueno, después de lo visto en los otros 2 posts anteriores, teníamos pendiente capturar el regreso de twitter con la confirmación o no del permiso de uso de nuestra App.

Parte 1

Parte 2

Aquí es donde utilizaremos el famoso protcolo “mgl://” inventado por mí, de “Miguel” 😀

Tenemos que ir a la clase MainActivity y reescribir la función “onNewIntent” como sigue (esto es un poco así porque sí)


/**
 * Called when the OAuthRequestTokenTask finishes (user has authorized the
 * request token). The callback URL will be intercepted here.
 */
@Override
public void onNewIntent(Intent intent) {
	super.onNewIntent(intent);
	final Uri uri = intent.getData();
	SharedPreferences preferencias = this.getSharedPreferences("TwitterPrefs", MODE_PRIVATE);
	if (uri != null && uri.toString().indexOf(TwitterData.CALLBACK_URL) != -1) {
		Log.i("MGL", "Retrieving Access Token. Callback received : " + uri);
		new RetrieveAccessTokenTask(this, getConsumer(), getProvider(), preferencias).execute(uri);
		finish();
	}
}

Esto hace que en cada new Intent de la Actividad (Si alguien puede arrojar luz, se lo agradezco) se mire si tiene una URI con él y si esta URI tiene la URL del CALLBACK nuestro (mgl://mamor) pues actuamos en consecuencia, llamando a otra AsyncActivity como hicimos para pedir la autorización. Este hilo lo que hará es tomar los parámetros que nos devuelve Twitter… en caso de que el usuario haya aceptado, lo que nos devuelve son las credenciales del usuario para que podamos tener permiso para actuar en Twitter bajo su nombre.

Así que creamos la clase “RetrieveAccessTokenTask” con el siguiente código:


public class RetrieveAccessTokenTask extends AsyncTask&lt;Uri, Void, Void&gt; {

	private Context context;
	private OAuthProvider provider;
	private OAuthConsumer consumer;
	private SharedPreferences prefs;

	public RetrieveAccessTokenTask(Context context, OAuthConsumer consumer,
			OAuthProvider provider, SharedPreferences prefs) {
		this.context = context;
		this.consumer = consumer;
		this.provider = provider;
		this.prefs = prefs;
	}

	/**
	 * Retrieve the oauth_verifier, and store the oauth and
	 * oauth_token_secret for future API calls.
	 */
	@Override
	protected Void doInBackground(Uri... params) {
		final Uri uri = params[0];
		final String oauth_verifier = uri
			.getQueryParameter(OAuth.OAUTH_VERIFIER);

		try {
			provider.retrieveAccessToken(consumer, oauth_verifier);

			final Editor edit = prefs.edit();
			edit.putString(OAuth.OAUTH_TOKEN, consumer.getToken());
			edit.putString(OAuth.OAUTH_TOKEN_SECRET, consumer.getTokenSecret());
			edit.commit();

			String token = prefs.getString(OAuth.OAUTH_TOKEN, ");
			String secret = prefs.getString(OAuth.OAUTH_TOKEN_SECRET, ");

			consumer.setTokenWithSecret(token, secret);
			context.startActivity(new Intent(context, MainActivity.class));

			Log.i(TAG, "OAuth - Access Token Retrieved");

		} catch (Exception e) {
			Log.e(TAG, "OAuth - Access Token Retrieval Error", e);
		}

		return null;
	}
}

Esto lo que hace es guardar en SharedPreferences las credenciales secretas del usuario. Para saber más sobre SharedPreferences, puedes leer esto del blog de sgoliver.net

Para ver si se han almacenado las credenciales podemos abrir el DDMS y en el dispositivo encontraremos el XML 🙂

Gracias a tener estos datos, podremos hacer un tuit 😉 y esto lo haremos a través de la librería Twitter4j que os dije que teníamos que incluir.

Esto es muy facilito… vamos a usar una función de esta librería, la función “updateStatus”. Tened en cuenta que esto es un tutorial así rapidillo, por lo que hay muchas mejoras que tendréis que hacer o que veremos en otras entradas otro día, como por ejemplo lanzar una actividad u otra dependiendo de si el usuario ya nos aceptó en twitter… O dejar que el usuario borre sus credenciales y haga “logout” de la aplicación.

De momento nosotros vamos a hacer que la tarea asíncrona que recoge la vuelta de twitter, una vez haya terminado de hacer esto, envíe un Tuit dándome las gracias :-p

Para ello, si has leido la documentación de http://www.sgoliver.net que te dije anteriormente, sabrás que cuando la tarea asíncrona termina hace una llamada a un hook que es el método “onPostExecute”

En la clase RetrieveAccessTokenTask vamos a añadir este método

protected void onPostExecute(Boolean result) {
   if(result){
	String tuit = "Enviado desde Android siguiendo el tutorial https://mamorblog.wordpress.com/2013/04/17/utilizar-twitter-en-nuestra-app-parte-1/ gracias a @miguelsaddress";
	new MandaTuitTask(tuit, prefs).execute();
        Toast.makeText(this.context,"Acceso a twitter conseguido! Enviando el tuit",
                    Toast.LENGTH_SHORT).show();
   }else{
        Toast.makeText(this.context,"Acceso a twitter NO conseguido! ",
                    Toast.LENGTH_SHORT).show();
   }
}

Esto lo que hará es que cuando se termine de recoger el token desde twitter, automáticamente se mande este tuit. Normalmente no lo haríamos así, pero eso es para otros posts 🙂 Aquí es para aprovechar el tirón y mandar un tuit, para que se vea que después de liar todo esto para el oAuth, es realmente fácil.

Para mandar el tuit, lo haremos como se ve en la linea resaltada… Se hace en una tarea asíncrona porque hay que hacer una conexión con twitter y ya os dije que a partir de cierta versión de Android (no sé si la 3 o la 4) no se pueden hacer conexiones en el hilo principal… y si no lo hacemos así nos dará fallos y excepciones y nos hablará del Looper y de cosas horribles. Así que vamos a crear otra clase para mandar el tuit (aquí cada uno que se lo monte como más le guste… una clase asíncrona para cada petición de twitter me parece mucho, pero para esto, nos vale)

Vamos a crear esta clase

package com.mamoreno.twittertest;

import com.mamoreno.utils.TwitterUtils;
import android.content.SharedPreferences;
import android.os.AsyncTask;

public class MandaTuitTask extends AsyncTask<Void, Void, Void> {

	private String tuit;
	private SharedPreferences prefs;

	public MandaTuitTask( String tuit, SharedPreferences prefs){
		this.tuit = tuit;
		this.prefs = prefs;
	}

	@Override
	protected Void doInBackground(Void... params) {
		TwitterUtils.mandaTuit( this.tuit, this.prefs );
		return null;
	}

}

Solamente queremos que ejecute en segundo plano el envío del tuit… El envío en sí lo va a hacer otra clase con métodos estáticos llamada TwitterUtils… esto es por organizar un poco y por centralizar todo lo que podemos hacer con twitter en un solo lugar…

La clase TwitterUtils es la que realmente envía el tuit haciendo uso de Twitter4j

package com.mamoreno.utils;

import oauth.signpost.OAuth;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.auth.AccessToken;
import android.content.SharedPreferences;
import android.util.Log;

public class TwitterUtils {

	public static void mandaTuit( String tuit, SharedPreferences prefs ){

    	AccessToken a = getAccessToken( prefs );
    	if( a!=null ){
	    	Twitter twitter = new TwitterFactory().getInstance();
	    	twitter.setOAuthConsumer(TwitterData.CONSUMER_KEY, TwitterData.CONSUMER_SECRET);
	    	twitter.setOAuthAccessToken(a);
	        try {
				twitter.updateStatus(tuit);
				Log.d("MGL", "+ twitter.getScreenName().toString()		);

			} catch (TwitterException e) {
				Log.e("MGL","TwitterExc: " + e.getMessage() 	 );
			}
    	}
	}

	private static AccessToken getAccessToken( SharedPreferences prefs ){
		String token = prefs.getString(OAuth.OAUTH_TOKEN, ");
    	String secret = prefs.getString(OAuth.OAUTH_TOKEN_SECRET, ");

    	if( secret.equals(") || token.equals(") ) return null;
    	Log.i("MGL", "TOKEN: " + token);
    	Log.i("MGL", "SECRET: " + secret);

    	return new AccessToken( token, secret );
	}

}

Para poder utilizar la librería Twitter4J, esta necesita tener un AccessToken, esto es, las credenciales del usuario, su autorización, para dárselas a twitter, y las usará CADA VEZ que interactúe con Twitter.

Para ello hacemos lo resaltado: Obtenemos una instancia de “Twitter”, le damos el consumer_key y el consumer_secret de nuestra aplicación y el AccessToken del usuario (que tomamos de lo que guardamos en SharedPreferences) y le decimos que mande un mensaje.

Y ya está! Así de simple!! (más de lo que parece una vez que le das una vueltecilla al proceso :D)

Ahora ejecuta la app… dale al botoncito que te llevará a twitter, confirma el acceso y a la vuelta, cuando vuelvas a ver la interfaz del botoncito corre a mirar tu twitter! a ver si te ha salido bien!

Quedaría pendiente:

  • A la vuelta de la confirmación, lanzar otra actividad con una interfaz diferente para mandar tuits o hacer lo que sea que haga nuestra aplicación…
  • Que al arrancar nuestra aplicación, identifique si ya hay credenciales guardadas y nos lleve a esa interfaz que comento.
  • Poder hacer “LogOut” es decir, borrar las credenciales de la app para que no pueda hablar en nuestro nombre o que la use otro usuario diferente.
  • Y por supuesto, gestión de errores, como por ejemplo: qué hacer si no se puede enviar el tuit o si el usuario no nos permite el acceso… habrá que poner algo en la interfaz, aunque sea un mensajito Toast, no? 🙂

Bájate el código desde GitHub AQUI!

 

Edit 26-julio-2013

OJO ha habido cambios en el API de twitter… averigua como hacer que te funcione esto!

Aqui

Utilizar Twitter en nuestra APP Android (Parte 2)

En la entrada anterior hemos visto el concepto y hemos creado el proyecto, añadiendo nuestras credenciales de twitter y modificado el Manifest para que podamos acceder a Internet  y hemos añadido una cosita que usaremos luego.

Ahora toca modificar la clase MainActivity y El layout!

Lo primero es modificar el Layout, le vamos a poner un botoncejo para que al clickarle desencadene la petición de autorización de la que hablábamos. Ponemos esto (verás que queda precioso)

<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"
    tools:context=".MainActivity" >

    <TextView
        android:id="@+id/codigoRespuesta"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:text="@string/codigo_respuesta" />

    <Button
        android:id="@+id/btnOAuth"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:text="@string/btn_oAuth" />

</LinearLayout>

El resultado es esta belleza

layout de la actividad
El area de texto es para poder poner algún mensaje si es el caso… y porque un botón solo me quedaba muy pobre y no me gustaba 🙂

Pues muy bien, ahora tenemos una interfaz muy chula…. que no hace nada! Así que vamos a darle funcionalidad al botoncico. Para ello tenemos que modificar la clase MainActivity, dando funcionalidad al botón en onCreate. Ahí solamente accederemos al botón y le diremos que en su evento OnClick (por cierto, vaya sintaxis más natural ehh?) nos llame al metodo autorizarApp, que es el que de verdad va a hacer algo.
Lo pongo así separadito para ir paso a paso… Ahora, un inciso:

Para poder acceder a todo este Kilombo del oAuth, vamos a necesitar utilizar clases de las librerías que incluimos en la primera parte… son lo que llamaremos el Consumer y el Provider y con ellos accederemos por Http a twitter para presentarnos con nuestras credenciales. Por ello, declararemos unas variables y accederemos a ellas con los métodos getProvider() y getConsumer(). Si no tienen valor, estos métodos las crean… luego pongo el código

Dicho esto, veamos el método autorizarApp() que es muy cortito (atención a la linea resaltada… que ahí venía mi pesadilla con esto en Android ICS+)

protected void autorizarApp() {
	try {
		getProvider().setOAuth10a(true);
		// retrieve the request token
		new OAuthRequestTokenTask(this, getConsumer(), getProvider()).execute();
	} catch (Exception e) {
		codigoRespuesta.setText(e.getMessage());
	}
}

Aquí es donde estamos ejecutando la miga… Resulta que hasta Android 4.0 ICS, se podían hacer estas peticiones en el hilo principal de ejecución, y por eso no había problemas en los tutoriales sobre Android 2.3.x que encontraba pero ahora, para hacer estas cosas que vamos a ver, tenemos que utilizar tareas asíncronas (hilos…) para que se lleven a cabo. Si no lo hacemos así, una de las cosas que pasarán será que todo el rato el emulador nos dirá que el proveedor del servicio nos devuelve null (tremendo What The Fuck). Puedes empaparte de las tareas asíncronas aquí en el blog/curso de Salvador García Oliver, que explica todo genial.

El código de la tarea asíncrona es éste:

public class OAuthRequestTokenTask extends AsyncTask<Void, Void, Void> {

	final String TAG = getClass().getName();
	private Context context;
	private OAuthProvider provider;
	private OAuthConsumer consumer;

	/**
	 *
	 * We pass the OAuth consumer and provider.
	 *
	 * @param context
	 *            Required to be able to start the intent to launch the
	 *            browser.
	 * @param provider
	 *            The OAuthProvider object
	 * @param consumer
	 *            The OAuthConsumer object
	 */
	public OAuthRequestTokenTask(Context context, OAuthConsumer consumer,
			OAuthProvider provider) {
		this.context = context;
		this.consumer = consumer;
		this.provider = provider;
	}

	/**
	 *
	 * Retrieve the OAuth Request Token and present a browser to the user to
	 * authorize the token.
	 *
	 */
	@Override
	protected Void doInBackground(Void... params) {

		try {
			Log.i(TAG, "Retrieving request token from Google servers");
			final String url = provider.retrieveRequestToken(consumer,
					TwitterData.CALLBACK_URL);
			Log.i(TAG, "Popping a browser with the authorize URL : " + url);
			Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url))
					.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP
							| Intent.FLAG_ACTIVITY_NO_HISTORY
							| Intent.FLAG_FROM_BACKGROUND);
			context.startActivity(intent);
		} catch (Exception e) {
			Log.e(TAG, "Error during OAUth retrieve request token", e);
		}

		return null;
	}

}

¿Y qué hace?

Bueno, además de un constructor para pasarle parámetros que usaremos, hay un método llamado “doInBackground” que es el que ejecuta principalmente este hilo (es decir, lo que queremos llevar a cabo). Aquí solamente obtiene la URL que tenemos que abrir en el navegador del terminal (que la da el provider) y pasársela para que se abra el navegador.

En esta linea:

final String url = provider.retrieveRequestToken(consumer,TwitterData.CALLBACK_URL);

Lo que hace es obtener la URL (que a su vez la forma porque conoce las URLs que almacenamos en la clase TwitterData y que es algo como ” https://api.twitter.com/oauth/authorize?oauth_token=mH9&#8230;..y mas “) y pasarle el CALLBACK_URL que definimos (mgl://mamor). Cuando el usuario diga si acepta o no el uso de nuestra App, Twitter hará un redireccionamiento a “mgl://mamor?loquesea=algo”

Tendremos que haber llegado a una pantalla como esta

autorizar

Para meter las credenciales de twitter y autorizar nuestra App. Aunque aún estamos lejos de conseguir algo… ya nos vamos acercando!

Todavía nos queda:

  • Detectar la vuelta con el resultado de twitter
  • Guardar lo que devuelva
  • Hacer un tuit!

Y eso va en las próximas entradas!

Aquí de momento pongo el código de los getProvider() y getConsumer()


/**
 * @return the provider (initialize on the first call)
 */
public static OAuthProvider getProvider() {
	if (httpOauthprovider == null) {
		httpOauthprovider = new DefaultOAuthProvider(
			TwitterData.REQUEST_URL, TwitterData.ACCESS_URL,
			TwitterData.AUTHORIZE_URL);
		httpOauthprovider.setOAuth10a(true);
	}
	return httpOauthprovider;
}

 

/**
 * @param context
 *            the context
 * @return the consumer (initialize on the first call)
 */
public static CommonsHttpOAuthConsumer getConsumer() {
	if (httpOauthConsumer == null) {
		httpOauthConsumer = new CommonsHttpOAuthConsumer(
			TwitterData.CONSUMER_KEY, TwitterData.CONSUMER_SECRET);
	}
	return httpOauthConsumer;
}

La explicación es muy sencilla… si no tienen valor, se lo damos mediante los constructores oportunos.

Aquí os dejo documentación de los objetos (que yo no he mirado mucho la verdad… esto es así)

CommonsHttpOAuthConsumer

OAuthConsumer

En las siguientes entradas vemos cómo detectar el retorno de twitter y cómo guardar las credenciales

🙂

Utilizar Twitter en nuestra APP Android (Parte 1)

Utilizar Twitter en la App de android es un asunto obligatorio en mi proyecto, por lo que no me ha quedado más opción que buscar cómo integrarlo!

Cuando busqué en Google encontré muchísimos tutoriales (algunos bastante buenos) pero a la hora de hacerlos no me funcionaban. Después de tener un arranque de rabia, conseguí calmarme y empezar a investigar… y vi que el problema radicaba en la versión de Android utilizada: Yo lo hacía para 4.2 (API Level 17) y los tutoriales eran para API Level 7. En teoría no debería ser un problema, lo único que a partir de Ice Cream Sandwich había habido cambios y no se permitían ya ciertas prácticas.

Conseguí integrar todo gracias a unas cuantas páginas que encontré, aprovecho para agradecer esta entrada en la que me baso y que no deberías dejar de visitar:

http://blog.doityourselfandroid.com/2011/02/13/guide-to-integrating-twitter-android-application/#more-677

En esta entrada pretendo explicar cómo se ha de integrar twitter en una App Android y estoy abierto a correcciones y comentarios.

Y los pasos técnicos van a ser:

  1. Registrar nuestra App en Twitter y obtener nuestros datos de Twitter relativos a la App (Consumer Key y Consumer Secret)
  2. Seguir el proceso oAuth para autorizar nuestra App para interaccionar en twitter en nombre del usuario.
  3. Guardar sus datos de aceptación
  4. Usar twitter 🙂

Empecemos!

1. Registrar nuestra App en Twitter

Para registrar nuestra App en twitter tenemos que ir a http://dev.twitter.com y accedemos con nuestra cuenta de twitter. Una vez ahí vamos a crear una aplicación:

crear aplicacion twitter

Y nos pedirá ciertos datos que hemos de rellenar

Datos crear App

Y una vez terminado el proceso nos dará unos datos que son únicos de nuestra aplicación y que son secretos, es decir, no debemos dárselos a nadie porque podría actuar en nuestro nombre (digitalmente hablando 😉

Estos datos son CONSUMER_KEY y CONSUMER_SECRET… Encuéntralos porque nos van a hacer falta en la aplicación.

2. Proceso oAuth

Esto es la parte difícil de la integración… resumiendo, oAuth es un sistema de autorización utilizado por muchas plataformas como Facebook, Google, LinkedIn… y por supuesto Twitter que consiste básicamente en que el usuario autoriza en la web respectiva (Facebook, Twitter…) que nuestra aplicación pueda “hablar” en su nombre y acceder a sus datos. De esta manera, el usuario no nos tiene que dar sus credenciales (usuario y contraseña) y podrá revocarnos el acceso en un futuro si ya no quiere que tengamos el mismo.

Para ello, iremos a una cierta URL de Twitter (nos la facilitan ellos, pero es conocida) acompañados de los consumer token y consumer secret que nos dio para identificar nuestra app. Twitter los mirará, identificará nuestra App y nos llevará a otra URL donde le dirá al usuario:

Hola, la aplicación XXXX con descripción YYY quiere pedirte permisos… quiere leer tus tuits / leer y escribir tuits como si fueras tú / leer y escribir tuits como si fueras tú y acceder a tus mensajes privados en twitter. Quieres darle ese permiso?

Entonces el usuario meterá su usuario y contraseña de twitter y dará permiso (o eso esperamos…). Ahora Twitter se redireccionará a la URL Callback proporcionada (ya veremos como, etc) adjuntando el resultado de lo que ha dicho el usuario.

En caso de que el usuario nos lo haya aprobado la aplicación, nos dará un par de numerajos cadenas de caracteres. Eso es la credencial de la aprobación del usuario y nos va a hacer falta CADA VEZ que operemos en su nombre. Es decir, cada vez que pidamos sus tuits, tuiteemos algo… tendremos que enseñarle a twitter esa autorización. Es como: Oye, pon este texto de parte del usuario ZZZZZ… mira, esta es su autorización. Si es correcta, Twitter lo hace.

Bueno, ahora que hemos visto la idea que hay detrás de todo esto, tenemos que ver como se hace en código. Para ello necesitamos varias librerías (podríamos hacerlo todo a mano, supongo… no me lo he planteado bueno en realidad sí pero era mucho curro) que son:

  • signpost-commonshttp4-1.2.1.2.jar
  • signpost-core-1.2.1.2.jar
  • twitter4j-core-3.0.3.jar

(Las librerías las meteremos en la carpeta “libs” de la App de Android)

Empecemos:

Vamos a crear un nuevo proyecto en Eclipse:

creando TwitterTest

Y dejamos todo como está y damos Next todo el rato hasta que nos pida el tipo de actividad, que será BlankActivity. Le dejamos el nombre MainActivity y que nos cree el proyecto con Finish.

Una vez está creado el proyecto, vamos a darle chicha. Lo primero es que nos vamos a crear una clase que nos va a servir para guardar las constantes de Twitter (consumer key, consumer secret, las URLS…). La clase queda como sigue:


package com.mamoreno.utils;

/**
 * This class is going to be a constants Library for the Twitter
 * functionality of this App
 * @author miguelangel.moreno
 *
 */
public class TwitterData {

	public static final String CONSUMER_KEY = "El que te dio twitter";
	public static final String CONSUMER_SECRET = "El que te dio twitter";
	public static final String REQUEST_URL = "https://api.twitter.com/oauth/request_token";
	public static final String AUTHORIZE_URL = "https://api.twitter.com/oauth/authorize";
	public static final String ACCESS_URL = "https://api.twitter.com/oauth/access_token";
	public static final String CALLBACK_URL = "mgl://mamor";

}

Hasta ahí no hay mucho misterio… es solo un contenedor de constantes. Ahora hay que modificar el Manifest para que pida permisos de utilización de Internet y para añadir unos Intent Filters.

El Manifest te tiene que quedar algo así

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.mamoreno.twittertest"
    android:versionCode="1"
    android:versionName="1.0" >

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

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.mamoreno.twittertest.MainActivity"
            android:label="@string/app_name"
            android:launchMode="singleInstance" >
            <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="mgl" android:host="mamor" />
            </intent-filter>

        </activity>
    </application>

</manifest>

Solamente remarcar la parte

<data android:scheme="mgl" android:host="mamor" />

Que debe coincidir con lo que hemos puesto en TwitterData.java (mgl://mamor) ya verás para qué se usa 🙂

Y para que no quede una entrada enorme, lo dejo aquí y sigo en otra entrada! 😉