Tu aplicación híbrida en Android con LungoJS

Vamos a suponer que quieres usar este fantástico framework llamado LungoJS en tu aplicación Android y que además quieres acceder a cualquier característica de la que disponga tu dispositivo móvil. La respuesta fácil la conocemos todos: phonegap. Casi todo el mundo en esta situación habla de integrar mediante phonegap  una cantidad considerable de plataformas y sistemas operativos nada despreciable. De hecho en su web hacen alarde de que son el único framework móvil de código abierto que soporta siete plataformas. Y es cierto, es una opción más, pero personalmente no me gusta añadir capas y capas que me encaminan a un desarrollo que en el futuro puede quedar “sin salida”. Por ejemplo, mi móvil tiene capacidad NFC y phonegap, a no ser a través de una libreria .jar que se ha trabajado un tercero, no dispone en su api de acceso a NFC (Sí, es un caso muy concreto, pero a esto me refiero).

Por eso hoy quiero usar Android SDK únicamente junto a LungoJS para probar nuestra webapp híbrida con el mismo ejemplo que venimos viendo, BuscaTuits, ya compilado como paquete apk en Android 2.2 o superior.

 

Assets: Directorio para añadir los ficheros de tu webapp (html,js,css …)


Éste es el directorio donde vamos a poner nuestra aplicación LungoJS dentro de nuestro proyecto en eclipse. Voy a saltarme los pasos de cómo empezar a crear un proyecto en eclipse, pero sólo decir que debéis especificar la versión del sistema operativo para la que va destinado vuestro desarrollo.

Pondremos toda nuestra aplicación con los ficheros que formarán parte de la vista web desplegada en android (htmls,css,js,recursos gráficos…). Como veréis es muy sencillo abrir una webview en Android aunque no lo es tanto corregir cada detalle o problema que nos va a surgir en nuestra webapp, sobre todo probando en cada dispositivo móvil, pero es cuestión de pulir cada detalle poco a poco.

Características de nuestra aplicación

Si, pero os estaréis preguntando que diferencia hay entre abrir el BuscaTuits1.2 en un navegador móvil o hacerlo como aplicación híbrida desde nuestro escritorio Android. Bien, ése precisamente es el propósito de este post. Como dije antes nos interesa acceder a cada funcionalidad de nuestro dispositivo móvil y desde javascript, a no ser con el intermediario phonegap (o cordova se llama ahora), no es posible. No es posible acceder a todas las funcionalidades hardware que posee neustro dispositivo, aunque HTML5 pueda hacerlo parcialmente. Por eso, creamos una aplicación nativa en Android incorporando nuestra webView. ¿Y qué mas hacemos? Pues vamos a poner dos cosas más: Un botón de salir que se llame cuando pulsemos en el móvil el menu contextual y una forma de comprobar si tenemos conexión a Internet para abrir o no nuestro BuscaTuits, que por cierto ya vimos en alguna entrada anterior.

Empezamos con el AndroidManifest.xml:

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

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

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

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
        <activity
            android:name=".LungoJsActivity"
            android:label="@string/app_name"
            android:screenOrientation="nosensor" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Importante decir que he bloqueado el cambio de orientación del dispositivo porque sólo quiero un modo portrait para mi webapp. Esto se hace añadiendo en el activity lo siguiente:

 android:screenOrientation="nosensor"

Poco más que comentar. Bueno sí, permisos para Internet, que lo vamos a necesitar, minSdkVersion y targetSdkVersion recomendado incorporarlo, es una buena práctica.

 

WebView en Android: Dándo soporte a Javascript

Una propiedad principal que debemos tener en cuenta a la hora de incorporar LungoJS a nuestra aplicación híbrida en Android es que debemos disponer de una webview compatible con javascript, añadiendo dicha propiedad (y otras que veremos) desde nuestro código.

 

Nuestro Layout: 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >

    <android.webkit.WebView
        android:id="@+id/webkit"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />

</LinearLayout>

Nuestro LungoJsActivity.Java, la parte del webview: 

	@Override
	public void onCreate(Bundle savedInstanceState) {

		if (!verificaConexion(this)) {
			Toast.makeText(getBaseContext(),
					"Comprueba tu conexión a Internet", Toast.LENGTH_SHORT)
					.show();
			this.finish();
		}

		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		mWebView = (WebView) findViewById(R.id.webkit);
		mWebView.getSettings().setJavaScriptEnabled(true);
		mWebView.getSettings().setLoadWithOverviewMode(true);
		mWebView.getSettings().setUseWideViewPort(true);

		// algunas propiedades de la vista web.
		mWebView.setScrollBarStyle(WebView.SCROLLBARS_INSIDE_OVERLAY);
		mWebView.setScrollbarFadingEnabled(false);
		mWebView.setHorizontalScrollBarEnabled(false);
		mWebView.loadUrl("file:///android_asset/index.html");
	}

 

Varios detalles a destacar aquí. Primero verificamos si tenemos o no conexión llamando a n método que describimos después,  verificaConexion. Después le decimos que nuestro webview es lo que le hemos dicho en el layout, android.webkit.WebView aquí:

 mWebView = (WebView) findViewById(R.id.webkit);

Y al final habilitamos una serie de propiedades de nuestro webView, la más importante, la que permite javascript y la que llama a nuestro index.html del directorio assets:

 

		// algunas propiedades de la vista web.
		mWebView.setScrollBarStyle(WebView.SCROLLBARS_INSIDE_OVERLAY);
		mWebView.setScrollbarFadingEnabled(false);
		mWebView.setHorizontalScrollBarEnabled(false);
		mWebView.loadUrl("file:///android_asset/index.html");

