46
Formación en Tecnologías Java CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-1 App Android: Desarrollo de aplicaciones en Android. Tutorial: Lector RSS de CAMON Material complementario (para hacer “copy/paste” del código) proporcionado a los asistentes de la charla-taller impartida en CAMON Alicante el 11-12 de enero de 2011. Boyán Bonev, Pablo Suau, Miguel A. Lozano y el Dep. CCIA de la Universidad de Alicante

02 Tutorial LectorRSS CAMON

Embed Size (px)

Citation preview

Page 1: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-1

App Android:Desarrollo de aplicaciones en Android.

Tutorial: Lector RSS de CAMONMaterial complementario (para hacer

“copy/paste” del código) proporcionado a los asistentes de la charla-taller impartida en

CAMON Alicante el 11-12 de enero de 2011.

Boyán Bonev, Pablo Suau, Miguel A. Lozano y el Dep. CCIA de la Universidad de Alicante

Page 2: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-2

Puntos a tratar

• Creación del proyecto con Eclipse• Manifest, actividad principal y recursos• Vista de tabla, layout para las filas, adaptador• Descarga y parsing de XML en segundo plano• Descarga de imágenes en segundo plano• Diálogo, menú, cambios de orientación• Intents para síntesis del habla y navegador• Generar paquete y firmarlo

Page 3: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-3

• Instalación: • Descargar y descomprimir Android SDK• Instalación del plug-in para Eclipse:

• Help > Install new software > Available software > Add: https://dl-ssl.google.com/android/eclipse/

• Ok, seleccionar el software, Next, Finish.

• Reiniciar Eclipse.

• Configuración de Eclipse:• Windows > Preferences > Android > SDK Location:

– Indicamos la ruta donde hemos descomprimido el Android SDK

Creación del proyecto con Eclipse

Page 4: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-4

AVD Manager

• Crear nuevo dispositivo virtual (AVD)

• Seleccionar la versión de Android

Page 5: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-5

Emulador

Page 6: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-6

Crear el proyecto

• Asistente de Eclipse• Genera la estructura

básica del proyecto• AndroidManifest.xml• Actividad principal• Layout• Recursos

• Conviene seleccionar la mínima versión posible

Page 7: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-7

Añadir recursos

• En values/strings.xml las cadenas de texto que vamos a mostrar en la interfaz

• En drawable, el icono de CAMON a alta, media y baja resolución

• En layout/main.xml la disposición de los componentes de la interfaz gráfica

Page 8: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-8

strings.xml

• Introducimos (en la pestaña XML):<?xml version="1.0" encoding="utf-8"?><resources><string name="app_name">CAMON</string><string name="url_base">http://www.tucamon.es</string><string name="url_rss">http://www.tucamon.es/welcome/rss?format=rss</string>

<string name="noitems">Lista vacía</string><string name="errordered">¡Error en la descarga!</string>

<string name="calla">¡Calla!</string><string name="recargar">Recargar</string><string name="acerca_de">Acerca de...</string><string name="acerca_de_camon">Acerca de CAMON</string><string name="licencia">Esta aplicación descarga las noticias RSS de www.tucamon.es y las muestra. Fue desarrollada en el taller de Android llevado a cabo en el aula de CAMON de Alicante en enero de 2011, por los asistentes y por Boyán Bonev (Universidad de Alicante) Esta aplicación no es \"oficial\" de CAMON, no se puede vender y su finalidad es didáctica. La Universidad de Alicante y CAMON no se responsabilizan de su uso y modificaciones hechas por terceros.</string>

<string name="aceptar">Aceptar</string><string name="ok">OK</string><string name="leemelo">Léemelo</string><string name="web">Web</string><string name="atras">Atrás</string>

<string name="espere">Espere...</string><string name="descargandonoticias">Descargando noticias</string><string name="descargandonoticia">Descargando noticia</string><string name="imagenes_descargadas">Imágenes descargadas</string>

