Broadcast Hub
BroadcastHub
App Inventor Classic • App Inventor Classic • FOR APP INVENTOR 2 CLICK HERE• App Inventor Classic • App Inventor Classic
This information pertains to App Inventor 1 (Classic). For tutorials about App Inventor 2, go to the App Inventor 2 Tutorials.
Download Refined Version (Book Chapter PDF)
In this tutorial, you'll write an app that automatically responds to texts messages and broadcasts texts messages it receives to a list of phone numbers.The app is inspired by FrontLineSMS, a tool that has been used in developing countries to monitor elections, broadcast weather changes, and in general connect people that don't have access to the web but do have phones and mobile connectivity.
FrontLineSMS is software that runs on a computer with a phone plugged into it. The computer and plugged-in phone serve as a hub for SMS text communication amongst a group. You'll write a version of the software that runs on an Android phone, thus allowing the hub to be as mobile as the phones it connects.
This tutorial assumes you are familiar with the basics of App Inventor-- using the Component Designer to build a user interface, and using the Blocks Editor to specify event-handlers. If you are not familiar with the basics, try stepping through some of the basic tutorials before continuing.
The tutorial also assumes you have completed the TextGroup tutorial and thus are familiar with the basics of the Texting component.
Getting Started
Connect to the App Inventor web site and start a new project. Name it BroadcastHub, and also set the screen's Title to "BroadcastHub". Open the Blocks Editor and connect to the phone.
Introduction
BroadcastHub processes text messages that are received in the following manner:
- If the text message is from an unknown source, the app responds with a text that invites that source to join the broadcast list.
- If the text message “joinabc” is received, the app adds the sender to the broadcast list.
- If the text message is from a number already in the broadcast list, the message is broadcast to all numbers in the list.
The tutorial covers the following App Inventor concepts:
- The Texting component for sending texts and processing received texts.
- List variables to keep track of the numbers to text.
- The Clock.Timer block that is used to cause an app to repeat operations on a list of data (the list of phone numbers) each time the Timer goes off
Setting up the Components
BroadcasterHub facilitates communication between mobile phones. Those phones need not have the BroadcasterHub app installed, or even be smart phones. The app is the hub, and the app's user interface is only for the administrator of the communications network.
For this sample, that user interface is simple: it will display the current broadcast list -- the list of phone numbers which have registered for the service-- and it will report all texts it receives and broadcasts.
To build the interface, add the following components:
Component Type | Palette Group | What you'll name it | Purpose of Component |
Label | Basic | BroadcastListLabel | This will display the phone numbers that are registered. |
Label | Basic | LogLabel | This will display a log of the texts received and broadcast. |
Texting | Social | Texting1 | The component that processes the texts |
TinyDB | Basic | TinyDB1 | To store the list of registered phone numbers |
- Set the Width of each of the labels to "Fill Parent" so that they span the phone horizontally. Set the Height for the labels to 200 pixels.
- Set the Text property of BroadcastListLabel to "Broadcast List...".
- Set the Text property of LogLabel to blank.
BroadcastHub should look like this after its front-end is designed:
Add behaviors to the components
The activity for Broadcast Hub is not triggered by the user entering information or clicking a button, but by texts being received from outside sources. You'll need the following behaviors:
- When the text message is from an unknown source, the app responds with a text that invites that source to join the broadcast list.
- When the text message "joinabc" is received, register the sender as part of the broadcast list.
- When the text message is from a number already in the broadcast list, the message is broadcast to all numbers in the list.
Start by creating the first behavior-- when you receive a text, send a message back to the sender inviting them to text a code, "joinabc", in order to register for the group. You'll need the following blocks:
Block type | Drawer | Purpose |
Texting1.MessageReceived | Texting1 | Event-handler triggered when the phone receives a text |
set Texting1.PhoneNumber to | Texting1 | To set the number for the return text |
value number | My Definitions | Argument of MessageReceived, it is the phone number of sender |
set Texting1.Message | Texting1 | To set the invite message to send |
text | Text | The invite message |
Texting1.SendMessage | Texting1 | Send it! |
The blocks should look like this:
How the Blocks Work
Texting1.MessageReceived is triggered when any text message is received on the phone. The blocks within the event-handler set the PhoneNumber and Message of the Texting1 component, then send the message.
Test the behavior. You'll need another phone besides the one on which the app resides. From the second phone, send a text "hello" to the phone running the app. The second phone should receive a text that invites them to the group.
Handling the "joinabc" text message
Create the blocks for the second behavior: "when the text message "joinabc" is received, add the sender to the broadcast list." First, you'll need to define a list variable, BroadcastList, to store the phone numbers that register. From Definitions, drag out a def var block and name it "BroadcastList". Initialize it with a make a list block from Lists:
Next, modify the Texting1.MessageReceived event-handler so that it adds the sender's phone number to the BroadcastList if the message received is "joinabc". After you add the number to the list, display the new list in the BroadcastListLabel. You'll need the following blocks:
Block type | Drawer | Purpose |
ifelse | Control | Depending on message received, you'll do different things |
= block | Math | The test will be: is messageText equal to "joinabc" |
value messageText | My Definitions | Plug into = block |
text ("joinabc") | Text | Plug into = block |
add items to list | Lists | To add the sender's number to BroadcastList |
BroadcastList | My Definitions | The list |
value number | My Definitions | Plug in as item of add items to list |
set BroadcastListLabel.Text to | BroadcastListLabel | Display the new list |
BroadcastList | My Definitions | Plug in to set BroadcastListLabel.Text to block |
set Texting1.Message to | Texting1 | Prepare Texting1 to send message back to sender |
text ("congrats, you ...") | Text | Congratulate sender for joining group |
The blocks should look like this:
How the Blocks Work
The first row of blocks sets Texting1.PhoneNumber to the phone number of the message that was just received. The app then asks if the messageText was of the special code "joinabc". If so, the sender's phone number is added to the BroadcastList, and a congratulations message is sent. If the messageText is something other than "joinabc", the reply message is just the invitation message from before. After the ifelse block, the reply message is sent (bottom row).
Test this behavior. From a second phone, send the text message "joinabc" to the phone on which the app is running. Do you recieve the correct reply? Try sending a different text message as well to check if the ifelse is working correctly.
Broadcasting Messages
Next, you'll add the behavior so that the app broadcasts messages to the numbers in BroadcastList, if the message arrives from a number in BroadcastList. You'll need an additional ifelse block to check if the number is in the list, and a Clock.Timer block to broadcast the message to each item in the list. Here are all the blocks you'll need:
Block type | Drawer | Purpose |
ifelse | Control | Depending on whether sender is already in list, you'll do different things |
is in list? | Lists | Checks to see if something is in a list |
global BroadcastList | My Definitions | Plug into list slot of is in list? |
value number | My Definitions | Plug into list slot of is in list? |
Clock.Timer | My Blocks | To repeatedly send out message to all in list |
global BroadcastList x2 | My Definitions | Plug into list slot of select list item |
set Texting1.Message to | Texting1 | To set the message |
value messageText | My Definitions | The message that was received and to be broadcast |
set Texting1.Message to | Texting1 | To set the phone number |
set Clock.TimerEnabled x2 | Clock1 | To turn the timer on/off |
set nTicks x2 | My Definitions | Sets the value of nTicks |
select list item | Control | To grab an item from the list |
The blocks should look like this:
How the Blocks Work
We've just set up a timer to send a text message to each number in the list, BroadcastList.
A timer performs some action each time the clock ticks. To control the timer, define a global variable named nTicks. We will set this variable initially to the number of phone numbers in the list. And then we will subtract 1 from the variable each time we send a message. A timer is done in two parts. In TextingMessage1.Received we will no longer send the message. Instead, we will initialize the global nTicks variable to the length of the BroadcastList list. And we will enable the Clock1. The second part is done using a Clock1.Timer block. This block will contain the Texting1.SendMessage command. Each time the clock ticks the following steps will be performed:
- Select a number from the phoneNumbers list using the nTicks as the index.
- Set the Texting1.PhoneNumber to the selected number.
- Send the text message using Texting1.SendMessage.
- Subtract 1 from the nTicks variable.
- If the nTicks is less than or equal to 0, disable the clock . This will stop the timer.
The behavior is complex enough that it requires a "nested" ifelse block. If the phone number of the received message is already in the list, the foreach block is executed and the message relayed to all in the list. If the number is not in the list, the outer else-do clause is executed, and it asks another question, which is the test of the nested ifelse block: is the messageText equal to "joinabc". The app then branches one of two ways based on the answer.
In general, if and ifelse blocks can be nested to arbitrary levels, giving you the power to program arbitrarily complex behaviors.
Test this behavior. First, have two different phones register with the app by texting "joinabc" to the phone which is running the app.Then text another message from one of the phones. Both phones should receive the text.
Logging the broadcasted texts
When a text is received and broadcast out to the other phones, the app should log that occurrence to the app so the administrator can monitor the activity. Earlier, you added the label LogLabel to the user interface for this purpose. Now, you'll code some blocks that change LogLabel each time a new text arrives.
You need to build a text that says something like "message from 111-2222 broadcast". The 111-2222 is not fixed data, but is the value of the variable number that comes with the MessageReceived event. So to build the text, you'll concatenate the first part "message from" with the variable number and finally with the last part of the message, the text "broadcast".
There are two functions in the Text drawer to build text. One is the join function, which joins two pieces of text. Another function, make text, is also available and more convenient to use than join when you have more than two parts. You'll use it for this behavior. Here are all the blocks you'll need:
Block type | Drawer | Purpose |
set LogLabel.Text to | LogLabel | Display the log here |
make text | Text | Build a text object out of multiple parts |
text ("message from") | Text | Report message |
value number | My Definitions | The number of the sender |
text ("broadcast\n") | Text | Text is "message from 111-2222 broadcast" with newline |
LogLabel.Text | LogLabel | Add new log to the previous ones |
The blocks should look like this:
How the Blocks Work
After broadcasting the message to all the numbers in BroadcastList , the app now modifies the LogLabel to include a report of the just broadcasted text. If the sender's number (value number) is "1112222", then the make text will build the text object:
message from: 1112222 broadcast
along with the log reports that were already in LogLabel.Text from previously received texts. The "\n" is the newline character that causes the old reports to be displayed on the next line, e.g.,
message from: 1112222 broadcast
message from: 555-6666 broadcast
Storing the BroadcastList in a Database
As is, the app works, but every time you close the app the BroadcastList will be lost and everyone would have to re-register. To fix this, you'll use the TinyDB component to store and retrieve the BroadcastList to and from a database. TinyDB allows you to store data persistently-- even if the app is closed, the next time you open the app the data will be there.
The general scheme for storing data persistently is the following: When the app begins, load the list from the database into a variable (e.g., BroadcastList ). Then, each time a new item is added to the list, update the database version of the list.
Start by coding the blocks to store the list in the database. With the TinyDB component, a tag is used to identify the data and distinguish it from other data stored in the database. In this case, you can tag the data as "broadcastList". Do the following:
- Drag out a TinyDB1.StoreValue block from the TinyDB1 drawer.
- Create a text block and give it the text "broadcastList". Place this text block as the tag of StoreValue.
- Drag out a reference to BroadcastList from My Definitions . Place it as the value argument of StoreValue.
- Place the StoreValue block right below the blocks that add an item to the BroadcastList.
The bottom part of the modified Texting1.MessageReceived event-handler should look like the following:
How the Blocks Work
TinyDB1.StoreValue stores the data in BroadcastList to a database with a tag that you can later use to retrieve the data into your app. In this case, the tag is the text, "broadcastList", and he value is the actual list BroadcastList.
Loading the BroadcastList from a Database
Now add the blocks for loading the list back in each time the app begins. When the app begins, the Screen1.Initialize event is triggered, so your blocks will go in that event-handler. You’ll call TinyDB.GetValue, using the same tag you used to store the list (“broadcastList”). Check if the returned value is a list, because it won’t be if there isn’t any data in the list yet.
The blocks should look like:
How the Blocks Work
The blocks first define a variable, valueFromDB, which is used to temporarily store the data returned from the database. When the app begins, the Screen1.Initialize event is triggered. TinyDB.GetValue is called, and the data from the database is placed in valueFromDB.
The variable is then checked to see if it is a list. If it is not, that means there was no data for the tag “broadcastList” in the database, and an empty text has been returned. This will happen the first time a user opens the app.
If valueFromDB is a list, the list is placed into the variable BroadcastList and the list is displayed.
Test this behavior. Choose Restart App which will trigger the Screen1.Initialize. Do the numbers registered earlier appear?
Display BroadcastList with each number on separate lines
Create a procedure displayBroadcastList, which displays the list with each phone number on a separate line. Be sure and call the procedure from below the add items to list block so that the updated list is displayed.
For help displaying the list on separate lines, see Displaying a List.
You'll need the following blocks:
Block type | Drawer | Purpose |
procedure ("displayBroadcastList") | Definitions | Create the procedure |
set BroadcastListLabel.Text to | BroadcastListLabel | Display the list here |
text ("Phone Numbers...") | Text | The header for the list |
foreach | Control | Iterate through the numbers |
name pnumber | in the foreach | Name the foreach variable "pnumber". This is the current item of the foreach |
BroadcastList | My Definitions | Plug into in list slot of foreach |
set BroadcastListLabel.Text to | BroadcastListLabel | Modify with each of the numbers |
make text | Text | Build a text object from multiple parts |
BroadcastListLabel.Text | BroadcastListLabel | Add to the label on each iteration of foreach |
text ("\n") | Text | Newline character so that next number is on next line |
value pnumber | My Definitions | The current number from the list |
The displayBroadcastList procedure should look like this:
How the Blocks Work
The foreach in displayBroadcastList successively adds a phone number to the end of the label, placing a newline character in between each item.
Of course this procedure will not do anything unless you call it: you should add two calls to the procedure: one within the Texting1.MessageReceived event-handler, and one within the Screen1.Initialize event-handler. The call displayBroadcastList block can be found in My Definitions, and it should replace the blocks that directly set the BroadcastListLabel.Text to the list. You can see how these calls to displayBroadcastList should look in the snapshot of the final version below.
Test this behavior. Try adding some more phone numbers to the list. Do the registered phone numbers now appear on separate lines?
Broadcast Hub, Final Version
Variations
Once you get the Broadcast Hub app working, you might want to explore some variations. For example,
- Allow client phones to remove themselves from the list by texting "quitabc" to the app.
- Let the hub administrator (the user of your app) add and remove numbers from the broadcast list.
- Let the hub administrator specify numbers that should not be allowed into the list.
Review
Here are some of the ideas covered in this tutorial:
- Apps can react to events, like a text being received, that are not initiated by the user of the app.
- Nested ifelse and foreach blocks can be used to code complex behaviors.
- The make text block can be used to build a text object out of multiple parts.
- TinyDB can be used to store and retrieve data from a database. A general scheme is to call StoreValue to update the database whenever the data changes, and call GetValue to retrieve the database data when the app begins.
Scan the Sample App to your Phone
Scan the following barcode onto your phone to install and run the sample app.
Download Source Code
If you'd like to work with this sample in App Inventor, download the source code to your computer, then open App Inventor, go to the My Projects page, and choose More Actions | Upload Source.
MIT and Google are grateful to Professor David Wolber, CS Professor at The University of San Francisco, for developing this tutorial. Done with BroadcastHub? Return to the other tutorials here.
Tutorial Version:
Tutorial Difficulty:
- Advanced
Tutorial Type:
- SMS Texting
- Data Storage