El menú para salir de la aplicación se invoca creando dos métodos en nuestor Activity:

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.menu_principal, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		case R.id.MnuOpc1:
			mWebView = null;
			this.finish();
			return true;

		default:
			return super.onOptionsItemSelected(item);
		}
	}

Un menú que se abre, muestra salir y listo. Objetivo conseguido, mostrar funcionalidad disponible sin phonegap mediante aplicaciones híbridas. Eso sí, phonegap despliega en 7 plataformas móviles, eso es a tener muy en cuenta, pero prefiero aprender siete plataformas móviles a repetir un error 7 veces.

Hasta aquí podemos decir que nuestro BuscaTuits, creado con LungoJS1.2, es compatible con android 2.2+ y puede acceder a TODAS las funcionalidades del dispositivo, al ser híbrido.

Bonus Track: lungojs-webapp.zip

Bonus Track II: Bloquear el comportamiento del scroll en el index.html:

		<script type="text/javascript">
		    touchMove = function(event) {
		        event.preventDefault();
		    }
		</script>


Diseñando una aplicación en Android: (IV) “Tus Perlas”.

En entregas anteriores hemos aprendido lo más básico de Android SDK: desde cómo bajar y configurar nuestro entorno de desarrollo hasta empezar a crear nuestra primera aplicación, creando las clases necesarias de la misma. Todo ello sin entrar demasiado en profundidad en cada una de las partes que explicábamos y teniendo presente que el lector de estas entradas ya estaría familiarizado con algún lenguaje, metodología o entorno de desarrollo. Es un estilo diferente de plantear un curso siendo un poco más practico que la siempre “pura y dura” teoría. Hemos incluido una versión de una  aplicación que despliega elementos básicos, como el menú de opciones. No para que adaptéis este modelo literalmente, sino para que penséis primero antes de programar. Yo creo que es la parte fundamental para que la curva de desarrollo no se vea afectada demasiado con retrasos o sorpresas inesperadas. ¿Que tenemos hasta ahora? Una aplicación ‘tonta’ que todavía no tiene funcionalidad pero que nos enseña a organizar nuestro código, comprender sus partes dentro una mínima complejidad y que permite ir avanzando poco a poco en su desarrollo. Claro que esto es totalmente variable. Tu puedes coger el código  y adaptarlo a tus necesidades, variarlo o destriparlo de la forma que quieras. Está para eso :-)

Vamos a ver la clase que se encarga de mostrar  el menú: TusPerlasMenuActivity.java. Recordamos que hemos iniciado nuestra aplicación con unos gráficos y animaciones de dudoso gusto pero que nos sirven para aprender ;-) y que después de esta pantalla ‘splash’ debería mostrarse nuestro menú. Aquí vamos a definir las acciones (“intents”, definición abstracta de una acción que va a ser realizada) que permiten mostrar las pantallas subsiguientes de nuestra aplicación.  ¿Que es lo que hace y cómo? Vamos a verlo, paso a paso.

 

TusPerlasMenuActivity

Nuestros imports. Aquí nada reseñable por ahora, solo que hemos incluído AdapterView, ArrayAdapater, ListView y TextView.

package com.tunelko.perlas; 

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

En esta primera parte de la clase construye la lista de opciones y en base al nombre que contiene cada opción lanza un Intent o una Activity diferente (Ayuda, Game, Scores, etc…). En el código hay comentarios que lo explican:

/**
 * @author tunelko.
 * @description Pantalla de menu. Hereda de TusPerlasActivity.
 *
 */

public class TusPerlasMenuActivity extends TusPerlasActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.menu);

        // Buscamos las propiedades del listview (menu.xml) para crear nuestro ListView.
        ListView menuList = (ListView) findViewById(R.id.ListView_Menu);

// metemos en un array los nombres del menu sacados de nuestro res/values/strings.xml
//R.string.nombre_en_el_strings_xml
        String[] items = { getResources	().getString(R.string.menu_item_play),
                getResources().getString(R.string.menu_item_scores),
                getResources().getString(R.string.menu_item_settings),
                getResources().getString(R.string.menu_item_help) };
        // Creamos el ArrayAdapter pasándoles nuestro array de strings 'items'
       // y sacando propiedades de menu_item.xml.
        ArrayAdapter<String> adapt = new ArrayAdapter<String>(this, R.layout.menu_item, items);
        menuList.setAdapter(adapt);
[... más código oculto.]

 

Después lo que hacemos es lo que comentábamos antes, escuchar que acción está detrás de nuestro menú y si pulsamos sobre ella, sacar la pantalla correspondiente. Concretamente, esto lo hacen las lineas:

        // Para las acciones del menu y los new Intent.
        menuList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        	   public void onItemClick(AdapterView<?> parent, View itemClicked, int position, long id) {

                   TextView textView = (TextView) itemClicked;
                   String strText = textView.getText().toString();

Si el literal del menú es ‘Ver y puntuar las perlas’ (sacado del res/values/strings.xml e ignorando mays/min.) entonces lanza TusPerlasGameActivity, si es ‘Ayuda’, TusPerlasAyudaActivity, etc… Tanto startActivity como new Intent vienen de Activity.class(android.app.Activity) e Intent.class (android.content.intent).

 if (strText.equalsIgnoreCase(getResources().getString(R.string.menu_item_play))) {
                       // Lanzamos TusPerlasGameActivity
                       startActivity(new Intent(TusPerlasMenuActivity.this, TusPerlasGameActivity.class));

                   } else if (strText.equalsIgnoreCase(getResources().getString(R.string.menu_item_help))) {
                       // Lanzamos TusPerlasAyudaActivity
                       startActivity(new Intent(TusPerlasMenuActivity.this, TusPerlasAyudaActivity.class));

                   } else if (strText.equalsIgnoreCase(getResources().getString(R.string.menu_item_settings))) {
                       // Lanzamos TusPerlasConfigActivity
                       startActivity(new Intent(TusPerlasMenuActivity.this, TusPerlasConfigActivity.class));

                   } else if (strText.equalsIgnoreCase(getResources().getString(R.string.menu_item_scores))) {
                       // Lanzamos TusPerlasScoreboardActivity
                       startActivity(new Intent(TusPerlasMenuActivity.this, TusPerlasScoreboardActivity.class));
                   }
               }