</resources>

Page 9: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-9

Drawable

• En las carpetas drawable introducimos el icono de la CAMON, redimensionado a tres resoluciones diferentes:• drawable-hdpi/icon.png de 72x72 píxeles • drawable-mdpi/icon.png de 48x48 píxeles• drawable-ldpi/icon.png de 36x36 píxeles

Page 10: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-10

layout/main.xml

• Introducimos un ListView que será la tabla donde iremos colocando las noticias de la web de CAMON. De momento no producirá ningún resultado visible.

<?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" >

<ListView android:id="@+id/ListView01" android:layout_width="fill_parent" android:layout_height="fill_parent"android:scrollbars="vertical" />

</LinearLayout>

Page 11: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-11

AndroidManifest.xml

• Especificaremos que la aplicación necesita permisos para acceder a Internet

• El XML quedaría así:<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="es.tucamon" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".Camon" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>

</application> <uses-sdk android:minSdkVersion="4" /> <uses-permission android:name="android.permission.INTERNET"/></manifest>

Page 12: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-12

Actividad principal

• es.tucamon.Camon.java• Añadimos dos cadenas que nos harán falta,

recogiéndolas de los recursos string.• Añadimos una variable Context porque

accederemos a ella varias veces.public class Camon extends Activity {

private static String URL_BASE;private static String URL_RSS;

Context context; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Context = getApplicationContext();

URL_BASE = getString(R.string.url_base); URL_RSS = getString(R.string.url_rss);

}}

Page 13: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-13

Acceso a componentes del layout

• Obtenemos una referencia utilizando el identificador del elemento.

