En este post vamos a recuperar el lector de RSS creado en un tutorial anterior añadiéndole funcionalidad para que el usuario pueda escoger los RSS que quiera ver. Los vamos a guardar en la base de datos SQLite de la aplicación.
El módulo Titanium.Database tiene dos objetos:
- Titanium.Database.DB: que es la instancia de la base de datos
- Titanium.Database.ResultSet: que es la instancia que se devuelve al ejecutar una sentencia SQL
Para empezar, tenemos que crear una base de datos SQLite y añadirla a la carpeta de recursos del proyecto.
Hay varios programas con interfaz gráfica para crear una base de datos SQLite fácilmente. Por destacar uno, SQLite Manager para Firefox (https://addons.mozilla.org/es-es/firefox/addon/sqlite-manager/).
Con éste programa (o cualquier otro) creamos la base de datos “rssReader.sqlite” y la guardamos dentro de la carpeta “Resources/db”. La base de datos solo contendrá una tabla llamada “feeds” con las columnas:
- url (varchar)
- name (varchar)
Para mantener una buena estructura del código, creamos un nuevo fichero javascript donde vamos a poner el código necesario para acceder a la base de datos y realizar las operaciones que necesitamos:
- consultar todos los feeds: readAvailableFeeds
- añadir un feed: addFeed
- eliminar un feed: removeFeed
El fichero lo hemos llamado “configReader.js” dentro del directorio “db”:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
function ConfigReader() {
// Instala la base de datos si no lo está
Titanium.Database.install('/db/rssReader.sqlite','feeds');
this.readAvailableFeeds = function() {
var db = Titanium.Database.open('feeds');
var resultSet = db.execute('select * from feeds');
var result = new Array();
// Pasamos el resultSet a un objeto para poderlo cerrar aquí
// mismo y no tener que ir cerrándolo en los lugares en que se use
while (resultSet.isValidRow()) {
var row = new Object();
row["name"]=resultSet.fieldByName("name");
row["url"]=resultSet.fieldByName("url");
result[result.length] = row;
resultSet.next();
}
// Siempre debemos cerrar el resultSet
resultSet.close();
db.close();
return result;
}
this.removeFeed = function(feedUrl) {
var db = Titanium.Database.open('feeds');
db.execute ('delete from feeds where url=?',feedUrl);
db.close();
}
this.addFeed = function(name, url) {
var db = Titanium.Database.open('feeds');
db.execute ('insert into feeds(name, url) values (?,?)',name, url);
db.close();
}
}
A destacar del código:
- Para utilizar una base de datos, primero se tiene que instalar con la llamada:Titanium.Database.install('/db/rssReader.sqlite','feeds') indicando la ruta del fichero dentro de la carpeta de recursos y el nombre que se utilizará. Si la base de datos ya está instalada, la llamada a la función no hace nada
- Se tiene que cerrar el RecordSet y la conexión a la base de datos una vez se haya acabado de utilizar
La función readAvailableFeeds convierte el ResultSet en un objeto para evitar devolver el ResultSet. De éste modo, el código que llame a la función no tiene que saber como navegar por el ResultSet ni cerrarlo.
Según la API, la llamada a next() del ResultSet incrementa el cursor y devuelve falso si ya no hay mas elementos, por lo que se podría utilizar directamente en el bucle:
1
while (resultSet.next())
sin embargo, en las pruebas que hemos realizado, la función siempre devolvía undefined por lo que se ha optado por navegar el bucle de forma alternativa.
Una vez creado el objeto para acceder a los datos, los siguientes pasos son:
- La ventana que muestra los RSS, tiene que obtener las urls de la base de datos
- Crear una interfaz para añadir y eliminar nuevos feeds
Ventana de listado
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
Titanium.include('/data/Reader.js');
Titanium.include('/db/configReader.js');
var w = Titanium.UI.currentWindow;
w.setBackgroundColor('#000');
var scrollView = Titanium.UI.createScrollView({
showVerticalScrollIndicator:true
});
scrollView.add(tableView = Titanium.UI.createTableView({
height: 400
}));
var tableview = Titanium.UI.createTableView();
w.add(tableview);
// Añadimos el handler de click en la tabla
tableview.addEventListener('click', function(e) {
var detail = Ti.UI.createWindow({
title:e.row.hTitle
});
var wb = Ti.UI.createWebView({
url:e.row.url
});
detail.add(wb);
detail.open({modal:true});
});
// Menú de opciones
var activity = Ti.Android.currentActivity;
activity.onCreateOptionsMenu = function(e) {
var menu = e.menu;
var itemRecargar = menu.add({ title: "Recargar"});
itemRecargar.addEventListener('click', function() {
loadRss();
});
var itemConfigurar = menu.add({ title: "Configurar"});
itemConfigurar.addEventListener('click', function() {
openConfiguration();
});
}
function openConfiguration() {
var config = Titanium.UI.createWindow({
url: "config.js",
title: "Configuración"
});
config.open({modal:true});
}
// Variable para guardar los datos que se mostrarán en la vista
var data;
// Cargamos los RSS
loadRss();
function loadRss() {
data = new Array();
var result = new ConfigReader().readAvailableFeeds();
for (var i=0; i<result.length; i++) {
new Reader(result[i].url, printRssStream);
}
}
// Rellena la variable data con los datos del rss
function printRssStream(result) {
for (var i=0; i<result.entries.length; i++) {
var entry = result.entries[i];
var row = Titanium.UI.createTableViewRow();
if (i==0) {
// Si es la primera celda, le ponemos la cabecera
row.header=result.title;
}
if (entry.media!=null) {
var img = Ti.UI.createImageView({
image: entry.media,
left:5,
height:60,
width:60
});
row.add(img);
var label = Ti.UI.createLabel({
text: entry.title,
left:72,
top:5,
bottom:5,
right:5
});
row.add(label);
} else {
var label = Ti.UI.createLabel({
text: entry.title,
left:5,
top:5,
bottom:5,
right:5
});
row.add(label);
}
row.url = entry.url;
row.hTitle = entry.title;
data[data.length] = row;
}
tableview.setData(data);
}
Titanium.App.addEventListener('newFeed', function (e) {
loadRss();
});
Los cambios en la ventana del listado son básicamente tres:
- La función loadRss() ha cambiado para obtener las urls de la base de datos
- Se ha añadido una nueva opción en el menú para ir a la ventana de configuración
- La ventana "escucha" el evento newFeed y recarga los feeds en recibirlo. De éste modo, al añadir un feed nuevo aparecerá automáticamente en el listado
Ventana de configuración
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
Titanium.include('/db/configReader.js');
var w = Titanium.UI.currentWindow;
w.setBackgroundColor('#000');
var checks = new Array();
w.add(Titanium.UI.createLabel({
text: "Feeds actuales",
top: 5
}));
w.add(scrollView = Titanium.UI.createScrollView({
top: 20
}));
scrollView.add(tableView = Titanium.UI.createTableView({
height: 400
}));
var configReader = new ConfigReader();
var feeds;
loadAvailableFeeds();
function loadAvailableFeeds() {
feeds = new Array();
var result = configReader.readAvailableFeeds();
for (var i=0; i<result.length; i++) {
var tableRow = Titanium.UI.createTableViewRow();
tableRow.add(Titanium.UI.createLabel({
text: result[i].name,
textAlign: "left"
}));
checks[checks.length] = Titanium.UI.createSwitch({
top:2,
left:2,
style: Titanium.UI.Android.SWITCH_STYLE_CHECKBOX,
feedUrl: result[i].url
});
tableRow.add(checks[checks.length-1]);
feeds[feeds.length] = tableRow;
}
tableView.setData(feeds);
}
// Menú de opciones
var activity = Ti.Android.currentActivity;
activity.onCreateOptionsMenu = function(e) {
var menu = e.menu;
var itemRemove = menu.add({
title: "Eliminar"
});
itemRemove.addEventListener('click', function() {
removeChecked();
});
var itemAddFeed = menu.add({
title: "Añadir"
});
itemAddFeed.addEventListener('click', function() {
var newWindow = Titanium.UI.createWindow({
url: "add.js",
title: "Añadir feed"
});
newWindow.open({
modal:true,
backgroundColor: 'green'
});
});
}
function removeChecked() {
for (var i=0; i<checks.length; i++) {
if (checks[i].value) {
// Debemos eliminar el feed correspondiente
configReader.removeFeed(checks[i].feedUrl);
}
}
loadAvailableFeeds();
}
Titanium.App.addEventListener('newFeed', function (e) {
loadAvailableFeeds();
});
La ventana de configuración obtiene todos los feeds disponibles y los muestra en un listado con un selector para cada uno.
También crea un menú con las opciones para añadir un nuevo feed y para eliminar. En caso de seleccionar la opción de eliminar, se eliminan todos los feeds seleccionados.
La ventana “escucha” el evento newFeed para actualizarse automáticamente si se añade un nuevo feed.
Ventana de nuevo feed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
Titanium.include('/db/configReader.js');
var w = Titanium.UI.currentWindow;
w.setBackgroundColor('#000');
w.add(Titanium.UI.createLabel({
text: "Nuevo feed",
top: 5
}));
var table = Titanium.UI.createTableView({
top: 20
});
var row = Titanium.UI.createTableViewRow();
row.add(labelName = Titanium.UI.createLabel({
text: "Nombre:",
left: 0
}));
row.add(fieldName = Titanium.UI.createTextField({
left: 60
}));
table.appendRow(row);
var row = Titanium.UI.createTableViewRow();
row.add(labelUrl = Titanium.UI.createLabel({
text: "Url:",
left: 0
}));
row.add(fieldUrl = Titanium.UI.createTextField({
left: 60,
value: "http://"
}));
table.appendRow(row);
row = Titanium.UI.createTableViewRow();
row.add(save = Titanium.UI.createButton({
title: "Guardar"
}));
table.appendRow(row);
w.add(table);
save.addEventListener('click', function() {
var name = fieldName.value;
var url = fieldUrl.value;
new ConfigReader().addFeed(name, url);
Titanium.App.fireEvent('newFeed');
w.close();
});
La ventana de nuevo feed monta una página para introducir un nombre y url para el feed asi como un botón de “Guardar” para añadirlo a la lista.
Después de añadir el nuevo feed, lanza el evento newFeed que escuchan las otras dos ventanas para actualizarse en el momento.