Ahora, todo junto: TusPerlasMenuActivity.java

package com.tunelko.perlas; 

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;

/**
 * @author tunelko.
 * @description Pantalla de menu. Hereda de TusPerlasActivity.
 *
 */

public class TusPerlasMenuActivity extends TusPerlasActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.menu);

        // Buscamos las propiedades del listview (menu.xml) para crear nuestro ListView.
        ListView menuList = (ListView) findViewById(R.id.ListView_Menu);

        // metemos en un array los nombres del menu sacados de nuestro res/values/strings.xml
        String[] items = { getResources	().getString(R.string.menu_item_play),
                getResources().getString(R.string.menu_item_scores),
                getResources().getString(R.string.menu_item_settings),
                getResources().getString(R.string.menu_item_help) };
        // Creamos el ArrayAdapter pasándoles nuestro array de strings items y sacando propiedades de menu_item.xml.
        ArrayAdapter<String> adapt = new ArrayAdapter<String>(this, R.layout.menu_item, items);
        menuList.setAdapter(adapt);

        // Para las acciones del menu y los new Intent.
        menuList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
        	   public void onItemClick(AdapterView<?> parent, View itemClicked, int position, long id) {

                   TextView textView = (TextView) itemClicked;
                   String strText = textView.getText().toString();

                   if (strText.equalsIgnoreCase(getResources().getString(R.string.menu_item_play))) {
                       // Lanzamos TusPerlasGameActivity
                       startActivity(new Intent(TusPerlasMenuActivity.this, TusPerlasGameActivity.class));

                   } else if (strText.equalsIgnoreCase(getResources().getString(R.string.menu_item_help))) {
                       // Lanzamos TusPerlasAyudaActivity
                       startActivity(new Intent(TusPerlasMenuActivity.this, TusPerlasAyudaActivity.class));

                   } else if (strText.equalsIgnoreCase(getResources().getString(R.string.menu_item_settings))) {
                       // Lanzamos TusPerlasConfigActivity
                       startActivity(new Intent(TusPerlasMenuActivity.this, TusPerlasConfigActivity.class));

                   } else if (strText.equalsIgnoreCase(getResources().getString(R.string.menu_item_scores))) {
                       // Lanzamos TusPerlasScoreboardActivity
                       startActivity(new Intent(TusPerlasMenuActivity.this, TusPerlasScoreboardActivity.class));
                   }

               }
           });

       }
   }


Intercambio de datos con JSON en Android SDK

Json es un formato compacto de intercambio de datos soportado por librerías en Android SDK. En este post vamos a escribir un par de funciones para entender como recuperar un fichero json de la web de TusPerlas y así de paso avanzar el pŕoximo artículo de nuestra primera aplicación en Android.

 