• Asignamos un Listener para clicks sobre los items de la tablapublic class Camon extends Activity {

private static String URL_BASE;private static String URL_RSS;

Context context;

ListView listView;

/** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Context = getApplicationContext();

URL_BASE = getString(R.string.url_base); URL_RSS = getString(R.string.url_rss);

listView = (ListView)findViewById(R.id.ListView01); listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int position, long id) { // en "id" tenemos el número de fila seleccionada

} }); }}

Page 14: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-14

Clase noticia

• Creamos una nueva clase Noticia.java en el mismo paquetepublic class Noticia { private String titulo; private Spanned descripcion; private String link; private String fecha; private String linkImagen; private Drawable imagen;

public Noticia(){ titulo = ""; descripcion = new SpannedString(""); link = ""; fecha = ""; linkImagen = ""; } public Noticia(String titulo, String fecha, Spanned descripcion, String link,String linkImagen){ this.titulo=titulo; this.fecha=fecha; this.descripcion=descripcion; this.link=link; this.linkImagen=linkImagen; }

public String getTitulo() { return titulo; } public void setTitulo(String titulo) { this.titulo = titulo; } public Spanned getDescripcion() { return descripcion; } public void setDescripcion(Spanned descripcion) { this.descripcion = descripcion; } public String getLink() { return link; } public void setLink(String link) { this.link = link; } public String getFecha() { return fecha; } public void setFecha(String fecha) { this.fecha = fecha; } public String getLinkImagen() { return linkImagen; } public void setLinkImagen(String linkImagen) { this.linkImagen = linkImagen; } public void setImagen(Drawable imagen) { this.imagen = imagen; } public Drawable getImagen() { return imagen; }}

Page 15: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-15

Layout para las filas

• En la carpeta res/layout creamos un nuevo xml llamado fila.xml

• Le añadimos una imagen con identificador @+id/FilaImagen y al lado de ella un Layout que disponga los componentes verticalmente, con dos campos de texto: @+id/FilaTexto1 y @+id/FilaTexto2

<?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="wrap_content" android:orientation="horizontal">

<ImageView android:id="@+id/FilaImagen" android:layout_width="100px" android:layout_height="70px" />

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

<TextView android:id="@+id/FilaTexto1" android:textSize="12sp" android:textColor="#E1DEFF" android:textStyle="bold" android:layout_width="fill_parent" android:layout_height="wrap_content"/>

<TextView android:id="@+id/FilaTexto2" android:textSize="10sp" android:layout_width="fill_parent" android:layout_height="wrap_content"/>

</LinearLayout></LinearLayout>

Page 16: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-16

Adaptador para la tabla

• NoticiasAdapter colocará la información de los objetos Noticia en el layout fila.xml, para cada fila (para cada noticia)

public class NoticiasAdapter extends ArrayAdapter<Noticia> { ArrayList<Noticia> noticias; Context context;

public NoticiasAdapter(Context context, int textViewResourceId, ArrayList<Noticia> noticias) { super(context, textViewResourceId, noticias); this.noticias = noticias; this.context = context; }

@Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null) { LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.fila, null); } Noticia noticia = noticias.get(position); if (noticia != null) { TextView tv1 = (TextView) convertView.findViewById(R.id.FilaTexto1); TextView tv2 = (TextView) convertView.findViewById(R.id.FilaTexto2); ImageView iv = (ImageView) convertView.findViewById(R.id.FilaImagen); if (tv1 != null) { tv1.setText(noticia.getTitulo()); } if (tv2 != null) { tv2.setText(noticia.getFecha()); } if (iv != null) { iv.setImageDrawable(noticia.getImagen()); } } return convertView; }}

Page 17: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-17

Adaptador para la table

• Declaramos una lista de noticias y el adaptador

• Asignamos el adaptador a la tabla (listView) dentro del método onCreate

public class Camon extends Activity {

private static String URL_BASE;private static String URL_RSS;

Context context;ListView listView;ArrayList<Noticia> noticias;NoticiasAdapter noticiasAdapter;

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Context = getApplicationContext();

URL_BASE = getString(R.string.url_base); URL_RSS = getString(R.string.url_rss);

noticias = new ArrayList<Noticia>(); noticiasAdapter = new NoticiasAdapter(context, R.layout.fila, noticias);

listView = (ListView)findViewById(R.id.ListView01); listView.setAdapter(noticiasAdapter); listView.setOnItemClickListener(new OnItemClickListener() {

Page 18: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-18

Descarga y parsing de XML en segundo plano• Las operaciones lentas deben realizarse en

segundo plano, de lo contrario la aplicación dejará de responder y Android nos ofrecerá matarla.

• Cualquier operación de red se puede considerar una operación lenta.

• Para crear procesos en segundo plano podemos utilizar Threads. • El problema: no podemos acceder a los componentes

gráficos desde otro hilo que no sea el principal.

• La solución: Android nos ofrece las AsyncTask que facilitan el acceso a los componentes gráficos

Page 19: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-19

Declaración de AsyncTask

• La declaramos dentro de la clase Camon para poder acceder a sus campos (sus variables)

public class Camon extends Activity {

private static String URL_BASE; private static String URL_RSS;

Context context; ListView listView; ArrayList<Noticia> noticias; NoticiasAdapter noticiasAdapter; TareaDescarga tarea;

@Override public void onCreate(Bundle savedInstanceState) { // ... lanzaDescargaDeNoticias(); }

void lanzaDescargaDeNoticias(){ try { tarea = new TareaDescarga(); tarea.execute(new URL(URL_RSS)); } catch (MalformedURLException e) { e.printStackTrace(); } } private class TareaDescarga extends AsyncTask<URL, String, List<Noticia>>{ // ... }}

Page 20: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-20

Métodos de AsyncTaskprivate class TareaDescarga extends AsyncTask<URL, String, List<Noticia>>{ ArrayList<Noticia> noticiasDescargadas; ProgressDialog progressDialog; boolean error=false;

@Override protected List<Noticia> doInBackground(URL... params) {

return noticiasDescargadas; }

@Override protected void onPreExecute() {

}

@Override protected void onCancelled() {

}

@Override protected void onProgressUpdate(String... values) {

}

@Override protected void onPostExecute(List<Noticia> result) {

}}

Page 21: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-21

Métodos de AsyncTask

• El método doInBackground( ) realizará la descarga pero no podrá acceder a la interfaz; lo hará solicitando la ejecución de onProgressUpdate( ) y onPostExecute( )

• También declaramos un ProgressDialog que debemos ir actualizando durante el progreso de la descarga (y parsing de XML).

Page 22: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-22

AsyncTask – acceso a interfaz@Overrideprotected void onPreExecute() { super.onPreExecute(); noticiasAdapter.clear(); progressDialog = ProgressDialog.show(Camon.this, getString(R.string.espere), getString(R.string.descargandonoticias),true,true); progressDialog.setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { tarea.cancel(true); } });}

@Overrideprotected void onCancelled() { super.onCancelled(); noticiasAdapter.clear(); noticiasDescargadas = new ArrayList<Noticia>();}

@Overrideprotected void onProgressUpdate(String... progreso) { super.onProgressUpdate(progreso); progressDialog.setMessage(progreso[0]); noticiasAdapter.notifyDataSetChanged();}

@Overrideprotected void onPostExecute(List<Noticia> result) {super.onPostExecute(result); for(Noticia n:noticiasDescargadas){ noticiasAdapter.add(n); } noticiasAdapter.notifyDataSetChanged(); progressDialog.dismiss(); if(error){ Toast.makeText(context, R.string.errordered, Toast.LENGTH_LONG).show(); }}

Page 23: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-23

AsyncTask – doInBackground( )

@Overrideprotected List<Noticia> doInBackground(URL... params) { try { URL url = params[0]; noticiasDescargadas = new ArrayList<Noticia>();

XmlPullParserFactory parserCreator = XmlPullParserFactory.newInstance(); XmlPullParser parser = parserCreator.newPullParser(); parser.setInput(url.openStream(), null); int parserEvent = parser.getEventType(); int nItems = 0;

while (parserEvent != XmlPullParser.END_DOCUMENT) { switch (parserEvent) { // Examinar la información parseada // ... } parserEvent = parser.next();

} } catch (Exception e) { Log.e("Net", "Error in network call", e); error = true; } return noticiasDescargadas;}

• Utilizaremos XmlPullParser para trocear el documento XML.

Page 24: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-24

Información a parsear

• Código fuente descargado de http://www.tucamon.es/welcome/rss?format=rss

<?xml version="1.0" encoding="UTF-8"?><rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"> <channel> <atom:link type="application/rss+xml" rel="self" href="http://www.tucamon.es/contenido/rss"/> <title>Feed de art&#237;culos</title> <link>http://www.tucamon.es/</link> <description>Feed de art&#237;culos</description> <language>es-ES</language> <item> <title>Presentaci&#243;n de Los zapatos de tac&#243;n rojos</title> <description>&lt;p&gt;El &lt;strong&gt; ... &lt;img alt=&quot;Zapatos&quot; src=&quot;/photo_posts/0000/8463/zapatos.jpg?1293014230&quot; /&gt;</description> <author>(Chivone)</author> <pubDate>Wed, 22 Dec 2010 11:46:57 +0100</pubDate> <link>/contenido/presentacion-de-los-zapatos-de-tacon-rojos</link> <guid>/contenido/presentacion-de-los-zapatos-de-tacon-rojos</guid> </item> <item> <title>D-FORMA inaugura su exposici&#243;n en CAMON Madrid</title> <description>&lt;p&gt;El colectivo D-Forma present&amp;oacute; en CAMON&amp; ... </description> <author>(Silvia Mu&#241;oz Garc&#237;a)</author> <pubDate>Tue, 21 Dec 2010 19:56:47 +0100</pubDate> <link>/contenido/d-forma-emulo-exposicion-paisaje-terro</link> <guid>/contenido/d-forma-emulo-exposicion-paisaje-terro</guid> </item> ...

Page 25: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-25

El bucle del XmlPullParser while (parserEvent != XmlPullParser.END_DOCUMENT) { switch (parserEvent) { case XmlPullParser.START_TAG: String tag = parser.getName(); if (tag.equalsIgnoreCase("item")) { publishProgress(getString(R.string.descargandonoticia)+" "+(++nItems)); Noticia noticia = new Noticia(); parserEvent = parser.next(); boolean itemClosed = false; while (parserEvent != XmlPullParser.END_DOCUMENT && !itemClosed) { switch (parserEvent) { case XmlPullParser.START_TAG: tag = parser.getName(); if (tag.equalsIgnoreCase("title")) { noticia.setTitulo(parser.nextText()); } if (tag.toLowerCase().contains("pubdate")) { noticia.setFecha(parser.nextText()); } if (tag.equalsIgnoreCase("link")) { noticia.setLink(URL_BASE+parser.nextText()); } if (tag.equalsIgnoreCase("description")) { String textoHtml = parser.nextText(); Spanned texto = Html.fromHtml(textoHtml, new ImageGetter(), null);

noticia.setDescripcion(texto);

String linkImagen = URL_BASE+imageSource; noticia.setLinkImagen(linkImagen); } break; case XmlPullParser.END_TAG: tag = parser.getName(); if(tag.equalsIgnoreCase("item")){ itemClosed = true; noticiasDescargadas.add(noticia); } break; } parserEvent = parser.next(); } } break; } parserEvent = parser.next();}

String imageSource=null; class ImageGetter implements Html.ImageGetter { public Drawable getDrawable(String source) { imageSource = source; return new BitmapDrawable(); } };

Page 26: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-26

Primer prototipo

• Tras descargar las noticias debe mostrarlas en la tabla

• El formato es el indicado por fila.xml y el adaptador que hemos implementado.

Page 27: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-27

Descarga de imágenes

• Añadimos a Noticia un método que descarga la imagen de la noticia, dada una URL

public class Noticia { private String titulo; private Spanned descripcion; private String link; private String fecha; private String linkImagen; private Drawable imagen; //...

public void loadImagen(String url) throws MalformedURLException, IOException{ InputStream is = (InputStream) new URL(url).getContent(); imagen = Drawable.createFromStream(is, "src"); }}

Page 28: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-28

Descarga de imágenes en segundo plano

private class TareaDescargaImagen extends AsyncTask<List<Noticia>, String, Drawable>{ @Override protected Drawable doInBackground(List<Noticia>... arg0) { List<Noticia> noticias = arg0[0]; int imagenesSinCargar = noticias.size(); while(imagenesSinCargar > 0){ imagenesSinCargar = noticias.size(); for(Noticia n:noticias){ if(n.getImagen()==null || n.getImagen().getIntrinsicHeight() <= 0){ // Reintento necesario? try{ n.loadImagen(n.getLinkImagen()); publishProgress(""); }catch(Exception e){ n.setImagen(getResources().getDrawable(R.drawable.icon)); } }else{ imagenesSinCargar --; } } try { Thread.sleep(1000); } catch (InterruptedException e) { } } return null; } @Override protected void onPostExecute(Drawable result) { super.onPostExecute(result); Toast.makeText(context, getString(R.string.imagenes_descargadas), Toast.LENGTH_SHORT).show(); noticiasAdapter.notifyDataSetChanged(); }

@Override protected void onProgressUpdate(String... values) { super.onProgressUpdate(values); noticiasAdapter.notifyDataSetChanged(); }}

Page 29: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-29

Iniciar la descarga

• Podemos iniciar la descarga de imágenes una vez que las noticias se hayan cargado.

private class TareaDescarga extends AsyncTask<URL, String, List<Noticia>>{

// ...

@Override protected void onPostExecute(List<Noticia> result) { super.onPostExecute(result); for(Noticia n:noticiasDescargadas){ noticiasAdapter.add(n); } noticiasAdapter.notifyDataSetChanged(); progressDialog.dismiss(); if(error){ Toast.makeText(context, R.string.errordered, Toast.LENGTH_LONG).show(); } lanzaDescargaDeImagenes(); }}

void lanzaDescargaDeImagenes(){ TareaDescargaImagen tdi = new TareaDescargaImagen(); tdi.execute(noticias);}

Page 30: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-30

Menú – el recurso XML

• Creamos un New Android XML File, de tipo menú y lo llamamos menu.xml. El asistente lo coloca en res/menu/menu.xml

• Lo editamos e introducimos tres items:

<?xml version="1.0" encoding="utf-8"?><menu xmlns:android="http://schemas.android.com/apk/res/android"><item android:id="@+id/item01" android:title="@string/calla"></item><item android:id="@+id/item02" android:title="@string/recargar"></item><item android:id="@+id/item03" android:title="@string/acerca_de"></item></menu>

Page 31: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-31

Menú – creación en la Activity principal

@Overridepublic boolean onCreateOptionsMenu(Menu m) { getMenuInflater().inflate(R.menu.menu, m); return true;} @Overridepublic boolean onOptionsItemSelected(MenuItem item) { switch(item.getItemId()){ case R.id.item01:

break; case R.id.item02: lanzaDescargaDeNoticias(); break; case R.id.item03:

break; } return true;}

Page 32: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-32

Diálogo “Acerca de”case R.id.item03:AlertDialog.Builder ab=new AlertDialog.Builder(Camon.this);ab.setTitle(R.string.acerca_de_camon);ab.setIcon(getResources().getDrawable(R.drawable.icon));ab.setMessage(R.string.licencia);ab.setPositiveButton(R.string.aceptar,null);ab.show();break;

Page 33: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-33

Diálogo con la noticia

• Creamos una clase en un fichero NoticiaAlertDialog.java, en el mismo paquete.public class NoticiaAlertDialog extends AlertDialog.Builder { private Noticia n; private Context c;

/** * After creating the NoticiaAlertDialog, don't forget to call .show(); * @param context The main Activity context. * @param noticia Noticia object with information on Noticia */ protected NoticiaAlertDialog(Context context, Noticia noticia) { super(context); n = noticia; c = context; this.setTitle(n.getTitulo()); this.setIcon(context.getResources().getDrawable(R.drawable.icon)); this.setMessage(n.getDescripcion()); this.setNegativeButton(context.getString(R.string.atras), null); this.setPositiveButton(context.getString(R.string.leemelo), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) {

} }); this.setNeutralButton(context.getString(R.string.web), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) {

} }); }}

