🎲 Listas, TinyDB y Aleatorio
Guía de referencia para guardar datos, elegir elementos al azar y construir las funcionalidades de SonRISAS en App Inventor
📋 Listas
¿Qué es una Lista?
Una lista es una colección ordenada de elementos
Ejemplos de la vida real:
En App Inventor
Usamos el bloque make a list para crear listas
Lo encontrarás en el cajón Lists
Bloques de Listas
Los bloques de listas están en el cajón "Lists"
make a list
item: "hola"
item: "adiós"
Crea una lista con los elementos que le des
select list item
list: miLista
index: 2
Obtiene el elemento en la posición indicada
length of list
list: miLista
Devuelve cuántos elementos tiene la lista
add items to list
list: miLista
item: "nuevo"
Añade un elemento al final de la lista
🗃️ TinyDB
¿Qué es TinyDB?
TinyDB es la memoria permanente de tu app
Sin TinyDB
Cuando cierras la app, se pierde todo
Como escribir en una pizarra que se borra sola
Con TinyDB
Los datos se guardan para siempre
Como escribir en un cuaderno que guardas
TinyDB funciona con etiquetas (tags)
Cada dato se guarda con un nombre (etiqueta) para poder encontrarlo después:
Valor: lista de frases
Valor: lista de actividades
Bloques de TinyDB
Solo necesitas dos bloques principales
StoreValue - Guardar datos 💾
tag = "quotes"
valueToStore = mi lista de frases
Guarda un valor (puede ser una lista) con una etiqueta
GetValue - Recuperar datos 📖
tag = "quotes"
valueIfTagNotThere = lista vacía
Recupera el valor guardado. Si no existe, devuelve el valor por defecto.
Cargar Datos al Inicio
Cuando la app se abre por primera vez, necesitamos cargar los datos iniciales
¿Cuándo cargar? En Screen1.Initialize
Este evento se ejecuta cada vez que la app se abre. Pero solo queremos cargar los datos la primera vez.
Pasos en bloques:
cuando Screen1.Initialize
si length of list(TinyDB1.GetValue("quotes", make a list())) = 0
entonces TinyDB1.StoreValue(
tag = "quotes"
value = make a list(
"It's never too late to make a new friend"
"A smile can change someone's day"
"You are important to someone"
... more quotes ...
)
)
🎲 Números Aleatorios
¿Qué es un Número Aleatorio?
Un número aleatorio es un número elegido al azar
Ejemplo de la vida real:
En App Inventor: random integer
Genera un número al azar entre los dos valores
Lo encuentras en el cajón Math (Matemáticas)
La Combinación Mágica
Juntamos 3 piezas para elegir un elemento al azar de una lista
1. La lista
Recuperamos la lista de TinyDB
2. Número al azar
Generamos un índice aleatorio
3. Seleccionar
Elegimos el elemento en esa posición
En bloques:
select list item (
list: TinyDB1.GetValue("quotes")
index: random integer from 1 to length of list
)
🎨 Diseño Común (todas las pantallas)
Antes de programar, todas las pantallas necesitan la misma estructura base y barra de título
Paso 1: Contenedor principal
Cada pantalla necesita un VerticalArrangement que contenga todos los demás elementos
Cómo añadirlo en App Inventor
#FFFFD1
Paso 2: Barra de título (TitleBar)
El primer elemento dentro del contenedor es la barra de título, común a todas las pantallas (menos la de inicio de Elia)
Crear la barra de título
Subir la imagen home.png a App Inventor: en la columna de la derecha (Media), pulsar Upload File
Paso 3: Propiedades del Label de título
El Label del título tiene estas propiedades comunes: FontSize = 40, FontTypeface = serif
El texto y color de fondo cambian según la pantalla:
Resumen de propiedades del Label de título:
📇 Agenda de Contactos
Concepto
La funcionalidad principal del MVP: guardar contactos cercanos y recordar contactar con ellos
Pantalla Principal
Popup al tocar contacto
Pantalla Editar
Grandma Ana
+34 612 345 678
Flujo del usuario
Pantalla Principal: Componentes
Componentes visibles
Componentes no visibles
Estructura de datos en TinyDB
| "contacts" | lista de nombres: ["Grandma Ana", "Uncle Pedro", ...] |
| "contact_phone_Grandma Ana" | "+34 612 345 678" |
| "contact_days_Grandma Ana" | 3 (recordar cada 3 días) |
| "contact_last_Grandma Ana" | "2026-02-25" (fecha del último recordatorio) |
Añadir Contactos
Usamos la extensión PhoneNumberTools + ListPicker para mostrar solo contactos reales del teléfono
⚠️ ¿Por qué no usamos ContactPicker?
El componente nativo ContactPicker tiene un bug en Android 13+ que muestra el selector de archivos en vez de los contactos. La solución es usar la extensión PhoneNumberTools (io.mohamed.PhoneNumbersTools) para cargar los contactos del teléfono y un ListPicker para que el usuario elija.
Flujo con extensión + ListPicker
Variables globales necesarias
global phoneBookNames = make a list() // nombres originales (con duplicados)
global phoneBookPhones = make a list() // teléfonos originales (índice paralelo)
global uniqueNames = make a list() // nombres sin duplicados y ordenados (para el ListPicker)
Paso 1 — Cargar contactos al iniciar la pantalla:
cuando Screen1.Initialize:
call PhoneNumberTools1.GetContactNameListAsync
call PhoneNumberTools1.GetPhoneNumberListAsync
call loadContactList // cargar contactos guardados en TinyDB
call checkReminders // comprobar si hay que recordar contactar con alguien
Paso 2 — Guardar listas cuando la extensión termina de cargar:
cuando PhoneNumberTools1.GotContactNameList(nameList):
set global phoneBookNames = nameList
// Eliminar duplicados
set global uniqueNames = make a list()
para cada name en nameList:
si not is in list?(name, uniqueNames):
add items to list(uniqueNames, name)
// Ordenar alfabéticamente y asignar al ListPicker
set ListPickerAddContact.Elements = list sort(uniqueNames)
cuando PhoneNumberTools1.GotContactPhoneList(phoneList):
set global phoneBookPhones = phoneList
Paso 3 — Cuando el usuario elige un contacto:
cuando ListPickerAddContact.AfterPicking:
set name = ListPickerAddContact.Selection
// Buscar el teléfono por nombre en la lista original
set index = index in list(name, global phoneBookNames)
set phone = select list item(global phoneBookPhones, index)
// Recuperar lista actual de contactos guardados
set contactList = TinyDB1.GetValue("contacts", make a list())
// Añadir nuevo contacto
add items to list(contactList, name)
TinyDB1.StoreValue("contacts", contactList)
// Guardar teléfono y configuración por defecto
TinyDB1.StoreValue(join("contact_phone_", name), phone)
TinyDB1.StoreValue(join("contact_days_", name), 3)
TinyDB1.StoreValue(join("contact_last_", name), Clock1.FormatDate(Clock1.Now()))
// Actualizar la lista visible
call loadContactList
Procedimiento: loadContactList
procedimiento loadContactList:
set contactList = TinyDB1.GetValue("contacts", make a list())
set ListViewContacts.Elements = contactList
Llamar y Gestionar
Al tocar un contacto, aparece un popup centrado con dos opciones claras
Mostrar popup al tocar un contacto:
cuando ListViewContacts.AfterPicking:
set global selectedContact = ListViewContacts.Selection
Notifier1.ShowChooseDialog(
message: selectedContact
title: "¿Qué quieres hacer?"
button1Text: "📞 Call"
button2Text: "⚙️ Manage"
cancelable: true
)
Ejecutar la acción elegida:
cuando Notifier1.AfterChoosing(choice):
si choice = "📞 Call":
set phone = TinyDB1.GetValue(join("contact_phone_", global selectedContact))
set PhoneCall1.PhoneNumber = phone
PhoneCall1.MakePhoneCall
si choice = "⚙️ Manage":
open another screen with start value
screenName: "EditContactScreen"
startValue: global selectedContact
Pantalla Editar Contacto
Pantalla separada (EditContactScreen) para modificar nombre, teléfono y frecuencia
Componentes en Designer
Variables globales (en el cajón Variables):
initialize global contactName = ""
initialize global currentDays = 3
initialize global newName = ""
Cargar datos al abrir EditContactScreen:
cuando EditContactScreen.Initialize:
set global contactName = get start value
set TextBoxName.Text = contactName
set TextBoxPhone.Text = TinyDB1.GetValue(join("contact_phone_", contactName))
set global currentDays = TinyDB1.GetValue(join("contact_days_", contactName), 3)
set ListPickerDays.Elements = make a list("1", "3", "7")
set ListPickerDays.Text = join("⏰ Cada ", currentDays, " días")
⏰ Elegir frecuencia:
cuando ListPickerDays.AfterPicking:
set global currentDays = number(ListPickerDays.Selection)
set ListPickerDays.Text = join("⏰ Cada ", currentDays, " días")
💾 Guardar cambios:
cuando ButtonSave.Click:
set newName = TextBoxName.Text
// Si cambió el nombre, actualizar la lista y mover datos
si newName ≠ contactName:
set contactList = TinyDB1.GetValue("contacts", make a list())
set idx = index in list(contactName, contactList)
replace list item(contactList, idx, newName)
TinyDB1.StoreValue("contacts", contactList)
// Mover contact_last_ al nuevo nombre
TinyDB1.StoreValue(join("contact_last_", newName), TinyDB1.GetValue(join("contact_last_", contactName), ""))
// Borrar claves antiguas
TinyDB1.StoreValue(join("contact_phone_", contactName), "")
TinyDB1.StoreValue(join("contact_days_", contactName), "")
TinyDB1.StoreValue(join("contact_last_", contactName), "")
// Guardar phone y days (con el nombre final, nuevo o igual)
TinyDB1.StoreValue(join("contact_phone_", newName), TextBoxPhone.Text)
TinyDB1.StoreValue(join("contact_days_", newName), currentDays)
close screen
🗑️ Eliminar contacto:
cuando ButtonDelete.Click:
set contactList = TinyDB1.GetValue("contacts", make a list())
set index = index in list(contactName, contactList)
remove list item(contactList, index)
TinyDB1.StoreValue("contacts", contactList)
// Borrar datos del contacto
TinyDB1.StoreValue(join("contact_phone_", contactName), "")
TinyDB1.StoreValue(join("contact_days_", contactName), "")
TinyDB1.StoreValue(join("contact_last_", contactName), "")
close screen
Sistema de Recordatorios
Cada vez que se abre la app, comprobamos si hay que recordar contactar con alguien
1. Comprobar fechas
¿Han pasado los días configurados?
2. Mostrar aviso
Popup con botones Call/SMS
3. Resetear contador
Guardar fecha actual como "último aviso"
Procedimiento: checkReminders
procedimiento checkReminders:
set contactList = TinyDB1.GetValue("contacts", make a list())
set reminders = make a list()
set global reminderFirstContact = ""
set today = Clock1.FormatDate(Clock1.Now())
para cada name en contactList:
set days = TinyDB1.GetValue(join("contact_days_", name), 3)
set lastDate = TinyDB1.GetValue(join("contact_last_", name), "")
// Calcular días transcurridos
set elapsed = Clock1.Duration(
Clock1.MakeInstantFromMillis(Clock1.GetMillis(Clock1.MakeInstant(lastDate)))
Clock1.Now()
) / 86400000 // milisegundos en un día
si elapsed ≥ days:
add items to list(reminders, name)
// Resetear: guardar fecha de hoy
TinyDB1.StoreValue(join("contact_last_", name), today)
// Mostrar recordatorios pendientes
si length of list(reminders) > 0:
set global reminderFirstContact = select list item(reminders, 1)
set message = "Time to reach out to: "
para cada name en reminders:
message = join(message, "\n• ", name)
Notifier1.ShowChooseDialog(
message: message
title: "🔔 Reminder"
button1Text: "📞 Call first contact"
button2Text: "OK, thanks!"
)
"Time to reach out to:"
• Grandma Ana
• Uncle Pedro
Al cerrar el aviso...
La fecha se actualiza a hoy.
Si se reabre la app, no se repite el recordatorio.
Variable global necesaria (en el cajón Variables):
initialize global reminderFirstContact = ""
Responder al botón del diálogo:
cuando Notifier1.AfterChoosing(choice):
si choice = "📞 Call first contact":
set phone = TinyDB1.GetValue(join("contact_phone_", reminderFirstContact), "")
si phone ≠ "":
call PhoneCall1.MakePhoneCall
phoneNumber: phone
💬 Frases Motivadoras
Vamos a aplicar todo esto a nuestra app
Componentes en Designer
Cargar frases al inicio
cuando Screen1.Initialize:
si length of list(TinyDB1.GetValue("quotes", make a list())) = 0:
TinyDB1.StoreValue("quotes", make a list(
"It's never too late to make a new friend"
"A phone call can brighten someone's whole day"
"Sharing a walk is sharing happiness"
"Small gestures make big differences"
"Today is a good day to say I love you"
))
Botón Aleatorio
Cuando pulsen el botón, mostramos una frase al azar
cuando ButtonNewQuote.Click:
set variable list = TinyDB1.GetValue("quotes", "")
si length of list(list) > 0:
entonces
set LabelQuote.Text =
select list item(
list: list
index: random integer(1, length of list(list))
)
si no:
set LabelQuote.Text = "No quotes saved"
🤝 Actividades Compartidas
Actividades por Categorías
Queremos que el usuario elija un tipo de actividad y le sugiera una al azar
Cooking
Games
Outdoors
Creativity
¿Cómo organizar esto en TinyDB?
Necesitamos guardar una lista diferente para cada categoría.
La solución: usar prefijos en las etiquetas (tags).
Es como organizar carpetas: en vez de meter todo junto, separamos por tipo.
Prefijo = una palabra al principio del tag que indica la categoría
"activities_cooking", "activities_games", "activities_outdoors"...
Prefijos en TinyDB
Cada categoría tiene su propia etiqueta con un prefijo común
| "activities_cooking" | 🍳 Bake cookies together, Make an omelette, Prepare a smoothie... |
| "activities_games" | 🎲 Play cards, Do a puzzle, Play dominoes... |
| "activities_outdoors" | 🌳 Walk in the park, Go to a market, Visit a museum... |
| "activities_creativity" | 🎨 Do crafts together, Look at old photos, Draw together... |
Cargar Actividades por Categoría
En Screen1.Initialize, cargamos cada categoría por separado
🍳 Cooking
si length of list(TinyDB1.GetValue("activities_cooking", make a list())) = 0:
TinyDB1.StoreValue("activities_cooking", make a list(
"Bake cookies together", "Make an omelette",
"Prepare a smoothie", "Make popcorn"
))
🌳 Outdoors
si length of list(TinyDB1.GetValue("activities_outdoors", make a list())) = 0:
TinyDB1.StoreValue("activities_outdoors", make a list(
"Walk in the park", "Go to a market",
"Visit a museum", "Sit at a terrace"
))
Lo mismo para cada categoría: games, creativity, etc.
Cada una con su tag: "activities_games", "activities_creativity"
Un Botón por Categoría
Cada botón busca en su categoría y muestra una actividad al azar
🍳 cuando ButtonCooking.Click:
set list = TinyDB1.GetValue("activities_cooking", "")
set LabelActivity.Text =
select list item(list, random integer(1, length of list(list)))
🎲 cuando ButtonGames.Click:
set list = TinyDB1.GetValue("activities_games", "")
set LabelActivity.Text =
select list item(list, random integer(1, length of list(list)))
🌳 cuando ButtonOutdoors.Click:
set list = TinyDB1.GetValue("activities_outdoors", "")
set LabelActivity.Text =
select list item(list, random integer(1, length of list(list)))
🎨 cuando ButtonCreativity.Click:
set list = TinyDB1.GetValue("activities_creativity", "")
set LabelActivity.Text =
select list item(list, random integer(1, length of list(list)))
😊 Registro Emocional (Mood Tracker)
Concepto
SonRISAS incluye un diario de emociones para saber cómo se siente el usuario
Happy
Okay
Sad
¿Cómo funciona?
Componentes en Designer
Componentes principales
Botones de navegación (ocultos por defecto)
Guardar y Mostrar Historial
Al pulsar un botón de mood: comprobar si ya se registró hoy, y si no, añadir a la lista, limitar a 7 y mostrar
Procedimiento: saveMood (valor: texto)
Un solo procedimiento para los tres botones. Recibe "happy", "okay" o "sad".
procedimiento saveMood(valor):
set today = Clock1.FormatDate(Clock1.Now())
set lastMoodDate = TinyDB1.GetValue("mood_last_date", "")
si today = lastMoodDate:
Notifier1.ShowAlert("You already logged your mood today!")
si no:
set global moodList = TinyDB1.GetValue("moods", make a list())
add items to list(moodList, valor)
si length of list(moodList) > 7:
remove list item(moodList, index: 1)
TinyDB1.StoreValue("moods", moodList)
TinyDB1.StoreValue("mood_last_date", today)
call showMoodHistory
call checkSadness
Los tres botones llaman al mismo procedimiento:
cuando ButtonHappy.Click: call saveMood("happy")
cuando ButtonOkay.Click: call saveMood("okay")
cuando ButtonSad.Click: call saveMood("sad")
Procedimiento: showMoodHistory
procedimiento showMoodHistory:
set text = ""
para cada item en moodList:
si item = "happy" → text = text + "😊"
si item = "okay" → text = text + "😐"
si item = "sad" → text = text + "😢"
set LabelMoodHistory.Text = text
Detectar Tristeza
Si los últimos 3 registros son "no felices" (okay o sad), sugerimos ayuda
Procedimiento: checkSadness
Necesita 4 variables globales: moodList, n, e1, e2, e3 (inicializadas a "" o 0)
procedimiento checkSadness:
set global moodList = TinyDB1.GetValue("moods", make a list())
si length of list(moodList) ≥ 3:
set global n = length of list(moodList)
set global e1 = select list item(moodList, n - 2)
set global e2 = select list item(moodList, n - 1)
set global e3 = select list item(moodList, n)
si e1 ≠ "happy" and e2 ≠ "happy" and e3 ≠ "happy":
set LabelSuggestion.Text = "It looks like you've been feeling down. How about..."
set ButtonGoContacts.Visible = true
set ButtonGoActivities.Visible = true
set ButtonGoQuotes.Visible = true
si no:
set LabelSuggestion.Text = "You're doing great! Keep smiling 😊"
set ButtonGoContacts.Visible = false
set ButtonGoActivities.Visible = false
set ButtonGoQuotes.Visible = false
😐😢😐
"It looks like you've been feeling down..."
😊😐😊
"You're doing great! Keep smiling 😊"
(botones de navegación ocultos)
Vocabulario Clave 📖
Lista = colección ordenada
TinyDB = memoria permanente
Tag = etiqueta para buscar datos
Prefijo = nombre al inicio del tag
Random = aleatorio, al azar
Index = posición en la lista
ListPicker = lista desplegable para elegir un elemento
SelectionIndex = posición del elemento elegido en el ListPicker
Clock = reloj para fechas
Mood Tracker = registro emocional