El código completo (se asume que debes tener permisos de acceso a INTERNET para nuestro AndroidManifest.xml y es conveniente comprobar nuestra conexión antes.)

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONObject;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class lecturaJson extends Activity {
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		String lecturaJsonTusPerlas = lecturaJsonTusPerlas();
try {
			JSONArray jsonArray = new JSONArray(lecturaJsonTusPerlas);
			Log.i(lecturaJson.class.getName(),
					\"Número \" + jsonArray.length());
			for (int i = 0; i < jsonArray.length(); i++) {
				JSONObject jsonObject = jsonArray.getJSONObject(i);
				Log.i(lecturaJson.class.getName(), jsonObject.getString("texto")); // el campo lo recuperamos con el Log
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public String lecturaJsonTusPerlas() {
		StringBuilder builder = new StringBuilder();
		HttpClient cliente = new DefaultHttpClient();
		HttpGet peticionGet = new HttpGet("http://perlas.tunelko.com/json/get_data");
		try {
			HttpResponse respuesta = cliente.execute(peticionGet);
			StatusLine estado = respuesta.getStatusLine();
			int codigoEstado = estado.getStatusCode();
			if (codigoEstado == 200) {
				HttpEntity entidad = respuesta.getEntity();
				InputStream contenido = entidad.getContent();
				BufferedReader lector = new BufferedReader(
						new InputStreamReader(contenido));
				String line;
				while ((line = lector.readLine()) != null) {
					builder.append(line);
				}
			} else {
				Log.e(lecturaJson.class.toString(), "Algo ha salido mal");
			}
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return builder.toString();
	}
}


Android SDK: ¿Cómo detecto la conexión a Internet?

Si tu aplicación necesita conectarse a Internet resulta muy útil comprobar si disponemos de dicha conexión. Lo primero que vamos a añadir son los permisos al AndroidManifest.xml para que nuestro usuario disponga de dichos permisos. Sí, en realidad la aplicación se ejecuta en el sistema operativo y actúa como usuario UNIX a nivel de permisos. Vamos a ver el Manifest:

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

Necesitamos el acceso a Internet genérico y saber el estado de la red. En nuestra clase Java vamos a poner una función de verificación de la conexión, es muy sencilo:

	public static boolean verificaConexion(Context ctx) {
		boolean bConectado = false;
		ConnectivityManager connec = (ConnectivityManager) ctx
				.getSystemService(Context.CONNECTIVITY_SERVICE);
		// No sólo wifi, también GPRS
		NetworkInfo[] redes = connec.getAllNetworkInfo();
		// este bucle debería no ser tan ñapa
		for (int i = 0; i < 2; i++) {
			// ¿Tenemos conexión? ponemos a true
			if (redes[i].getState() == NetworkInfo.State.CONNECTED) {
				bConectado = true;
			}
		}
		return bConectado;
	}

Ahora que tenemos el método, vamos a usarlo en la parte que nos interese comprobar el estado de la conexión, típicamente puede ser inmediatamente después del  super.onCreate(savedInstanceState); en nuestro onCreate. Si no es TRUE que tengamos conexión, lanzamos un Toast o mensajito por pantalla y salimos de la aplicación :

		if (!verificaConexion(this)) {
			Toast.makeText(getBaseContext(),
					"Comprueba tu conexión a Internet. Saliendo ... ", Toast.LENGTH_SHORT)
					.show();
			this.finish();
		}

Hacemos todo esto porque si nuestra aplicación necesita conexión y no lo comprobamos ésta lanza una excepción y sale a las bravas, sin despedirse ni nada. Un FAIL en toda regla de programador.
Hasta la próxima !



Diseñando una aplicación en Android (III): “Tus Perlas”

Ya tenemos planteada la aplicación. De una forma muy básica: pantalla de inicio a modo de “splash” con animación incluída, menú en un ListView con las opciones correspondientes al juego que luego veremos con detalle, etc… También adelantábamos que los permisos iban a requerir la conexión a Internet. Esto es debido a que la aplicación va a conectar y consultar algunos datos de la web oficial. En este punto nos tenemos que plantear una forma de recuperar esos datos, un formato y un flujo de actividad de red, en definitiva. En este caso vamos a emplear JSON, por ser un formato compacto, manejable , soportado y más ligero que el pesado XML. Además, Android incluye una versión de estas librerías. De cualquier manera esto no lo veremos hasta dentro de dos o tres entregas.

Una vez elegido el formato de intercambio de datos, debemos pensar en los aspectos que pueden hacer inestable o inconsistente la aplicación. como puede ser comprobar que tenemos conexión a Internet al ejecutar la aplicación y avisar de una manera u otra al usuario con una notificación Toast. Caso de no hacerlo, lanzaría una excepción y forzaría el cierre, algo que no es lo suficientemente educado ;-)

Pero vamos con lo inmediato por hacer, completar la el .java de la pantalla de inicio y entender como se ejecutan las animaciones.  La plataforma Android soporte cuatro formas de animaciones gráficas:

  • Gif animados: típicos gráficos animados que contienen frames para desplegar la animación.
  • Animacion “frame-by-frame”: El SDK de Android nos ofrece la posibilidad de emplear un mecanismo similar a las animaciones “frame-by-frame” donde el programador usa gráficos individuales y transiciones entre ellos. (ver la clase AnimationDrawable).
  • Animación “Tweened”: Simple pero efectivo método para definir animaciones que se apliquen a cualquier view ó layout.
  • OpenGL ES: A través de su API, Android ofrece la posibilidad de emplear librerías openGL para iluminar, crear 3d, efectos de luz, flares….

Pantalla de inicio.

Crea un directorio dentro de /res que sea anim. De tal forma que en /res/anim tengas los xml que definan el comportamiento de los elementos que quieras animar. En este caso vamos a rotar una imagen que tenemos en la pantalla de inicio y que hemos definido en su correspondiente layout inicio.xml. Vamos a crear un xml que sea custom_anim.xml y que contenga lo siguiente:

custom_anim.xml

<?xml version="1.0" encoding="utf-8" ?>
<set
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="false">
    <rotate
        android:fromDegrees="0"
        android:toDegrees="360"
        android:pivotX="50%"
        android:pivotY="50%"
        android:duration="2000" />
    <alpha
        android:fromAlpha="0.0"
        android:toAlpha="1.0"
        android:duration="2000">
    </alpha>
    <scale
        android:pivotX="50%"
        android:pivotY="50%"
        android:fromXScale=".1"
        android:fromYScale=".1"
        android:toXScale="1.0"
        android:toYScale="1.0"
        android:duration="2000" />
</set>

fade_in.xml

<?xml version="1.0" encoding="utf-8" ?>
<set
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="false">
    <alpha
        android:fromAlpha="0.0"
        android:toAlpha="1.0"
        android:duration="2500">
    </alpha>
</set>

fade_in2.xml

<?xml version="1.0" encoding="utf-8" ?>
<set
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shareInterpolator="false">
    <alpha
        android:fromAlpha="0.0"
        android:toAlpha="1.0"
        android:duration="2500"
        android:startOffset="2500">
    </alpha>
</set>

En la primera, custom_anim.xml, aplicamos una rotación, fundido alpha y escalado de la imagen. En los ficheros fade_in.xml y fade_in2.xml, simplemente un pequeño fundido de 2,5 segundos  (android:duration=”2500″) con la diferencia de que en la segunda esperamos precisamente otros 2,5 segundos a que aparezca (android:startOffset=”2500″). Manejar las animaciones es bastante intuitivo y muy personal. A efectos de demostrar brevemente como se hace, pueden servir estas.

La parte de animación en nuestro “TusPerlasInicioActivity”

Editamos TusPerlasInicioActivity.java para crear el método comienzaAnimacion(). Declaramos las librerias que vamos a usar, son estas:

import android.os.Bundle;
import android.content.Intent;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.LayoutAnimationController;
import android.view.animation.Animation.AnimationListener;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;

Tenemos sobreescrito nuestro onCreate, como en las demás clases, renderizando el layout correspondiente con setContentView(view):

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.inicio);
    }

Y ahora vamos a ver como creamos nuestro primer efectillo.

    /**
     * Método para iniciar nuestra animación de la pantalla de inicio
     */
    private void comienzaAnimacion() {
        // Fundido del texto superior del
        TextView texto_superior = (TextView) findViewById(R.id.TextViewTopTitle);
     Animation efecto_fundido = AnimationUtils.loadAnimation(this, R.anim.fade_in);
        texto_superior.startAnimation(efecto_fundido);
... [código]
}

Lo primero que hacemos es decirle a nuestro método que queremos animar, un TextView que llamamos texto_superior y cuyo id está definido como TextViewTopTitle, de nuestro inicio.xml. Después le decimos que cargue nuestro efecto, lo llamamos efecto_fundido. Las propiedades del efecto las aplicará del recurso R.anim.fade_in (¿Os suena este xml?). Y “Voilá”, ya tenemos un efecto básico de fundido.

Dentro del mismo método onCreate, creamos el resto de efectos y nos encargamos de decirle que después de mostrarlos, vaya a la pantalla del menú:

        //Fundido del texto de abajo despues de un breve delay de 2,5s
        TextView texto_abajo = (TextView) findViewById(R.id.TextViewBottomTitle);
        Animation efectillo_dos = AnimationUtils.loadAnimation(this, R.anim.fade_in2);
        texto_abajo.startAnimation(efectillo_dos);

Esta es la parte donde se crea el ‘listener’ de la animación.

 // Vamos al menu cuando haya acabado el efecto del texto de abajo
        efectillo_dos.setAnimationListener(new AnimationListener() {
            public void onAnimationEnd(Animation animation) {
                // La animación ha terminado, vamos a la pantalla de menú.
                startActivity(new Intent(TusPerlasInicioActivity.this, TusPerlasMenuActivity.class));
                TusPerlasInicioActivity.this.finish();
            }

            public void onAnimationRepeat(Animation animation) {
            }

            public void onAnimationStart(Animation animation) {
            }
        });

Esta es forma de recorrer la tabla y aplicar los efectos al contenido de sus celdas (custom_anim.xml, recordad):

     // Carga las animaciones para todas las vistas dentro del TableLayout
        Animation girando = AnimationUtils.loadAnimation(this, R.anim.custom_anim);
        LayoutAnimationController controller = new LayoutAnimationController(girando);

        TableLayout table = (TableLayout) findViewById(R.id.TableLayout01);
        for (int i = 0; i < table.getChildCount(); i++) {
            TableRow row = (TableRow) table.getChildAt(i);
            row.setLayoutAnimation(controller);
        }

Y por último la sobrecarga de onPause y onResume para terminar de rematar esta clase. Es en onResume donde llamamos a comienzaAnimacion():

            @Override
	    protected void onPause() {
	        super.onPause();
	        // Paramos la animación
	        TextView texto_superior = (TextView) findViewById(R.id.TextViewTopTitle);
	        texto_superior.clearAnimation();

	        TextView texto_abajo = (TextView) findViewById(R.id.TextViewBottomTitle);
	        texto_abajo.clearAnimation();

	        TableLayout table = (TableLayout) findViewById(R.id.TableLayout01);
	        for (int i = 0; i < table.getChildCount(); i++) {
	            TableRow row = (TableRow) table.getChildAt(i);
	            row.clearAnimation();
	        }
	    }

	    @Override
	    protected void onResume() {
	        super.onResume();
	        // Comienza el 'espectaculo' ;-)
	        comienzaAnimacion();
	    }

Representación de lo que tendremos en pantalla:

Si puedo voy a ir colgando el proyecto completo para que lo podáis ir probando vosotros. ¡ Hasta la próxima entrega !



Diseñando una aplicación en Android (II): “Tus Perlas”

Como veíamos en la entrega anterior la preparación del esquema en el diseño de nuestra aplicación resulta fundamental. Una buena practica para alcanzar cotas altas en rapidez durante el desarrollo es, sin duda, planificar mentalmente que queremos que haga nuestra aplicación y de que recursos tiraremos para ello, aparte del código, naturalmente. Habíamos dicho que teníamos una clase base con las propiedades comunes y el resto que extendieran de esta para desplegar las pantallas del “juego” (como prototipo, las perlas).

Vamos primero a ver el * AndroidMAnifest.xml, aquí deberemos incluir todos los Activities que compongan nuestra aplicación Android:

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

    <application
        android:debuggable="true"
        android:icon="@drawable/icon_androidtusperlas"
        android:label="@string/app_name" >
        <activity
            android:label="@string/app_name"
            android:name=".TusPerlasInicioActivity" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".TusPerlasMenuActivity" >
        </activity>
        <activity android:name=".TusPerlasScoreboardActivity" >
        </activity>
        <activity android:name=".TusPerlasConfigActivity" >
        </activity>
        <activity android:name=".TusPerlasGameActivity" >
        </activity>
        <activity android:name=".TusPerlasAyudaActivity" >
        </activity>
    </application>

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

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

</manifest>

En esta versión del AndroidManifest.xml ya intuímos que permisos requerimos al usuario para ejecutar la aplicación.

Después, vamos a crear todas las clases de nuestros citados Activities. Esta es la clase base con elementos comunes a todas las demás clases (fíjate que extiende de Activity) y sólo contiene algunos settings que pueden ser usados posteriormente por las demás clases que dependan de ésta.

package com.tunelko.perlas;
import android.app.Activity;

public class TusPerlasActivity extends Activity {

	// Preferencias
    public static final String SETTINGS_PREFERENCES = "GamePrefs";
    public static final String SETTINGS_PREFERENCES_NICKNAME = "Usuario";
    public static final String SETTINGS_PREFERENCES_EMAIL = "Email";
    public static final String SETTINGS_PREFERENCES_PASSWORD = "Password";
    public static final String SETTINGS_PREFERENCES_BIRTHDATE = "Fecha de nacimiento";
    public static final String SETTINGS_PREFERENCES_SEX = "Sexo";
    public static final String SETTINGS_PREFERENCES_SCORE = "Puntuación";
    public static final int MAX_PERLAS = 15;
    public static final String DEBUG_TAG = "TusPerlas Log";
}

Para cada una de las clases restantes metemos este código y creamos cada uno de los layouts que se mostraran al llamar a setContentView(view). Fíjate que ahora extiende de TusPerlasActivity.

package com.tunelko.perlas;
import android.os.Bundle;

/**
 * @author tunelko.
 * @description Pantalla de Inicio. Hereda de TusPerlasActivity.
 *
 */

public class TusPerlasInicioActivity extends TusPerlasActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.inicio);
    }
}