Page 34: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-34

Mostrar diálogo con la noticia

• Lo mostraremos al hacer click sobre un item de la lista. Introducimos el código en el listener que ya teníamos asignado a la lista:

listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int position, long id) { Noticia n = noticias.get((int)id); NoticiaAlertDialog nad = new NoticiaAlertDialog(Camon.this, n); nad.show(); }});

Page 35: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-35

Cambios de orientación

• Cuando cambia la orientación del móvil de vertical a horizontal, Android la reinicia para que todos los componentes se coloquen de nuevo en la nueva configuración de pantalla.

• En consecuencia las noticias se empiezan a cargar de nuevo.

• Hay 3 maneras de evitarlo:• Deshabilitarlo: <activity android:name="Camon"

android:configChanges="keyboardHidden|orientation">

• Ocuparnos de recolocar los componentes (complicado).• Guardarnos las noticias para no volver a bajarlas.

Page 36: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-36

Cambios de orientación

public void onCreate(Bundle savedInstanceState) { // ... final ArrayList<Noticia> data = (ArrayList<Noticia>) getLastNonConfigurationInstance(); if (data == null) { noticias = new ArrayList<Noticia>(); noticiasAdapter = new NoticiasAdapter(context, R.layout.fila, noticias); lanzaDescargaDeNoticias(); }else{ noticias = data; noticiasAdapter = new NoticiasAdapter(context, R.layout.fila, noticias); } // ... } @Override public Object onRetainNonConfigurationInstance() { final ArrayList<Noticia> data = noticias; return data; }

Page 37: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-37

Abrir el navegador web con un intent

• Hay actividades que nos proporciona el sistema operativo, y que pueden ser ejecutadas por medio de un Intent. Abrimos el navegador web al pulsar el botón correspondiente:public class NoticiaAlertDialog extends AlertDialog.Builder {

// ...

protected NoticiaAlertDialog(Context context, Noticia noticia) {

// ...

this.setNeutralButton(context.getString(R.string.web), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(n.getLink())); c.startActivity( intent ); } }); }}