Una vez creadas las clases y los layouts vamos a pararnos en estos últimos y ver cuales podemos manejar. Ya sabeis que el desarrollo de aplicaciones móviles está condicionado por el tamaño de las pantallas (y resoluciones). Igual que en web, nuestros layouts contienen elementos para determinar que nuestros diseños de interfaces se adapten a las necesidades de los dispositivos disponibles en el mercado. Vemos a continuación un resumen de los mismos:

 

 
Nombre Descripción Características
LinearLayout Cada vista ‘hija’ está puesta después de la anterior en una única fila o columna. Orientación vertical y horizontal.
RelativeLayout Cada vista ‘hija’ esta puesta en relación a las demás del layout o relativa a los límites del layout ‘padre’  Diferentes atributos de alineación para controlar donde uan vista ‘hija’ se posiciona con relación a otros controles de la vista ‘hija’
FrameLayout  Cada vista ‘hija’ está emplazada dentro del frame, relativa a la esquina superior izquierda. Los controles se pueden solapar.  El orden de posicionamiento es importante cuando se usa con los apropiados ‘gravity settings’
TableLayout  Cada vista ‘hija’ es una celda en un conjunto de filas y columnas. Cada fila requiere un elemento TablerRow.

Tabla 1: Controles más comunes de Layouts en Android. * A partir de Android 1.5 SDK AbsoluteLayout está deprecated.

 

Diseñando la pantalla de inicio

Nuestra pantalla de inicio (layout inicio.xml) va a constar de una sencilla presentación con título, imagen con determinado efecto animado y texto de pie de imagen con otro efecto de animación. Más abajo, estarán los créditos, como vemos en este diagrama:

 

Debajo, nuestro inicio.xml con el layout:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@android:color/black">
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/TextViewTopTitle"
        android:text="@string/app_logo_top"
        android:textColor="@color/logo_color"
        android:layout_gravity="center_vertical|center_horizontal"
        android:gravity="top|center"
        android:textSize="@dimen/logo_size"></TextView>
    <TableLayout
        android:id="@+id/TableLayout01"
        android:stretchColumns="*"
        android:layout_height="wrap_content"
        android:layout_width="fill_parent">
        <TableRow
            android:id="@+id/TableRow01"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:layout_gravity="center_vertical|center_horizontal">
            <ImageView
                android:id="@+id/ImageView2_Left"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical|center_horizontal"
                android:src="@drawable/inicio_tusperlas"></ImageView>

        </TableRow>

    </TableLayout>
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/TextViewBottomTitle"
        android:text="@string/app_logo_bottom"
        android:textColor="@color/logo_color"
        android:gravity="center"
        android:textSize="@dimen/titulo_debajo_logo"></TextView>
    <TextView
        android:id="@+id/TextViewBottomVersion"
        android:text="@string/app_version_info"
        android:textSize="@dimen/version_size"
        android:textColor="@color/version_color"
        android:background="@color/version_bkgrd"
        android:layout_height="fill_parent"
        android:lineSpacingExtra="@dimen/version_spacing"
        android:layout_width="fill_parent"
        android:layout_gravity="center_vertical|center_horizontal"
        android:gravity="center"></TextView>
</LinearLayout>

Como véis se aplican una serie de atributos a cada nodo xml que son practicamente intuitivos y en todo caso consultables en la documentacion de la API de android. Comentar que en el directorio res/values tendremos que añadir otros xmls con estos valores. Por ejemplo, para dimensiones. Si tenemos que  android:textSize=”@dimen/version_size” en nuestro TextView iremos a res/values/dimens.xml y añadiremos version_size, por ejemplo.

Ya falta poco para poder ejecutar en nuestro dispositivo o maquina virtual la aplicación tonta que muestre todo lo que hasta ahora llevamos explicando. Fijaros en res/draw-* e ir añadiendo los recursos gráficos que queráis mostrar en la aplicación.  android:src=”@drawable/inicio_tusperlas” indica que debemos crear un gráfico “inicio_tusperlas.png” (o jpg,gif,…* mirar los formatos admitidos en la documentación).

En la próxima entrega, veremos como crear las animaciones de esta pantalla de inicio que como recordaréis, dará paso al menú de la aplicación.



Diseñando una aplicación en Android (I): “Tus Perlas”

Para aprender a programar con Android, lo mejor es pensar en una aplicación prototipo que nos sirva para desarrollar todo el potencial que nos ofrece el SDK: desde lo más básico vamos avanzando hacía técnicas que ahora nos pueden parecer complicadas, abarcando todos los elementos que requieran una aplicación más o menos compleja. Vamos a pensar en nuestra aplicación, ¿Qué nos hace falta? ¿Cómo queremos que sea o nos la imaginamos inicialmente? . Empecemos.