Page 38: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-38

Síntesis del habla

• Se utiliza la clase TextToSpeech

• Sólo se puede utilizar su método .speak( ) una vez que ha terminado su inicialización (implementar método onInit).

• Además es necesario tener instalado el TextToSpeech en el sistema operativo. Se puede hacer una comprobación y si no está instalado, lanzar la actividad que ofrece su instalación.

• Una vez terminado, hay que llamar al método .shutdown( ) del objeto de clase TextToSpeech, para que se liberen los recursos nativos involucrados.

Page 39: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-39

Speech• Creamos, en un fichero aparte, una clase que

encapsule toda la lógica necesaria para utilizar la síntesis del habla: Speech.java

package es.tucamon;import java.util.Locale;import android.app.Activity;import android.content.Intent;import android.speech.tts.TextToSpeech;import android.speech.tts.TextToSpeech.Engine;import android.speech.tts.TextToSpeech.OnInitListener;/** * Encapsulates some methods for using TextToSpeech from an Activity. * @author Boyan Bonev, Universidad de Alicante * @date December 20th, 2010 */public class Speech { private Activity activity; private TextToSpeech tts; private static final int TTS_DATA_CHECK = 1; private String text; private boolean isTTSinstalled = false; private boolean isTTSinitialized = false;

/** * Gets the reference to the Activity for being able to call Intents. * @param activity */ public Speech(Activity activity){ this.activity = activity; }

// ...}

Page 40: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-40

Métodos de Speech/** * Does not assume that TextToSpeech is installed but starts an intent * for checking whether it is or not. The Activity which calls this * function should implement: * protected void onActivityResult(int requestCode, int resultCode, Intent data) { * speech.installOrSpeak(requestCode,resultCode); * } * @param txt Text to speak */public void speakAfterCheckingForTTS(String txt){ this.text = txt; if(isTTSinitialized){ tts.speak(txt, TextToSpeech.QUEUE_ADD, null); }else{ Intent intent = new Intent(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA); activity.startActivityForResult(intent, TTS_DATA_CHECK); }}

/** * Assumes that TextToSpeech is installed * @param txt Text to speak */public void speakWithoutCheckingForTTS(String txt){ tts = new TextToSpeech(activity, new OnInitListener() { public void onInit(int status) { if (status == TextToSpeech.SUCCESS) { Locale loc = new Locale("es","",""); if (tts.isLanguageAvailable(loc) >= TextToSpeech.LANG_AVAILABLE) tts.setLanguage(loc); tts.setPitch(0.8f); tts.setSpeechRate(1.1f); isTTSinitialized = true; //Speak this: tts.speak(text, TextToSpeech.QUEUE_ADD, null); } } });}

/** * Called after checking for the installation of TTS * @param requestCode * @param resultCode */public void installOrSpeak(int requestCode, int resultCode){ if(requestCode == TTS_DATA_CHECK){ if (resultCode == Engine.CHECK_VOICE_DATA_PASS) { isTTSinstalled = true; speakWithoutCheckingForTTS(text); } else { Intent installVoice = new Intent(Engine.ACTION_INSTALL_TTS_DATA); activity.startActivity(installVoice); } } }

/** * Should be called in the onPause() or onStop() method of the Activity. */public void stop(){ if (tts != null) { tts.stop(); tts.shutdown(); isTTSinitialized = false; }}

public boolean isTTSinstalled() { return isTTSinstalled;}

Page 41: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-41

Uso de la clase Speech

• La declaramos en nuestra actividad Camon y la inicializamos en onCreate( ).

• Añadimos el método onActivityResult( ) e invocamos Speech.stop( ) cuando Camon termine su ejecución, en onStop( ), así como cuando se pulse el botón “Calla” del menú.

• Ya tenemos cuándo “callar”, ahora falta decidir cuándo hablar.

public class Camon extends Activity { Speech speech;

public void onCreate(Bundle savedInstanceState) {

// ... speech = new Speech(this);

// ... } protected void onActivityResult(int requestCode, int resultCode, Intent data) {

speech.installOrSpeak(requestCode,resultCode); } @Override public void onStop() { speech.stop(); super.onStop(); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch(item.getItemId()){ case R.id.item01: speech.stop(); break; case R.id.item02: lanzaDescargaDeNoticias(); break; case R.id.item03: // ... break; } return true; }}

Page 42: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-42

Speech en NoticiaAlertDialog

• Para pronunciar la descripción de la noticia al pulsar “leer” del NoticiaAlertDialog, le pasaremos por el constructor el objeto speech ya inicializado.

public class NoticiaAlertDialog extends AlertDialog.Builder { private Noticia n; private Context c; private Speech s;

protected NoticiaAlertDialog(Context context, Speech speech, Noticia noticia) { super(context); n = noticia; c = context; s = speech; this.setTitle(n.getTitulo()); this.setIcon(context.getResources().getDrawable(R.drawable.icon)); this.setMessage(n.getDescripcion()); this.setNegativeButton(context.getString(R.string.atras), null); this.setPositiveButton(context.getString(R.string.leemelo), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { s.speakAfterCheckingForTTS(n.getTitulo()+". "+n.getDescripcion()); } }); this.setNeutralButton(context.getString(R.string.web), new DialogInterface.OnClickListener() { // ... }); }}

Page 43: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-43

Pasar Speech por el constructor

• No olvidemos pasar el objeto speech como parámetro del constructor de NoticiaAlertDialog

• La aplicación ya permite escuchar una descripción mientras seguimos examinando la lista.

listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> arg0, View arg1, int position, long id){ Noticia n = noticias.get((int)id); NoticiaAlertDialog nad = new NoticiaAlertDialog(Camon.this, speech, n); nad.show(); }});

Page 44: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-44

Paquete

• Podemos generar un paquete instalable de prueba, firmándolo con un certificado “self-signed” (no firmado por ninguna autoridad certificadora).

• keytool -genkey -v -keystore mialmacen.keystore -alias aliasname -keyalg RSA -validity 10000• Introducimos una contraseña para el almacén y otra

para el certificado (puede ser la misma).

• Eclipse: sobre el proyecto, Android tools > Export signed package.• Nos pedirá abrir el almacén, y su contraseña.

Page 45: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-45

Probar el paquete

• Debemos guardarlo en el dispositivo móvil, copiándolo por cable o enviándolo por e-mail.• Si al enviar el .apk a gmail, y al intentar abrirlo da un

error, se puede probar renombrándolo a .zip; entonces el cliente de gmail nos permite guardarlo; le cambiamos el nombre a .apk (con un gestor de archivos) y lo ejecutamos.

• Al ejecutarlo se inicia el instalador que nos advierte que la aplicación necesita acceso a Internet.

Page 46: 02 Tutorial LectorRSS CAMON

Formación en Tecnologías Java

CAMON Alicante, 11-12 enero 2010 Depto. Ciencia de la Computación e IA (Univ. Alicante) App Android-46

¿Preguntas...?