Vamos a crear una aplicación que sea un juego. El juego de puntuar las perlas , esas frases tontas que nuestros compañeros de trabajo o en nuestro entorno social más cercano, sueltan en un determinado momento. La dinámica de esta web es muy sencilla: Alguien apunta una frase tonta que ha escuchado y los usuarios del entorno social del ‘perlero’ lo puntúan en base a una normas y con el único fin de releer las más ‘humorísticas’ a medida que avance el tiempo. Puedes mirar algunas en la web de las perlas. Necesitaremos lo típico en un juego: pantalla de incio, menú de opciones, top scoreboard, ayuda y pantallas de acción propias del juego.

 Definiendo cada uno de nuestros Activities

La clase Activity permite disponer de un interfaz de usuario que se centre en lo que el usuario puede hacer. Para nuestra aplicación vamos a crear un Activity para cada una de las pantallas (recordemos: Inicio, menú de opciones, scoreboard, ayuda y pantalla de acción). Aparte, una buena práctica es implementar una clase base  que permita compartir recursos comunes desde donde podremos hacer las llamadas al juego en sí.

  • PerlasActivity. Extiende de android.app.Activity y es nuestra clase base. Aquí vamos a definir las preferencias del juego así como otras características.
  • PerlasInicioActivity. Extiende de PerlasActivity, la clase base. Representa nuestra pantalla de inicio, más o menos gráfica.
  • PerlasMenuActivity. Extiende de PerlasActivity, encargada de gestionar nuestro menú del juego.
  • PerlasAyudaActivity. Extiende de PerlasActivity, encargada de mostrar los textos de ayuda y las normas del juego.
  • PerlasScoreBoardActivity. Extiende de PerlasActivity, esta clase representa la pantalla de puntuaciones.
  • PerlasConfigActivity. Extiende de PerlasActivity, esta clase permite definir las opciones de nuestra aplicación.
  • PerlasGameActivity. Extiende de PerlasActivity, esta clase representa el juego en sí, poder puntuar las perlas que estén activas y correspondan a nuestro entorno social.

Definiendo las características del prototipo de nuestra aplicación

Para concretar un poco que hace cada una de las clases que hemos definido a modo de propuesta anteriormente, es conveniente poner en un esquema que hará cada una de las partes. No queremos escribir una línea de código sin saber que es lo que  vamos a escribir. Os suena, ¿No? Vamos allá.

Pantalla de inicio

La pantalla de inicio sirve como punto de entrada a nuestra aplicación. Su funcionalidad debería estar contenida dentro de PerlasInicioActivity y debería mostrar lo siguiente:

  • Nombre y versión de la aplicación.
  • Un gráfico que identifique las perlas para la pantalla de inicio.
  • Una pequeña pausa para dar paso a la pantalla de menú.

Menú principal

El menú es nuestra pantalla de navegación que nos llevará a las opciones propias del juego. Su funcionalidad debería estar contenida dentro de PerlasMenuActivity y debería mostrar lo siguiente:

  • Se muestra después de la pantalla de inicio.
  • Contiene las opciones: Puntuar perlas, ir a la configuración, ver los marcadores y mostrar una pequeña ayuda.

Pantalla de ayuda

La pantalla de ayuda es la típica en una apliación. En este caso muestra una breve explicación de que son de las perlas y cómo jugar. Su funcionalidad debería estar contenida dentro de PerlasAyudaActivity  y debería mostrar lo siguiente:

  • Descripción de perla.
  • Normas y características de puntuación.

Scoreboard o puntuaciones

Es el ranking del juego. Muestra los diez primeros ‘perleros’ activos del mes. Su funcionalidad debería estar contenida dentro de PerlasScoreBoardActivity  y debería mostrar lo siguiente:

  • Un listado con las puntuaciones y el nombre del ‘perlero’

Configuración de la aplicación de las perlas

Aquí desplegamos y mostramos los ‘settings’ de la aplicación. Muestra algunas opciones que podemos modificar . Su funcionalidad debería estar contenida dentro de PerlasConfigActivity  y debería mostrar lo siguiente:

  • Registro del usuario
  • Datos modificables a través del interfaz.

Pantalla de juego de la aplicación de las perlas

Es el juego en sí. Consiste en una pantalla donde poder puntuar visualizando las perlas de el mes en curso. Su funcionalidad debería estar contenida dentro de PerlasGameActivity  y debería mostrar lo siguiente:

  • Nombre del perlero
  • Perla en texto.
  • Slider para definir cuantos puntos votamos.
  • Botón para votar.
Empecemos por crear cada una de las clases con la llamada a su setContentView( view )  y así poder crear tambien los layouts del prototipo de cada pantalla. Será en la siguiente entrega ! Hasta la próxima.

 



AndroidManifest.xml, una declaración de intenciones.

Cada aplicación en Android lleva su AndroidManifest.xml en el directorio raíz. Determina, antes de la ejecución de la aplicación, que acciones y permisos requiere la misma para ser ejecutada. Como en cualquier sistema linux, las aplicaciones Android están determinadas por los permisos de usuario. Si nuestra aplicación necesita hacer uso de la agenda de contactos, debemos especificarlo en el AndroidManifest.xml. La aplicación se ejecuta en un entorno linux con sus permisos de usuario. Aparte de otras cosas, AndroidManifest.xml se encarga de :

  • Nombra el paquete JAVA. Cada paquete JAVA lleva un identificador único en la aplicación.
  • Describe cada uno de los componentes de la aplicación — activities, services y broadcast receivers junto con content providers de los que nuestra aplicación hace uso. Nombra las clases que implementan cada uno de sus componentes y publica sus posibles opciones. Si necesitas acceder a una Activity o un Intent, lo declaramos.
  • Determina que procesos implementa.
  • Declara los permisos que necesita nuestra aplicación.
  • Declara, asimismo, que permisos requieren otras partes para interactuar con nuestra aplicación.
  • Permite definir niveles mínimos y máximo de SDK en los que nuestra aplicación será compatible.
  • Lista las librerias que son enlazadas a nuestra aplicación.
Por norma general, Eclipse te ofrece siempre dos vistas para modificar tanto los res/values/strings.xml (cadenas de textos de tu aplicación), los res/layout/main.xml o el propio AndroidManifest.xml, una completamente visual que rellenas a través de formularios o visualizas arrastrando elementos (caso del layout) y otra como editor de textos puro y duro, directamente al xml. Siempre es recomendable, una vez que conocemos cada una de las partes de nuestros ficheros xml, ir directamente al editor y crear nuestros ficheros utilizando solo texto, ya que en ocasiones no todas las opciones están disponibles en todos los casos.

Tipos de permisos

 

Tipo de permiso

Constante AndroidManifest.xml

Servicios basados en localización

 android.permission.ACCESS_COARSE_LOCATION
android.permission.ACCESS_FINE_LOCATION

Acceso a contactos

android.permission.CALL_PHONE
android.permission.CALL_PRIVILEGED

Enviar y recibir SMS

android.permission.READ_SMS
android.permission.RECEIVE_MMS
android.permission.RECEIVE_SMS
android.permission.RECEIVE_WAP_PUSH
android.permission.SEND_SMS
android.permission.WRITE_SMS

Sockets de red

 android.permission.INTERNET

Configuración audio

android.permission.RECORD_AUDIO
android.permission.MODIFY_AUDIO_SETTINGS

Configuración de red

android.permission.ACCESS_NETWORK_STATE

Configuración WiFi

 android.permission.ACCESS_WIFI_STATE
android.permission.CHANGE_WIFI_STATE

Configuración hardware del dispostivo

 android.permission.BLUETOOTH
android.permission.CAMERA
android.permission.FLASHLIGHT
android.permission.VIBRATE
android.permission.BATTERY_STATS

En resumen, para empezar a entender el alcance de tu aplicación es importante conocer a fondo el fichero AndroidManifest.xml. Para la próxima entrega veremos un ejemplo práctico de aplicación y comenzaremos con los Activities, clases de JAVA donde se empieza a crear la interacción usuario-aplicación y donde podemos desplegar nuestro User Interface con setContentView(View). Hasta la próxima.



Android SDK: Instalación y configuración del entorno con Eclipse

Lo primero que debemos hacer antes de instalar el SDK de Android es asegurarnos de cumplir con los  requisitos exigidos, en particular los del JDK.

 

Paso 1: Descarga y preparación de Eclipse

Si eres nuevo, lo mejor es que empieces a desarrollar con Android Development Tools (ADT) en Eclipse. Asegúrate de descargar una versión clásica del entorno Eclipse o Eclipse Java / RCP para empezar a integrar tus aplicaciones con Android SDK.

 

Paso 2: Descarga  Android-SDK

Puedes hacerlo para los distintos sistemas operativos aquí: http://developer.android.com/sdk/index.html. En Windows tienes un bonito instalador .exe mientras que en Mac OS X (intel) y Linux (i386) lo tendrás empaquetado como tgz o zip . No tendrás mas que decirle en que  directorio quieres que te descomprima el SDK, por defecto lo hace en  android-sdk-<machine-platform>. Tenlo en cuenta porque posteriormente te pedirán este path desde eclipse, para configurar el ADT plugin.

 

Paso 3: Instalación del plugin ADT en Eclipse

Android ha diseñado un plugin para Eclipse para integrar todo lo relacionado con el desarrollo de aplicaciones Android desde Eclipse. Desde la depuración de la aplicación a la maquina virtual o el dispositivo real mediante USB (DDMS) hasta la firmna de paquetes APK para su distribución. Eclipse es el entorno recomendado para desarrollar aplicaciones Android, sin duda, gracias al plugin ADT.

Para instalar la última versión del plugin ADT y partiendo de una instalación de Eclipse compatible deberás realizar uan serie de pasos desde el gestor de actualizaciones de Eclipse (Update manager).

1. Haz click en Ayuda -> Instalar nuevo software

2. Añade el repositorio de Google: https://dl-ssl.google.com/android/eclipse/.

3. Aceptar todos los acuerdos de licencia.

4. Esperar a que se descargue todo.

5. Reiniciar Eclipse.

* Si por algún motivo la instalación en este punto fallara o no te dejara continuar, prueba a reinstalar Eclipse, desinstalando e instalando de nuevo.

 

Configurando el plugin ADT

Ve a las preferencias de Eclipse y selecciona ‘Android’. Indícale donde tienes el Android SDK dándole el path correspondiente. Si en algún momento necesitas actualizarlo ve a Help -> Check Updates desde Eclipse.

 

 Imagen 1: Ubicación del SDK de Android. 

 

¡ Ya tienes todo listo !

Ahora te toca pensar en una idea de aplicación. ¿Estás preparado? Estate atento porque voy a seguir con una serie de artículos dedicado a la post-instalación del entorno. Empezando por explicar que es eso de los Activities, los intents, los services, el Android Manifest y los principios más básicos para empezar a programar.

 

Ya tienes todo lo que necesitas. ¿Eres capaz de crear tu propia aplicación?