Explore MIT App Inventor - SMS Texting http://dev-explore.appinventor.mit.edu/tutorial-type/sms-texting en Google Voice for Texting over Wifi http://dev-explore.appinventor.mit.edu/content/google-voice <div class="field field-name-body field-type-text-with-summary field-label-hidden view-mode-rss"><div class="field-items"><div class="field-item even" property="content:encoded"><style> <!--/*--><![CDATA[/* ><!--*/ li { padding-bottom: 7px; } .basicblock { border: 1px dashed #7AA81C; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E7F2CB; font-size: 9pt; text-wrap: suppress; } .callblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E0D1FF; font-size: 9pt; text-wrap: suppress; } .argblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E5E5FF; font-size: 9pt; text-wrap: suppress; } .textblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #FADAA0; font-size: 9pt; text-wrap: suppress; } .setblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #C1D5F8; font-size: 9pt; text-wrap: suppress; } } /*--><!]]>*/ </style><h2 class="ai-header"><span style="color:green;">Texting Over Wifi: </span>Using Google Voice</h2> <h4 class="ai-header">Introduction</h4> <p>This tutorial shows how to use the Texting component to send and receive text messages (SMS) over Wifi. This lets<br /> instructors use App Inventor texting examples and exercises with phones that do not have mobile or data plans.</p> <p>In order to use the Texting component over Wifi, the app user will need a <a href="https://www.google.com/voice">Google Voice account</a> and will also need to download and install the <a href="https://play.google.com/store/apps/details?id=com.google.android.apps.googlevoice">Google Voice app</a> from the Play Store.</p> <p>Once you get Google Voice set up on your phone, try the <a href="http://explore.appinventor.mit.edu/content/alertme">AlertMe</a> tutorial.</p> <h4 class="ai-header">Under the Hood: How Things Work</h4> <p>Texting over Wifi uses Google Voice to send and receive messages. The mobile Google Voice app is used for receiving messages and the user's Google Voice account is used for sending messages.</p> <p><b>Receiving a Message:</b></p> <p>Here are some of the internal details for how things work when a message is received.</p> <ol><li>The phone receives an incoming text message, say, "Hi", from some phone, say, "555-111-2222".</li> <li>The Android system forwards the message to the Google Voice mobile app, which has been set up to receive messages.</li> <li>The Google Voice app forwards the message to the Texting component's Broadcast Receiver.</li> <li>If the Texting component is <b>enabled to receive messages</b>, the Broadcast Receiver forwards the message to the Texting component.</li> <li>If the Texting component is running (meaning it is visible in the foreground), its <span class="basicblock">MessageReceived</span> (with the arguments <span class="argblock">555-111-2222</span> and <span class="argblock">Hi</span> for this example) event handler will be invoked.</li> <p><img src="/sites/all/files/tutorials/googleVoiceWifiText/MessageReceivedBlock.png" /></p> <li>The <span class="basicblock">MessageReceived</span> method will process the message according to the algorithm specified in its do slot.</li> </ol><p>Of course most of these details are not visible to the App Inventor programmer, who just needs to worry about the procedure in the Do slot.</p> <p><b>Sending a Message:</b></p> <p>Here are some of the details for how things work when a message is sent.</p> <ol><li>Sending a message requires that the Texting component's <span class="setblock">Message</span> and <span class="setblock">PhoneNumber</span> properties have been set and that its <span class="callblock">SendMessage</span> method has been called.</li> <li>If this is the first message being sent by the app, the user will be prompted to authenticate with their online Google Voice account.</li> <p><img src="/sites/all/files/tutorials/googleVoiceWifiText/sendingmsg.png" /></p> <li>Once authenticated, the <span class="callblock">SendMessage</span> method will forward the message and phone number to Google Voice.</li> <li>Google Voice will forward the message to the destination phone number.</li> </ol><p>Note: if the user sends a message to her own Google Voice number, it will be forwarded back to the phone., which<br /> could be very confusing. This may have implications for how you design your app.</p> <h4 class="ai-header">Preliminaries: Logging onto Wifi</h4> <p>A reliable Wifi hotspot is a prerequisite to using the Texting component without a SIM card or mobile account. Use the phone's <em>Settings &gt; Wireless &amp; networks &gt; Wi-Fi</em> to connect to a Wifi hotspot. This may require you to login to the Wifi with a password and may also require you to login to the school's network with a username and password.</p> <h4 class="ai-header">Setting Up Your Google Voice Account</h4> <p>The revised Texting component uses Google Voice to send outgoing messages over Wifi. In order to build and test an app that sends text messages over Wifi, the app developer will have to have a <a href="https://www.google.com/voice">Google Voice account</a>. Once the app is developed, users of the app will also have to have Google Voice accounts.</p> <p>To create a Google Voice account,</p> <ol><li>Login to your Gmail account.</li> <li>Click on the <em>More</em> tab on the Google menu bar.</li> <li>Select the <em>Even More</em> option.</li> <li>Scroll down to the <em>Google Voice</em> link under <em>Home and Office/<em>.</em></em></li> <li>Follow the instructions there to create a Google Voice number and activate your account.</li> </ol><h4 class="ai-header">Installing the Google Voice App</h4> <p>The revised Texting component receives incoming messages over Wifi through the <a href="http://www.google.com/mobile/voice">Google Voice mobile app</a>. To download and install Google voice on your phone:</p> <ol><li>Start the phone's <em>Market</em> or <em>Play Store</em> app.</li> <li>Search for "Google Voice".</li> <li>Download and install the app.</li> <li>Read and approve the app's permission requests.</li> <li>Start the app and follow the setup instructions.</li> <li>Set the following Google Voice settings under <em>Voice &gt; Sync</em> and notifications:</li> <li>Receive text messages &gt; Via the Google Voice app</li> <li>Text notifications &gt; Off (unless you want your app and Google Voice to notify you)</li> </ol><h4 class="ai-header">Authenticating Outgoing Messages</h4> <p>Because outgoing messages are sent through the user's (online) Google Voice account, users of Texting-over-wifi apps will be asked to give the app permission to access the user's account <b>now and in the future</b>. If there are more than one user account on the phone, the user will first be prompted to choose an account. </p> <table><tr><td><img src="/sites/all/files/tutorials/googleVoiceWifiText/permissionscreen.png" width="200" /></td> <td><img src="/sites/all/files/tutorials/googleVoiceWifiText/authscreen.png" width="200" /></td> </tr></table><p>This description assumes that the user already has an active Google account on the phone. If not, the user will be asked to setup an account or login to an existing Google account.</p> <p>This authentication process will happen the first time the app sends an outgoing message after it is installed. Depending on how the app and phone are used, the user may never again be asked for permission </p> <h4 class="ai-header">In Summary</h4> <p>Congratulations! You have successfully installed Google Voice and are ready to text over Wifi! This will be useful for a variety of Apps that use texting. Now try to build <a href="http://explore.appinventor.mit.edu/content/alertme">AlertMe</a>, an app that allows you to try this new capability.</p> <pre class="ai-box">Done with <span style="color:black;">Google Voice</span>? Return to the other tutorials <a href="/tutorials">here</a>.</pre></div></div></div><section class="field field-name-field-version field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Version:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-version/app-inventor-1" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">App Inventor 1</a> </li> </ul> </section> <section class="field field-name-field-tutorial-difficulty field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Difficulty:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-difficulty/advanced" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Advanced</a> </li> </ul> </section> <section class="field field-name-field-tutorial-type field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Type:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-type/sms-texting" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">SMS Texting</a> </li> </ul> </section> Fri, 14 Jun 2013 16:20:49 +0000 aaron 321 at http://dev-explore.appinventor.mit.edu http://dev-explore.appinventor.mit.edu/content/google-voice#comments No Text While Driving (Part 2) http://dev-explore.appinventor.mit.edu/content/no-text-while-driving-part-2 <div class="field field-name-body field-type-text-with-summary field-label-hidden view-mode-rss"><div class="field-items"><div class="field-item even" property="content:encoded"><style> <!--/*--><![CDATA[/* ><!--*/ li { padding-bottom: 7px; } .basicblock { border: 1px dashed #7AA81C; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E7F2CB; font-size: 9pt; text-wrap: suppress; } .callblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E0D1FF; font-size: 9pt; text-wrap: suppress; } .argblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E5E5FF; font-size: 9pt; text-wrap: suppress; } .textblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #FADAA0; font-size: 9pt; text-wrap: suppress; } .setblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #C1D5F8; font-size: 9pt; text-wrap: suppress; } .controlblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #FAEDBB; font-size: 9pt; text-wrap: suppress; } /*--><!]]>*/ </style><h2 class="ai-header"><span style="color:green;">No Text While Driving</span>, Part 2</h2> <p><img src="/sites/all/files/tutorials/NTWD2/emulator.png" style="float:right; width:350px;" /></p> <p><a href="http://cs.usfca.edu/~wolber/appinventor/bookSplits/ch4NoTexting.pdf">Download Refined Version (Book Chapter PDF)</a></p> <p>You know that texting while driving is dangerous, so you've created and installed the <a href="http://explore.appinventor.mit.edu/content/no-text-while-driving">No Text While Driving</a> app on your phone. Now, when you drive you open that app and let it auto-respond to incoming texts.</p> <p>But the jingle of the texts coming in is killing you with curiosity-- wouldn't it be great if you could hear the texts spoken aloud? With Part II of the tutorial, you'll extend the app so that it speaks out both the message and who sent it. And since you're making some changes anyway, you'll modify the auto-response so it reports your whereabouts in the reply: "Sorry, I'm driving and I'm at 1600 Pennsylvania Avenue". </p> <p>Before completing this tutorial you should complete <a href="http://explore.appinventor.mit.edu/content/no-text-while-driving">part I</a>.</p> <h4 class="ai-header">Getting Started</h4> <p>For this app to work, you'll need to load a text-to-speech module, <em>Text-To-Speech Extended</em>, on your phone. App Inventor and other apps rely on this module for the underlying voice synthesis. If you don't already have it, you can download onto your phone using the Android Market:</p> <ul><li>On your phone, open the Market app</li> <li>Search for TTS</li> <li>Select the app <em>Text-To-Speech Extended</em> to install</li> </ul><p>Once the <em>Text-To-Speech</em> module is installed, open it to test its features. When it opens, set the default language as desired. Then select "Listen to Preview". If you don't hear anything, make sure the volume on your phone is turned on. You can also change the default Engine which can enhance the quality of the spoken words.</p> <p>Once the Text-To-Speech module is installed and configured as desired, you can use the <b>TextToSpeech</b> component within App Inventor. Begin by opening App Inventor and your <span style="color:green;">NoTextWhileDriving</span> project from part I. SaveAs the project and name the new copy "NoTextWhileDriving2".</p> <h4 class="ai-header">What you'll Learn</h4> <p>The app demonstrates two powerful components:</p> <ul><li>The <b>LocationSensor</b> component to determine the phone's location, and</li> <li>The <b>TextToSpeech</b> component to speak the incoming texts aloud.</li> </ul><p>These components encapsulate some advanced functionality, but using them in building an app is relatively straight-forward.</p> <h4 class="ai-header">Set up the Components</h4> <p>Leave the user interface for <span style="color:green;">NoTextWhileDriving, Part II</span> the same as in Part I. But in the Component Designer, add two non-visual components: a <b>LocationSensor</b> and a <b>TextToSpeech</b> component. You do not need to set any properties of the components.</p> <h4 class="ai-header">Add behaviors to the components</h4> <p>The behavior for part I of No Text While Driving was: "when a text is received, the message the user has put in MessageTextbox is sent as a text message response to the sender."</p> <p>For part II, the behavior will be changed to: "when a text is received, it is spoken aloud and a message, including the text in MessageTextbox and the phone's current location, is sent in response to the sender."</p> <h4 class="ai-header">Speaking the text aloud</h4> <p>When a text message is received by the phone, the <span class="basicblock">Texting.MessageReceived</span> event is triggered. The blocks from Part I handle this event by setting the PhoneNumber and Message properties of the Texting component appropriately and then sending the response text.</p> <p>Extend the blocks from Part I by using the TextToSpeech component to speak both the sender's phone number and the message. You'll add the following blocks to the existing <span class="basicblock">Texting.MessageReceived</span> blocks:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Block type </td> <td> Drawer </td> <td> Purpose </td> </tr><tr><td><span class="callblock">TextToSpeech1.Speak</span></td> <td>TextToSpeech1</td> <td>Speak out the message received</td> </tr><tr><td><span class="textblock">make text</span></td> <td>Text</td> <td>Build the words that are to be spoken</td> </tr><tr><td><span class="textblock">text</span> ("message from")</td> <td>Text</td> <td>First words spoken, put a space at end</td> </tr><tr><td><span class="argblock">number</span></td> <td>My Definitions</td> <td>This is the number the original text was received from</td> </tr><tr><td><span class="textblock">text</span> ("")</td> <td>Text</td> <td>Put a space in between number and message</td> </tr><tr><td><span class="argblock">messageText</span></td> <td>My Definitions</td> <td>This is the original message received</td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/NTWD2/MessageReceivedSpoken.png" /></p> <h4 class="ai-header">How the blocks work</h4> <p>After the response is sent, the <span class="callblock">TextToSpeech1.Speak</span> function is called. You can plug any text object into the message slot of the Speak function. In this case, <span class="textblock">make text</span> is used to build the words to be spoken, including the text "message from", the phone number from which the message was received (number), a blank text, and the message received (messageText). If the text "hello" was received from the number "111-2222", the words "message from 111-2222. hello." would be spoken.</p> <pre class="ai-testing"><b>Test the behavior.</b> You'll need a second phone to test the behaviors in this app. From the second phone, send a text to the phone that is running the app. Does the second phone speak the text aloud? Does it still send an automated response?</pre><h4 class="ai-header">Adding location information to the auto-response</h4> <p>Next, add a <span class="basicblock">LocationSensor.LocationChanged</span> event handler which places the current address into a variable, lastKnownLocation. When a text comes in, the value of that variable can be added to the response message. Here are the blocks you'll need:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Block type </td> <td> Drawer </td> <td> Purpose </td> </tr><tr><td><span class="setblock">def variable</span> (lastKnownLocation)</td> <td>Definitions</td> <td>Create a variable to hold the last read address</td> </tr><tr><td><span class="textblock">text</span> ("unknown")</td> <td>Text</td> <td>Set the default value in case the phone's sensor is not working</td> </tr><tr><td><span class="basicblock">LocationSensor1.LocationChanged</span></td> <td>LocationSensor1</td> <td>This is triggered on the first location reading and every change</td> </tr><tr><td><span class="setblock">set lastKnownLocation to</span></td> <td>My Definitions</td> <td>Set this variable to be used later</td> </tr><tr><td><span class="argblock">LocationSensor1.CurrentAddress</span></td> <td>LocationSensor1</td> <td>This is a street address such as "2222 Willard Street, Atlanta, Georgia"</td> </tr></table><p>Here are the blocks:</p> <p><img src="/sites/all/files/tutorials/NTWD2/lastLocation.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p><span class="basicblock">LocationSensor1.LocationChanged</span> is called the first time the sensor gets a reading and each time the phone is moved enough to trigger a new reading. These blocks just place the current (street) address into the variable lastKnownLocation, which you'll use later when you change the auto-response that is sent out.</p> <h4 class="ai-header">Modify the message with location information</h4> <p>Next, modify the <span class="basicblock">Texting1.MessageReceived</span> event-handler so that the current address is added to the response. You'll need the following blocks:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Block type </td> <td> Drawer </td> <td> Purpose </td> </tr><tr><td><span class="textblock">make text</span></td> <td>Text</td> <td>If there is a location reading, build a compound text object</td> </tr><tr><td><span class="argblock">MessageTextBox.Text </span></td> <td>MessageTextBox</td> <td>This is the (custom) message in the text box</td> </tr><tr><td><span class="textblock">text</span> ( "My location is: ")</td> <td>Text</td> <td>This will be spoken after the custom message (note the leading space)</td> </tr><tr><td><span class="argblock">global lastKnownLocation</span></td> <td>LocatonSensor</td> <td>This is an address such as "2222 Willard Street, Atlanta, Georgia"</td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/NTWD2/ReceivedText.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>Instead of directly sending a message of the text in MessageTextbox.Text, the app first builds a message using <span class="textblock">make text</span>. It combines the response text in MessageTextBox.Text with the text " My location is " and then the variable lastKnownLocation.</p> <pre class="ai-testing"><b>Test the behavior.</b> From the second phone, send a text to the phone that is running the app. Does the second phone receive the response text? Does it include location information? </pre><h4 class="ai-header">Final Program</h4> <p><img src="/sites/all/files/tutorials/NTWD2/finalProg.png" /></p> <h4 class="ai-header">Variations</h4> <p>Once you get the app working, you might want to explore some variations. For example,</p> <ul><li>Write a version that lets the user define custom responses for particular phone numbers.</li> <li>Write a version that sounds an alarm when a text is received from a number in a "notify" list.</li> </ul><h4 class="ai-header">Review</h4> <p>Here are some of the ideas covered in this tutorial: </p> <ul><li>The <b>TextToSpeech</b> component takes any text object and speaks it aloud. It relies on an Android module, <em>Text-To-Speech Extended</em>, which must be downloaded to the phone for the app to work.</li> <li><span class="textblock">make text</span> is used to piece together (concatenate) separate text items into a single text object.</li> <li>The <b>LocationSensor</b> component can report the phone's latitude, longitude, and current street address. To ensure that it has a reading, you should access its data in its <span class="basicblock">LocationChanged</span> event-handler, which is triggered the first time a reading is made and on every change thereafter. </li> </ul><h4 class="ai-header">Scan the Sample App to your Phone</h4> <p>Scan the following barcode onto your phone to install and run the sample app. </p><p><img src="/sites/all/files/tutorials/NTWD2/NTWD2Barcode.png" /></p> <p>Or <a href="http://explore.appinventor.mit.edu/sites/all/files/tutorials/NTWD2/notextwhiledriving2.apk">download the apk</a></p> <h4 class="ai-header">Download Source Code</h4> <p>If you'd like to customize this sample in App Inventor, download the <a href="/sites/all/files/tutorials/NTWD2/notextwhiledriving2.zip">source code</a> to your computer, then open App Inventor, go to the My Projects page, and choose <b>More Actions | Upload Source</b>.</p> <pre class="ai-box">MIT and Google are grateful to <a href="http://www.appinventor.org/">Professor David Wolber</a>, CS Professor at The University of San Francisco, for developing this tutorial. Done with <span style="color:black;">No Text While Driving, Part 2</span>? Return to the other tutorials <a href="/tutorials">here</a>.</pre></div></div></div><section class="field field-name-field-version field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Version:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-version/app-inventor-1" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">App Inventor 1</a> </li> </ul> </section> <section class="field field-name-field-tutorial-difficulty field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Difficulty:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-difficulty/advanced" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Advanced</a> </li> </ul> </section> <section class="field field-name-field-tutorial-type field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Type:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-type/sms-texting" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">SMS Texting</a> </li> </ul> </section> Fri, 14 Jun 2013 16:20:14 +0000 aaron 320 at http://dev-explore.appinventor.mit.edu http://dev-explore.appinventor.mit.edu/content/no-text-while-driving-part-2#comments No Text While Driving http://dev-explore.appinventor.mit.edu/content/no-text-while-driving <div class="field field-name-body field-type-text-with-summary field-label-hidden view-mode-rss"><div class="field-items"><div class="field-item even" property="content:encoded"><style> <!--/*--><![CDATA[/* ><!--*/ li { padding-bottom: 7px; } .basicblock { border: 1px dashed #7AA81C; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E7F2CB; font-size: 9pt; text-wrap: suppress; } .callblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E0D1FF; font-size: 9pt; text-wrap: suppress; } .argblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E5E5FF; font-size: 9pt; text-wrap: suppress; } .textblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #FADAA0; font-size: 9pt; text-wrap: suppress; } .setblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #C1D5F8; font-size: 9pt; text-wrap: suppress; } .controlblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #FAEDBB; font-size: 9pt; text-wrap: suppress; } /*--><!]]>*/ </style><h2 class="ai-header"><span style="color:green;">No Text While Driving</span></h2> <p><img src="/sites/all/files/tutorials/NTWD/screenshot.PNG" style="float:right;" /></p> <p><a href="http://cs.usfca.edu/~wolber/appinventor/bookSplits/ch4NoTexting.pdf">Download Refined Version (Book Chapter PDF)</a></p> <p>This tutorial demonstrates how an app can respond to text messages automatically. You'll build an app that sends back a response when a text message is received. The idea for the app came from University of San Franciso student Daniel Finnegan.</p> <p>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 <a href="/tutorials">basic tutorials</a> before continuing.</p> <h4 class="ai-header">Getting Started</h4> <p>Connect to the App Inventor web site and start a new project. Name it <span style="color:green;">NoTextWhileDriving</span>, and also set the screen's <b style="color:green;">Title</b> to "NoTextWhileDriving". Open the Blocks Editor and connect to the phone.</p> <h4 class="ai-header">Introduction</h4> <p>You'll design the app so that it sends a response to any text message received. You'll also allow the user to customize the response sent.</p> <p>The tutorial introduces the following App Inventor concepts: </p> <ul><li>The <b>Texting</b> component for sending texts and processing received texts.</li> <li>The <b>TinyDB</b> database component for saving the customized message even after the app is closed.</li> </ul><h4 class="ai-header">Set up the Components</h4> <p>The user interface for NoTextWhileDriving is simple: it has a text box for the response message and a button for submitting a change to this message. It also has two checkboxes: one to decide whether or not you want to be receiving messages and one to choose to enable Google Voice. This is useful for texting if your phone doesn't have a SIM card. Visit the <a href="http://appinventor.mit.edu/explore/content/google-voice-texting-over-wifi.html">Texting over Google Voice</a> tutorial to learn more about this.</p> <p>You'll also need a Texting component and a TinyDB component, both of which will appear in the "non-visual" component area. A table of detailed instructions for designing the components is below, but you can also build it from the following picture of what it should look like:</p> <p><img src="/sites/all/files/tutorials/NTWD/fullscreen.png" /></p> <p>The components listed below were used to create the designer window shown above. Drag each component from the Palette into the Viewer and name it as specified below:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Component Type </td> <td> Palette Group </td> <td> What you'll name it </td> <td> Purpose of Component </td> <td> Component Settings </td> </tr><tr><td> <b>Label</b> </td> <td>Basic</td> <td style="color:green;">PromptLabel</td> <td>Let the user know how the app works</td> <td>Set the <b style="color:green;">Text</b> to "The text below will be sent in response to all texts while this app is running."</td> </tr><tr><td> <b>Label</b> </td> <td>Basic</td> <td style="color:green;">ResponseLabel</td> <td>Displays what text will be sent to incoming messages.</td> <td></td> </tr><tr><td> <b>TextBox</b> </td> <td>Basic</td> <td style="color:green;">NewResponseTextbox</td> <td>User will enter custom response here</td> <td>Set the <b style="color:green;">Hint </b><p> to "Enter custom response."</p></td> </tr><tr><td> <b>Button</b> </td> <td>Basic</td> <td style="color:green;">SubmitResponseButton</td> <td>User clicks this to submit new response</td> <td>Set the <b style="color:green;">text </b><p> to "Submit Response."</p></td> </tr><tr><td> <b>CheckBox</b> </td> <td>Basic</td> <td style="color:green;">CheckBoxReceivingEnabled</td> <td>Let the user choose if they want to receive messages.</td> <td></td> </tr><tr><td> <b>CheckBox</b> </td> <td>Basic</td> <td style="color:green;">CheckBoxGvEnabled</td> <td>Let the user choose if they want to respond using Google Voice.</td> <td></td> </tr><tr><td> <b>Texting</b> </td> <td>Social</td> <td style="color:green;">Texting1</td> <td>The component that processes the texts</td> <td></td> </tr><tr><td> <b>TinyDB</b> </td> <td>Basic</td> <td style="color:green;">TinyDB1</td> <td>The component that will store the response in the database</td> <td></td> </tr></table><h4 class="ai-header">Add behaviors to the components</h4> <p><span style="color:green;">NoTextWhileDriving</span> has the following behaviors:</p> <ol><li>When a text is received, the message displayed in ResponseLabel is sent as a text message response to the sender.</li> <li>When the user modifies the custom message in NewResponseTextbox and clicks the SubmitResponseButton , the new message is saved persisently in the phone's database.</li> <li>When the app begins, the custom message is loaded from the database into ResponseLabel.</li> </ol><h4 class="ai-header">1. Responding to a text</h4> <p>When a text message is received by the phone, the <span class="basicblock">Texting.MessageReceived</span> event is triggered. Your app should handle this event by setting the PhoneNumber and Message properties of the Texting component appropriately and sending the response text.</p> <p>You'll need the following blocks: </p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Block type </td> <td> Drawer </td> <td> Purpose </td> </tr><tr><td><span class="basicblock">Texting1.MessageReceived</span></td> <td>Texting1</td> <td>Event-handler triggered when the phone receives a text</td> </tr><tr><td><span class="setblock">set Texting1.PhoneNumber to</span></td> <td>Texting1</td> <td>Set the PhoneNumber property before sending </td> </tr><tr><td><span class="argblock">value number</span></td> <td>My Definitions</td> <td>This is the phone number of the person who sent the text</td> </tr><tr><td><span class="setblock">set Texting1.Message to</span></td> <td>Texting1</td> <td>Set the Message property before sending</td> </tr><tr><td><span class="argblock">ResponseLabel.Text</span></td> <td>ResponseLabel</td> <td>This is the message the user has entered</td> </tr><tr><td><span class="callblock">Texting1.SendMessage </span></td> <td>Texting1</td> <td>Send the message</td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/NTWD/messagereceivedblocks.PNG" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>When the phone receives any text message, the <span class="basicblock">Texting1.MessageReceived</span> event is triggered. The phone number of the sender is in the argument number, and the sender's message is in the argument <span class-="">messageText</span>.</p> <p>In response, the app sends a text message to the sender. To send a text, the app sets the two key properties of the Texting component: PhoneNumber and Message. <span class="setblock">Texting.PhoneNumber</span> is set to the number of the sender, and <span class="setblock">Texting.Message</span> is set to the text in <span class="argblock">ResponseLabel</span> -- this might be the default, "I'm driving right now, I'll contact you shortly.", or the user may have modified it. Once these properties are set, <span class="callblock">Texting1.SendMessage</span> is called to actually send the response text message.</p> <p>The <span class="controlblock">if</span> block prevents the app from sending the automatic reply if it receives its own message. This would happen, for example, if the user is texting their own number to test the app.</p> <pre class="ai-testing"><b>Test the behavior.</b> You'll need a second phone to test this behavior. From the second phone, send a text to the phone that is running the app. You can <a href="http://explore.appinventor.mit.edu/content/google-voice">set up one phone with Google Voice</a> or start two emulators to communicate with one another to test this behavior. Does the second phone receive the response text? If that works, try modifying the response message and sending another message from the second phone. Is the new response sent?</pre><h4 class="ai-header">2. Storing the custom response</h4> <p>The app so far works, but if you close the app and reopen it, the custom message will be lost. To make things more convenient for the user, store the custom response message they enter into a database using the TinyDB component.</p> <p>TinyDB provides two blocks: StoreValue and GetValue . The former allows you to store a tagged piece of information, while the latter let's you retrieve one.</p> <p>You'll need the following blocks to store the custom message: </p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Block type </td> <td> Drawer </td> <td> Purpose </td> </tr><tr><td><span class="basicblock">SubmitResponseButton.Click</span></td> <td>SubmitResponseButton</td> <td>User clicks this button to submit new response message</td> </tr><tr><td><span class="setblock">ResponseLabel.Text</span></td> <td>ResponseLabel</td> <td>Sets the response to be what the user inputs.</td> </tr><tr><td><span class="argblock">NewResponseTextbox.Text</span></td> <td>NewResponseTextbox</td> <td>The response message entered by the user is here</td> </tr><tr><td><span class="callblock">TinyDB1.StoreValue</span></td> <td>TinyDB1</td> <td>Store the custom message in the phone's database</td> </tr><tr><td><span class="textblock">text</span> ("responseMessage")</td> <td>Text</td> <td>Use this as the tag for the data</td> </tr><tr><td><span class="argblock">ResponseLabel.Text</span></td> <td>ResponseLabel</td> <td>The response message entered by the user is displayed here after the user submits their new response.</td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/NTWD/submitresponse.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>When the user clicks the SubmitResponseButton, the app will store the response message entered by the user as the shown text that will be sent. The text in this label (<span class="argblock">ResponseLabel.Text</span>) will be stored in TinyDB. The text <span class="textblock">responseMessage</span> is used as a tag to uniquely identify the information-- later, you'll use that same tag to retrieve the message from the database.</p> <h4 class="ai-header">3. Retrieving the saved response message</h4> <p>Program the <span class="basicblock">Screen1.Initialize</span> event-handler so that the saved custom response message is retrieved from the database and placed in ResponseLabel. Check the retrieved data to make sure there's something there-- after all, the first time the app is used, the database will not have any message saved. If there is a stored message, place it in the ResponseLabel so that the user can see it and so that it will be used to respond to incoming texts.</p> <p>You'll need the following blocks:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Block type </td> <td> Drawer </td> <td> Purpose </td> </tr><tr><td><span class="setblock">def variable</span> ("response")</td> <td>Definitions</td> <td>A temporary variable to hold the retrieved data </td> </tr><tr><td><span class="textblock">text</span> (blank)</td> <td>Text</td> <td>Initial value for the variable can be anything</td> </tr><tr><td><span class="basicblock">Screen1.Initialize</span></td> <td>Screen1</td> <td>This is triggered when app begins</td> </tr><tr><td><span class="setblock">Texting1.ReceivingEnabled</span></td> <td>Texting1</td> <td>Sets the app so that it receives SMS text messages</td> </tr><tr><td><span class="basicblock">number</span> (3)</td> <td>Number</td> <td>The app is initialized to receive text messages while the app is running or when it's not</td> </tr><tr><td><span class="setblock">CheckBoxGvEnabled.Checked</span></td> <td>CheckBoxGvEnabled</td> <td>Sets status of the Google Voice Enabled checkbox</td> </tr><tr><td><span class="argblock">Texting1.GoogleVoiceEnabled</span></td> <td>Texting1</td> <td>If Google Voice is enabled, SMS messages will be sent over Google Voice</td> </tr><tr><td><span class="controlblock">if else test</span></td> <td>Control</td> <td>To ask if texting should be enabled.</td> </tr><tr><td><span class="basicblock">not= block</span></td> <td>Math</td> <td>Check if the retrieved value is not equal to (=) 1</td> </tr><tr><td><span class="argblock">Texting1.ReceivingEnabled</span></td> <td>Texting1</td> <td>Lets the user decide whether or not the app will received SMS messages</td> </tr><tr><td><span class="setblock">CheckBoxReceivingEnabled.Checked</span>(x2)</td> <td>CheckBoxReceivingEnabled</td> <td>Sets status of the Receiving Enabled checkbox.</td> </tr><tr><td><span class="controlblock">true</span></td> <td>Logic</td> <td>Sets the app to receive text messages</td> </tr><tr><td><span class="controlblock">false</span></td> <td>Logic</td> <td>Sets the app to not receive text messages</td> </tr><tr></tr><tr><td><span class="setblock">set Response</span></td> <td> Definitions </td> <td>Sets the variable to the value retrieved from db.</td> </tr><td><span class="callblock">TinyDB1.GetValue</span></td> <td>TinyDB1</td> <td>Get the stored response text from the database</td> <tr><td><span class="textblock">text</span> ("responseMessage")</td> <td>Text</td> <td>Plug into tag slot of GetValue, make sure text is same as was used in StoreValue above</td> </tr><tr><td><span class="controlblock">if test</span></td> <td>Control</td> <td>To ask if the retrieved value has some text</td> </tr><tr><td><span class="basicblock">&gt; block</span></td> <td>Math</td> <td>Check if length of retrieved value is greater than (&gt;) 0</td> </tr><tr><td><span class="textblock">length test</span></td> <td>Text</td> <td>Check if length of retrieved value is greater than 0</td> </tr><tr><td><span class="argblock">global response</span> (x2)</td> <td>My Definitions</td> <td>This variable holds the value retrieved from GetValue</td> </tr><tr><td><span class="basicblock">number</span> (0)</td> <td>Math</td> <td>To compare with length of response</td> </tr><tr><td><span class="setblock">set ResponseLabel.Text to</span></td> <td>Message</td> <td>If we retrieved something, place it in ResponseLabel</td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/NTWD/initializeblock_update.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>When the app begins, the <span class="basicblock">Screen1.Initialize</span> event is triggered. The app calls the <span class="callblock">TinyDB1.GetValue</span> with a tag of <span class="textblock">responseMessage</span>-- the same tag used when you stored the user's entry earlier. The resulting value is placed in the variable <span class="setblock">response</span>. When the app starts, we set the Texting component to have receiving enabled, but not Google Voice, which is displayed on the user interface. <span class="argblock">ReceivingEnabled</span> takes a number value 1 (receiving disabled), 2 (receiving enabled while app is running), or 3 (receiving enabled when app is running and is not running) </p> <p>The variable response is used so that the value returned from the database can be checked. If it has a length of 0, then there was no database entry with a tag of <span class="textblock">responseMessage</span>-- something that will occur the first time a user runs this app. But if the length is greater than 0, the app knows that a custom response has been stored previously, and the retrieved value can be placed in the ResponseLabel which the user will see and which is used as the message for any response texts sent. </p> <pre class="ai-testing"><b>Test the app.</b> Enter a new response message in the NewResponseTextbox and click the SubmitResponseButton. Then restart the app by clicking the Restart App button in the Blocks Editor. This will cause the app to re-initialize just like it will when a user closes the app and reopens it later. Does the custom message you entered appear?</pre><h4 class="ai-header">Selecting Response Settings</h4> <p>The user interface has two checkboxes that the user can toggle: whether Receiving is enabled and whether Google Voice is enabled. The first allows the user to decide whether or not they want to be receiving messages. The second lets the user decide if they want to send messages from their own phone number or from a Google Voice account. When the user toggles these switches, the changes are communicated to the Texting component which enables or disables these properties as marked.</p> <p>You'll need the following blocks.</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Block type </td> <td> Drawer </td> <td> Purpose </td> </tr><tr><td><span class="basicblock">CheckBoxGvEnabled.Changed</span></td> <td>CheckBoxGvEnabled</td> <td>Detects if the user toggles this control.</td> </tr><tr><td><span class="setblock">Texting1.GoogleVoiceEnabled</span></td> <td>Texting1</td> <td>Sets whether or not Google Voice is enabled</td> </tr><tr></tr><tr><td><span class="argblock">CheckBoxGvEnabled.Checked</span></td> <td>CheckBoxGvEnabled</td> <td>Returns true or false value.</td> </tr><td><span class="basicblock">CheckBoxReceivingEnabled.Changed</span></td> <td>CheckBoxReceivingEnabled</td> <td>Detects if the user toggles this control.</td> <tr><td><span class="controlblock">if else block</span></td> <td>Control</td> <td>Checks whether or not receiving is enabled.</td> </tr><tr><td><span class="basicblock">= block</span></td> <td>Math</td> <td>Checks whether or not receiving is enabled.</td> </tr><tr><td><span class="basicblock">true</span></td> <td>Logic</td> <td>Checks whether or not receiving is enabled.</td> </tr><tr><td><span class="argblock">CheckBoxReceivingEnabled.Checked</span></td> <td>CheckBoxReceivingEnabled</td> <td>Returns true or false value.</td> </tr><tr><td><span class="setblock">Texting1.ReceivingEnabled</span>(x2)</td> <td>Texting1</td> <td>Sets whether or not receiving is enabled.</td> </tr><tr><td><span class="basicblock">number</span>(1)</td> <td>Math</td> <td>Sets ReceivingEnabled as false.</td> </tr><tr><td><span class="basicblock">number</span>(3)</td> <td>Math</td> <td>Sets ReceivingEnabled as true.</td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/NTWD/checkboxblocks_update.png" /></p> <pre class="ai-testing"><b>Test the app.</b> Try texting your Android device when the "Receiving Enabled" is checked and then when it is not checked. Do you receive texts when it is unchecked? Try texting over Google Voice. If any of these features aren't working like you expect them to, go back and look at the blocks again!</pre><h4 class="ai-header">No Text While Driving, Final Program</h4> <p><img src="/sites/all/files/tutorials/NTWD/allblocks.png" /></p> <h4 class="ai-header">Variations</h4> <p>Once you get the No Text While Driving app working, you might want to explore some variations. For example,</p> <ul><li>Write a version that speaks the received texts aloud. You'll need to use the <b>TextToSpeech</b> component.</li> <li>Write a version that lets the user define custom responses for particular phone numbers.</li> <li>Write a version that sends custom responses based on the user's location (e.g., I'm in church...)</li> </ul><h4 class="ai-header">Review</h4> <p>Here are some of the ideas covered in this tutorial: </p> <ul><li>The <b>Texting</b> component can be used both to send text messages and process the ones that are received.</li> <li>The <b>TinyDB</b> component is used to store information persistently, in the phone's database, so that it there each time the app is opened.</li> </ul><h4 class="ai-header">Scan the Sample App to your Phone</h4> <p>Scan the following barcode onto your phone to install and run the sample app. </p><p><img src="/sites/all/files/tutorials/NTWD/NTWDBarcode.png" /></p> <p>Or <a href="http://explore.appinventor.mit.edu/sites/all/files/tutorials/NTWD/NoTextWhileDriving.apk">download the apk</a></p> <h4 class="ai-header">Download Source Code</h4> <p>If you'd like to work with this sample in App Inventor, download the <a href="/sites/all/files/tutorials/NTWD/NoTextWhileDriving.zip">source code</a> to your computer, then open App Inventor, go to the My Projects page, and choose <b>More Actions | Upload Source</b>.</p> <pre class="ai-box">MIT and Google are grateful to <a href="http://www.appinventor.org/">Professor David Wolber</a>, CS Professor at The University of San Francisco, for developing this tutorial. Done with <span style="color:black;">No Text While Driving</span>? Return to the other tutorials <a href="/tutorials">here</a>.</pre></div></div></div><section class="field field-name-field-version field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Version:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-version/app-inventor-1" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">App Inventor 1</a> </li> </ul> </section> <section class="field field-name-field-tutorial-difficulty field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Difficulty:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-difficulty/advanced" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Advanced</a> </li> </ul> </section> <section class="field field-name-field-tutorial-type field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Type:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-type/data-storage" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Data Storage</a> </li> <li class="field-item odd"> <a href="/tutorial-type/sms-texting" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">SMS Texting</a> </li> </ul> </section> Fri, 14 Jun 2013 16:19:45 +0000 aaron 319 at http://dev-explore.appinventor.mit.edu http://dev-explore.appinventor.mit.edu/content/no-text-while-driving#comments AlertMe http://dev-explore.appinventor.mit.edu/content/alertme <div class="field field-name-body field-type-text-with-summary field-label-hidden view-mode-rss"><div class="field-items"><div class="field-item even" property="content:encoded"><style> <!--/*--><![CDATA[/* ><!--*/ li { padding-bottom: 7px; } .basicblock { border: 1px dashed #7AA81C; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E7F2CB; font-size: 9pt; text-wrap: suppress; } .callblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E0D1FF; font-size: 9pt; text-wrap: suppress; } .argblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E5E5FF; font-size: 9pt; text-wrap: suppress; } .textblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #FADAA0; font-size: 9pt; text-wrap: suppress; } .setblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #C1D5F8; font-size: 9pt; text-wrap: suppress; } .controlblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #FAEDBB; font-size: 9pt; text-wrap: suppress; } /*--><!]]>*/ </style><h2 class="ai-header"><span style="color:green;">AlertMe: </span>Texting in the background, when app is not running</h2> <p>The <span style="color:green;">AlertMe</span> app receives alert messages, and notifies the user even when the app is not currently running. If the app is running (visible on the screen), when it receives an SMS, the message will be displayed on the screen. If it's not running, the user will receive a <em>Notification</em> in the status bar, which can be viewed by pulling down the status bar. Tapping on the notification will start the app and display the message. The user can then click the <em>Acknowledge</em> button to send an acknowledgment to the sender that the alert has been received.</p> <table><tr><td><img src="/sites/all/files/tutorials/alertMe/notification.png" width="200" /></td> <td><img src="/sites/all/files/tutorials/alertMe/notification%20bar.png" width="200" /></td> <td><img src="/sites/all/files/tutorials/alertMe/alert%20me%20running.png" width="200" /></td> </tr><tr><td colspan="3" align="center"><a href="/sites/explore.appinventor.mit.edu/files/AlertMe.zip"><img src="/sites/explore.appinventor.mit.edu/files/downloadSourcebutton.png" style="float: center;border:none" width="250" /></a></td> </tr></table><pre class="ai-box"><strong>If you are using a device that does not have regular SMS enabled through a cell phone carrier and want to do texting over the internet (IP/wifi)</strong>, make sure you have a Google Voice account and you have the Google Voice app set up on your phone or tablet. For more information on how to do this, see the <a href="http://explore.appinventor.mit.edu/content/google-voice">Google Voice for Wifi Texting</a> tutorial. When you set up the texting component in the designer, be sure you have checked the <em>GoogleVoiceEnabled</em> property.</pre><hr /><h4 class="ai-header">The Designer</h4> <p>The interface of the app is pretty simple. It displays the time, phone number, and message of each of the alerts, as well as an acknowledge button. What really drives this app is the Texting component that you will add. The components are: </p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td>Component Type</td> <td>Palette Group</td> <td>What you'll name it</td> <td>Purpose of Component</td> </tr><tr><td style="color:green;">Label</td> <td>Basic</td> <td>LabelHeader</td> <td>Displays the "Alerts" header.</td> </tr><tr><td style="color:green;">Label</td> <td>Basic</td> <td>LabelAlerts</td> <td> <p>Displays the incoming messages in a log</p> <p>Set width and height to <b>Fill Parent</b></p> </td> </tr><tr><td style="color:green;">Button</td> <td>Basic</td> <td>ButtonAck</td> <td>Sends an achknowledgment to the alert sender.</td> </tr><tr><td style="color:green;">Clock</td> <td>Basic</td> <td>Clock1</td> <td>Used to generate a time stamp in the message log.</td> </tr><tr><td style="color:green;">Texting</td> <td>Social</td> <td>Texting1</td> <td> <p>Receives and sends messages over Wifi.</p> <p><em>Only if you are using texting over wifi through Google Voice:</em> <b>Check the <em>GoogleVoiceEnabled</em> property.</b></p> </td> </tr></table><p>When you're done, the designer should look like this.</p> <p><img src="/sites/all/files/tutorials/alertMe/designer_0.png" /></p> <h4 class="ai-header">The Blocks Editor</h4> <p>The blocks have to handle two events: receiving a message and sending an acknowledgment. In each of these cases, they have to know who sent the message and what it said. We'll approach each of these actions in turn.</p> <p><b>Variables</b></p> <p>Since both procedures require that we know the alert message and who sent it, we will define two globalvariables: <span class="setblock">alertPhoneNumber</span> and <span class="setblock">alertMessage</span>.</p> <p><img src="/sites/all/files/tutorials/alertMe/variables.png" /></p> <p><b>The Message Received Block</b></p> <p>Incoming messages are handled by the <span class="basicblock">MessageReceived</span> block, which takes two arguments, the <span class="argblock">number</span> and the <span class="argblock">messageText</span>. For this app, we store the values of these arguments in our global variables, <span class="setblock">alertPhoneNumber</span> and <span class="setblock">alertMessage</span> respectively, and then construct a log entry and display it on the label using <span class="setblock">LabelAlerts.Text</span>.</p> <p><img src="/sites/all/files/tutorials/alertMe/AlertMeBlock.png" /></p> <p><b>Sending an Acknowledgment</b></p> <p>The user can acknowledge an alert by sending a message back to the sender. This is done using <span class="basicblock">ButtonAck.Click</span>, which constructs the message and sends it as shown here. </p> <p><img src="/sites/all/files/tutorials/alertMe/buttonclick.png" /></p> <h4 class="ai-header">Variations</h4> <p>Now that you have AlertMe working, here are some variations on the program you can make.</p> <ul><li>Modify the function of the <em>Acknowledge</em> button so you can choose to send different text, such as choosing between "What's up?", "On my way", and "Call you later."</li> <li>Change the blocks so the user gets an automatically generated response (without you clicking a button). This could be a setting you could enable if you are away from your phone or in a meeting.</li> </ul><h4 class="ai-header">Scan the Sample App to your Phone</h4> <p>Scan the following barcode onto your phone to install and run the sample app. </p><p><img src="/sites/all/files/tutorials/alertMe/AlertMeBarcode.png" /></p> <p>Or <a href="http://explore.appinventor.mit.edu/sites/all/files/tutorials/alertMe/AlertMe.apk">download the apk</a></p> <h4 class="ai-header">Download Source Code</h4> <p>If you'd like to work with this sample in App Inventor, download the <a href="/sites/all/files/tutorials/alertMe/AlertMe.zip">source code</a> to your computer, then open App Inventor, go to the My Projects page, and choose <b>More Actions | Upload Source</b>.</p> <pre class="ai-box">Done with <span style="color:black;">AlertMe</span>? Return to the other tutorials <a href="/tutorials">here</a>.</pre></div></div></div><section class="field field-name-field-version field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Version:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-version/app-inventor-1" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">App Inventor 1</a> </li> </ul> </section> <section class="field field-name-field-tutorial-difficulty field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Difficulty:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-difficulty/advanced" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Advanced</a> </li> </ul> </section> <section class="field field-name-field-tutorial-type field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Type:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-type/clock-timer" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Clock Timer</a> </li> <li class="field-item odd"> <a href="/tutorial-type/sms-texting" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">SMS Texting</a> </li> </ul> </section> Fri, 14 Jun 2013 16:13:33 +0000 aaron 318 at http://dev-explore.appinventor.mit.edu http://dev-explore.appinventor.mit.edu/content/alertme#comments Broadcast Hub http://dev-explore.appinventor.mit.edu/content/broadcasthub <div class="field field-name-body field-type-text-with-summary field-label-hidden view-mode-rss"><div class="field-items"><div class="field-item even" property="content:encoded"><style> <!--/*--><![CDATA[/* ><!--*/ li { padding-bottom: 7px; } .basicblock { border: 1px dashed #7AA81C; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E7F2CB; font-size: 9pt; text-wrap: suppress; } .callblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E0D1FF; font-size: 9pt; text-wrap: suppress; } .argblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E5E5FF; font-size: 9pt; text-wrap: suppress; } .textblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #FADAA0; font-size: 9pt; text-wrap: suppress; } .setblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #C1D5F8; font-size: 9pt; text-wrap: suppress; } .controlblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #FAEDBB; font-size: 9pt; text-wrap: suppress; } .listblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #FCF5D7; font-size: 9pt; text-wrap: suppress; } /*--><!]]>*/ </style><h2 class="ai-header"><span style="color:green;">BroadcastHub</span></h2> <p><a href="http://cs.usfca.edu/~wolber/appinventor/bookSplits/ch11BroadcastHub.pdf">Download Refined Version (Book Chapter PDF)</a></p> <p>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 <a href="http://www.frontlinesms.com/">FrontLineSMS</a>, a tool that has been used in developing countries to <a href="http://news.bbc.co.uk/2/hi/technology/8209172.stm">monitor elections</a>, broadcast weather changes, and in general connect people that don't have access to the web but do have phones and mobile connectivity.</p> <p>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.</p> <p>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 <a href="http://explore.appinventor.mit.edu/tutorials">basic tutorials</a> before continuing.</p> <p>The tutorial also assumes you have completed the <a href="http://explore.appinventor.mit.edu/content/textgroup">TextGroup</a> tutorial and thus are familiar with the basics of the <b>Texting</b> component.</p> <h4 class="ai-header">Getting Started</h4> <p>Connect to the App Inventor web site and start a new project. Name it <span style="color:green;">BroadcastHub</span>, and also set the screen's <b style="color:green;">Title</b> to "BroadcastHub". Open the Blocks Editor and connect to the phone.</p> <h4 class="ai-header">Introduction</h4> <p>BroadcastHub processes text messages that are received in the following manner: </p> <ul><li>If the text message is from an unknown source, the app responds with a text that invites that source to join the broadcast list.</li> <li>If the text message “joinabc” is received, the app adds the sender to the broadcast list.</li> <li>If the text message is from a number already in the broadcast list, the message is broadcast to all numbers in the list.</li> </ul><p>The tutorial covers the following App Inventor concepts:</p> <ul><li>The <b>Texting</b> component for sending texts and processing received texts.</li> <li>List variables to keep track of the numbers to text.</li> <li>The <span class="basicblock">Clock.Timer</span> 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</li> </ul><h4 class="ai-header">Setting up the Components</h4> <p>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.</p> <p>For this sample, that user interface is simple: it will display the current <em>broadcast list</em> -- the list of phone numbers which have registered for the service-- and it will report all texts it receives and broadcasts.</p> <p>To build the interface, add the following components:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Component Type </td> <td> Palette Group </td> <td> What you'll name it </td> <td> Purpose of Component </td> </tr><tr><td> <b>Label</b> </td> <td>Basic</td> <td style="color:green;">BroadcastListLabel</td> <td>This will display the phone numbers that are registered.</td> </tr><tr><td> <b>Label</b> </td> <td>Basic</td> <td style="color:green;">LogLabel</td> <td>This will display a log of the texts received and broadcast.</td> </tr><tr><td> <b>Texting</b> </td> <td>Social</td> <td style="color:green;">Texting1</td> <td>The component that processes the texts</td> </tr><tr><td> <b>TinyDB</b> </td> <td>Basic</td> <td style="color:green;">TinyDB1</td> <td>To store the list of registered phone numbers</td> </tr></table><ul><li>Set the <b style="color:green;">Width</b> of each of the labels to "Fill Parent" so that they span the phone horizontally. Set the <b style="color:green;">Height</b> for the labels to 200 pixels.</li> <li>Set the <b style="color:green;">Text</b> property of BroadcastListLabel to "Broadcast List...".</li> <li>Set the <b style="color:green;">Text</b> property of LogLabel to blank.</li> </ul><p>BroadcastHub should look like this after its front-end is designed:</p> <p><img src="/sites/all/files/tutorials/broadcastHub/broadcasterDesigner.png" /></p> <h4 class="ai-header">Add behaviors to the components</h4> <p>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:</p> <ul><li>When the text message is from an unknown source, the app responds with a text that invites that source to join the broadcast list.</li> <li>When the text message "joinabc" is received, register the sender as part of the broadcast list.</li> <li>When the text message is from a number already in the broadcast list, the message is broadcast to all numbers in the list.</li> </ul><p>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:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Block type </td> <td> Drawer </td> <td> Purpose </td> </tr><tr><td><span class="basicblock">Texting1.MessageReceived</span></td> <td>Texting1</td> <td>Event-handler triggered when the phone receives a text</td> </tr><tr><td><span class="setblock">set Texting1.PhoneNumber to</span></td> <td>Texting1</td> <td>To set the number for the return text</td> </tr><tr><td><span class="argblock">value number</span></td> <td>My Definitions</td> <td>Argument of <span class="basicblock">MessageReceived</span>, it is the phone number of sender </td> </tr><tr><td><span class="setblock">set Texting1.Message</span></td> <td>Texting1</td> <td>To set the invite message to send</td> </tr><tr><td><span class="textblock">text</span></td> <td>Text</td> <td>The invite message</td> </tr><tr><td><span class="callblock">Texting1.SendMessage</span></td> <td>Texting1</td> <td>Send it!</td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/broadcastHub/messageRcvd1.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p><span class="basicblock">Texting1.MessageReceived</span> 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.</p> <pre class="ai-testing"><b>Test the behavior.</b> 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.</pre><h4 class="ai-header">Handling the "joinabc" text message</h4> <p>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:</p> <p><img src="/sites/all/files/tutorials/broadcastHub/BroadcastList.png" /></p> <p>Next, modify the <span class="basicblock">Texting1.MessageReceived</span> 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:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Block type </td> <td> Drawer </td> <td> Purpose </td> </tr><tr><td><span class="controlblock">ifelse</span></td> <td>Control</td> <td>Depending on message received, you'll do different things</td> </tr><tr><td>= block</td> <td>Math</td> <td>The test will be: is messageText equal to "joinabc"</td> </tr><tr><td><span class="argnumber">value messageText</span></td> <td>My Definitions</td> <td>Plug into = block</td> </tr><tr><td><span class="textblock">text</span> ("joinabc")</td> <td>Text</td> <td>Plug into = block</td> </tr><tr><td><span class="listblock">add items to list</span></td> <td>Lists</td> <td>To add the sender's number to BroadcastList</td> </tr><tr><td><span class="argblock">BroadcastList</span></td> <td>My Definitions</td> <td>The list</td> </tr><tr><td><span class="argblock">value number</span></td> <td>My Definitions</td> <td>Plug in as item of <span class="listblock">add items to list</span></td> </tr><tr><td><span class="setblock">set BroadcastListLabel.Text to</span></td> <td>BroadcastListLabel</td> <td>Display the new list</td> </tr><tr><td><span class="argblock">BroadcastList</span></td> <td>My Definitions</td> <td>Plug in to <span class="setblock">set BroadcastListLabel.Text to</span> block</td> </tr><tr><td><span class="setblock">set Texting1.Message to</span></td> <td>Texting1</td> <td>Prepare Texting1 to send message back to sender</td> </tr><tr><td><span class="textblock">text</span> ("congrats, you ...")</td> <td>Text</td> <td>Congratulate sender for joining group</td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/broadcastHub/addtolist.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>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 <span class="controlblock">ifelse</span> block, the reply message is sent (bottom row).</p> <pre class="ai-testing"><b>Test this behavior.</b> 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.</pre><h4 class="ai-header">Broadcasting Messages</h4> <p>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 <span class="controlblock">ifelse</span> block to check if the number is in the list, and a <span class="basicblock">Clock.Timer</span> block to broadcast the message to each item in the list. Here are all the blocks you'll need:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Block type </td> <td> Drawer </td> <td> Purpose </td> </tr><tr><td><span class="controlblock">ifelse</span></td> <td>Control</td> <td>Depending on whether sender is already in list, you'll do different things</td> </tr><tr><td><span class="listblock">is in list?</span></td> <td>Lists</td> <td>Checks to see if something is in a list</td> </tr><tr><td><span class="argblock">global BroadcastList</span></td> <td>My Definitions</td> <td>Plug into list slot of is in list?</td> </tr><tr><td><span class="argblock">value number</span></td> <td>My Definitions</td> <td>Plug into list slot of is in list?</td> </tr><tr><td><span class="controlblock">Clock.Timer</span></td> <td>My Blocks</td> <td>To repeatedly send out message to all in list</td> </tr><tr><td><span class="argblock">global BroadcastList</span> x2</td> <td>My Definitions</td> <td>Plug into list slot of <span class="controlblock">select list item</span></td> </tr><tr><td><span class="setblock">set Texting1.Message to</span></td> <td>Texting1</td> <td>To set the message</td> </tr><tr><td><span class="argblock">value messageText</span></td> <td>My Definitions</td> <td>The message that was received and to be broadcast</td> </tr><tr><td><span class="setblock">set Texting1.Message to</span></td> <td>Texting1</td> <td>To set the phone number</td> </tr><tr><td><span class="basicblock">set Clock.TimerEnabled</span> x2</td> <td>Clock1</td> <td>To turn the timer on/off</td> </tr><tr><td><span class="basicblock">set nTicks</span> x2</td> <td>My Definitions</td> <td>Sets the value of nTicks</td> </tr><tr><td><span class="controlblock">select list item</span></td> <td>Control</td> <td>To grab an item from the list</td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/broadcastHub/textingreceived.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>We've just set up a <span class="controlblock">timer</span> to send a text message to each number in the list, BroadcastList. </p> <pre class="ai-box">A <span class="controlblock" style="color;black;">timer</span> performs some action each time the clock ticks. To control the timer, define a global variable named <b>nTicks</b>. 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 <span class="controlblock" style="color;black;">timer</span> is done in two parts. In <span class="basicblock">TextingMessage1.Received</span> we will no longer send the message. Instead, we will initialize the global <span class="controlblock">nTicks</span> variable to the length of the <span class="controlblock">BroadcastList</span> list. And we will enable the <span class="controlblock">Clock1</span>. The second part is done using a <span class="controlblock">Clock1.Timer</span> block. This block will contain the <span class="controlblock">Texting1.SendMessage</span> command. Each time the clock ticks the following steps will be performed: </pre><ul><li>Select a number from the <span class="controlblock">phoneNumbers</span> list using the <span class="controlblock">nTicks</span> as the index.</li> <li>Set the <span class="controlblock">Texting1.PhoneNumber</span> to the selected number.</li> <li>Send the text message using <span class="controlblock">Texting1.SendMessage</span>.</li> <li>Subtract 1 from the <span class="controlblock">nTicks</span> variable.</li> <li>If the <span class="controlblock">nTicks</span> is less than or equal to 0, disable the clock</li>. This will stop the timer. </ul><p>The behavior is complex enough that it requires a "nested" <span class="controlblock">ifelse</span> block. If the phone number of the received message is already in the list, the <span class="controlblock">foreach</span> 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 <span class="controlblock">ifelse</span> block: is the messageText equal to "joinabc". The app then branches one of two ways based on the answer.</p> <p>In general, <span class="controlblock">if</span> and <span class="controlblock">ifelse</span> blocks can be nested to arbitrary levels, giving you the power to program arbitrarily complex behaviors.</p> <pre class="ai-testing"><b>Test this behavior.</b> 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.</pre><h4 class="ai-header">Logging the broadcasted texts</h4> <p>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.</p> <p>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".</p> <p>There are two functions in the Text drawer to build text. One is the join function, which joins two pieces of text. Another function, <span class="textblock">make text</span>, 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:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Block type </td> <td> Drawer </td> <td> Purpose </td> </tr><tr><td><span class="setblock">set LogLabel.Text to</span></td> <td>LogLabel</td> <td>Display the log here</td> </tr><tr><td><span class="textblock">make text</span></td> <td>Text</td> <td>Build a text object out of multiple parts</td> </tr><tr><td><span class="textblock">text</span> ("message from")</td> <td>Text</td> <td>Report message</td> </tr><tr><td><span class="argblock">value number</span></td> <td>My Definitions</td> <td>The number of the sender</td> </tr><tr><td><span class="textblock">text</span> ("broadcast\n")</td> <td>Text</td> <td>Text is "message from 111-2222 broadcast" with newline</td> </tr><tr><td><span class="argblock">LogLabel.Text</span></td> <td>LogLabel</td> <td>Add new log to the previous ones</td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/broadcastHub/broadcastit.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>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 <span class="textblock">make text</span> will build the text object:</p> <p>message from: 1112222 broadcast</p> <p>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.,</p> <p>message from: 1112222 broadcast<br /> message from: 555-6666 broadcast</p> <h4 class="ai-header">Storing the BroadcastList in a Database</h4> <p>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 <b>TinyDB</b> 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. </p> <p>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. </p> <p>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: </p> <ol><li>Drag out a <span class="callblock">TinyDB1.StoreValue</span> block from the TinyDB1 drawer.</li> <li>Create a <span class="textblock">text</span> block and give it the text "broadcastList". Place this text block as the tag of StoreValue.</li> <li>Drag out a reference to BroadcastList from My Definitions . Place it as the value argument of StoreValue.</li> <li>Place the StoreValue block right below the blocks that add an item to the BroadcastList.</li> </ol><p>The bottom part of the modified <span class="basicblock">Texting1.MessageReceived</span> event-handler should look like the following:</p> <p><img src="/sites/all/files/tutorials/broadcastHub/storeBroadcastList.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p><span class="callblock">TinyDB1.StoreValue</span> 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.</p> <h4 class="ai-header">Loading the BroadcastList from a Database</h4> <p>Now add the blocks for loading the list back in each time the app begins. When the app begins, the <span class="basicblock">Screen1.Initialize</span> event is triggered, so your blocks will go in that event-handler. You’ll call <span class="callblock">TinyDB.GetValue</span>, 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.</p> <p>The blocks should look like:</p> <p><img src="/sites/all/files/tutorials/broadcastHub/getValue.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>The blocks first define a variable, valueFromDB, which is used to temporarily store the data returned from the database. When the app begins, the <span class="basicblock">Screen1.Initialize</span> event is triggered. <span class="callblock">TinyDB.GetValue</span> is called, and the data from the database is placed in valueFromDB.</p> <p>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.</p> <p>If valueFromDB is a list, the list is placed into the variable BroadcastList and the list is displayed.</p> <pre class="ai-testing"><b>Test this behavior.</b> Choose <b>Restart App</b> which will trigger the <span class="basicblock" style="color:black;">Screen1.Initialize</span>. Do the numbers registered earlier appear?</pre><h4 class="ai-header">Display BroadcastList with each number on separate lines</h4> <p>Create a procedure <span class="callblock">displayBroadcastList</span>, which displays the list with each phone number on a separate line. Be sure and call the procedure from below the <span class="listblock">add items to list</span> block so that the updated list is displayed.</p> <p>For help displaying the list on separate lines, see <a href="/content/displaying-list">Displaying a List</a>.</p> <p>You'll need the following blocks:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Block type </td> <td> Drawer </td> <td> Purpose </td> </tr><tr><td><span class="callblock">procedure</span> ("displayBroadcastList")</td> <td>Definitions</td> <td>Create the procedure</td> </tr><tr><td><span class="setblock">set BroadcastListLabel.Text to</span></td> <td>BroadcastListLabel</td> <td>Display the list here</td> </tr><tr><td><span class="textblock">text</span> ("Phone Numbers...")</td> <td>Text</td> <td>The header for the list</td> </tr><tr><td><span class="controlblock">foreach</span></td> <td>Control</td> <td>Iterate through the numbers</td> </tr><tr><td><span class="argblock">name pnumber</span></td> <td>in the <span class="controlblock">foreach</span></td> <td>Name the <span class="controlblock">foreach</span> variable "pnumber". This is the current item of the <span class="controlblock">foreach</span></td> </tr><tr><td><span class="argblock">BroadcastList</span></td> <td>My Definitions</td> <td>Plug into in list slot of <span class="controlblock">foreach</span></td> </tr><tr><td><span class="setblock">set BroadcastListLabel.Text to</span></td> <td>BroadcastListLabel</td> <td>Modify with each of the numbers</td> </tr><tr><td><span class="textblock">make text</span></td> <td>Text</td> <td>Build a text object from multiple parts</td> </tr><tr><td><span class="argblock">BroadcastListLabel.Text</span></td> <td>BroadcastListLabel</td> <td>Add to the label on each iteration of <span class="controlblock">foreach</span></td> </tr><tr><td><span class="textblock">text</span> ("\n")</td> <td>Text</td> <td>Newline character so that next number is on next line</td> </tr><tr><td><span class="argblock">value pnumber</span></td> <td>My Definitions</td> <td>The current number from the list</td> </tr></table><p>The <span class="callblock">displayBroadcastList</span> procedure should look like this:</p> <p><img src="/sites/all/files/tutorials/broadcastHub/displayBroadcastList.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>The <span class="controlblock">foreach</span> in <span class="callblock">displayBroadcastList</span> successively adds a phone number to the end of the label, placing a newline character in between each item.</p> <p>Of course this procedure will not do anything unless you call it: you should add two calls to the procedure: one within the <span class="basicblock">Texting1.MessageReceived</span> event-handler, and one within the <span class="basicblock">Screen1.Initialize</span> event-handler. The call <span class="callblock">displayBroadcastList</span> 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 <span class="callblock">displayBroadcastList</span> should look in the snapshot of the final version below.</p> <pre class="ai-testing"><b>Test this behavior.</b> Try adding some more phone numbers to the list. Do the registered phone numbers now appear on separate lines?</pre><h4 class="ai-header">Broadcast Hub, Final Version</h4> <p><img src="/sites/all/files/tutorials/broadcastHub/allBlocks.png" /></p> <h4 class="ai-header">Variations</h4> <p>Once you get the Broadcast Hub app working, you might want to explore some variations. For example,</p> <ul><li>Allow client phones to remove themselves from the list by texting "quitabc" to the app.</li> <li>Let the hub administrator (the user of your app) add and remove numbers from the broadcast list.</li> <li>Let the hub administrator specify numbers that should not be allowed into the list.</li> </ul><h4 class="ai-header">Review</h4> <p>Here are some of the ideas covered in this tutorial: </p> <ul><li>Apps can react to events, like a text being received, that are not initiated by the user of the app.</li> <li>Nested <span class="controlblock">ifelse</span> and <span class="controlblock">foreach</span> blocks can be used to code complex behaviors.</li> <li>The <span class="textblock">make text</span> block can be used to build a text object out of multiple parts.</li> <li><b>TinyDB</b> can be used to store and retrieve data from a database. A general scheme is to call <span class="callblock">StoreValue</span> to update the database whenever the data changes, and call <span class="callblock">GetValue</span> to retrieve the database data when the app begins.</li> </ul><h4 class="ai-header">Scan the Sample App to your Phone</h4> <p>Scan the following barcode onto your phone to install and run the sample app. </p><p><img src="/sites/all/files/tutorials/broadcastHub/broadcastHubBarcode.png" /></p> <p>Or <a href="http://explore.appinventor.mit.edu/sites/all/files/tutorials/broadcastHub/broadcastHub.apk">download the apk</a></p> <h4 class="ai-header">Download Source Code</h4> <p>If you'd like to work with this sample in App Inventor, download the <a href="http://explore.appinventor.mit.edu/sites/all/files/tutorials/broadcastHub/broadcastHub.zip">source code</a> to your computer, then open App Inventor, go to the My Projects page, and choose <b>More Actions | Upload Source</b>.</p> <pre class="ai-box">MIT and Google are grateful to <a href="http://www.appinventor.org/">Professor David Wolber</a>, CS Professor at The University of San Francisco, for developing this tutorial. Done with <span style="color:black;">BroadcastHub</span>? Return to the other tutorials <a href="http://explore.appinventor.mit.edu/tutorials">here</a>.</pre></div></div></div><section class="field field-name-field-version field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Version:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-version/app-inventor-1" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">App Inventor 1</a> </li> </ul> </section> <section class="field field-name-field-tutorial-difficulty field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Difficulty:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-difficulty/advanced" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Advanced</a> </li> </ul> </section> <section class="field field-name-field-tutorial-type field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Type:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-type/data-storage" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Data Storage</a> </li> <li class="field-item odd"> <a href="/tutorial-type/sms-texting" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">SMS Texting</a> </li> </ul> </section> Fri, 14 Jun 2013 16:13:05 +0000 aaron 317 at http://dev-explore.appinventor.mit.edu http://dev-explore.appinventor.mit.edu/content/broadcasthub#comments TextGroup (Part 2) http://dev-explore.appinventor.mit.edu/content/text-group-part-2 <div class="field field-name-body field-type-text-with-summary field-label-hidden view-mode-rss"><div class="field-items"><div class="field-item even" property="content:encoded"><style> <!--/*--><![CDATA[/* ><!--*/ li { padding-bottom: 7px; } .basicblock { border: 1px dashed #7AA81C; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E7F2CB; font-size: 9pt; text-wrap: suppress; } .callblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E0D1FF; font-size: 9pt; text-wrap: suppress; } .argblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E5E5FF; font-size: 9pt; text-wrap: suppress; } .textblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #FADAA0; font-size: 9pt; text-wrap: suppress; } .setblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #C1D5F8; font-size: 9pt; text-wrap: suppress; } .controlblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #FAEDBB; font-size: 9pt; text-wrap: suppress; } /*--><!]]>*/ </style><h2 class="ai-header"><span style="color:green;">Text Group Part II: Adding and Removing Members</span></h2> <h4 class="ai-header">What you're building</h4> <p><img src="/sites/all/files/tutorials/textGroup2/textGroup2emulator.png" style="float:right; width:250px;" /></p> <p>This tutorial extends the <a href="http://explore.appinventor.mit.edu/content/textgroup">Text Group</a> tutorial. That app sent a text to a fixed list of phone numbers, and only the programmer could change the numbers in the list. The app in this tutorial allows the user to add and remove the phone numbers in the list, and it stores the list persistently in a database. </p> <h4 class="ai-header">Getting started</h4> <p>Connect to the App Inventor web site and open your TextGroup project (if you haven't completed that tutorial, do so now before continuing). SaveAs the app and name it "TextGroup2". You can leave the screen's <b style="color:green;">Title</b> as "Text Group". Open the Blocks Editor and connect to the phone.</p> <h4 class="ai-header">Introduction</h4> <p>You'll design the app so that the user adds phone numbers to the group by choosing from the phone's contact list, and removes them by choosing from the items already in the group.. You'll store the list of numbers in a database so that they are there even when the app is closed and re-launched.</p> <p>The tutorial covers the following App Inventor components and concepts:</p> <ul><li>The <span class="control">foreach</span> block for repeating operations on a list.</li> <li>The <b>PhoneNumberPicker</b> component for accessing the phone's contacts.</li> <li>The <b>ListPicker</b> component for allowing the user to choose a number for removal.</li> <li>The <b>Texting</b> component for sending texts.</li> <li>The <b>TinyDB</b> database component for storing the phone list in a database. </li> </ul><h4 class="ai-header">Set up the Components</h4> <p>The user interface for Text Group, Part II has a textbox, button, and status label for sending the group text, just as in Part I. If you performed a SaveAs to begin this project, those components should already be in the view. You should then add a <b>PhoneNumberPicker</b> and <b>ListPicker</b> components for adding and removing phone numbers from the list, along with a <b>Texting</b> component and a <b>TinyDB</b> component, both of which will appear in the "non-visual" component area. A table of detailed instructions for designing the components is below, but you can also build it from the following picture of what it should look like:</p> <p><img src="/sites/all/files/tutorials/textGroup2/designerPart2.png" /></p> <p>The components listed below were used to create the designer window shown above. The first five were part of TextGroup so will already be there if you built that app and performed a SaveAs to begin this one. You'll need to drag each of the other components from the Palette into the Viewer and name it as specified below:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Component Type </td> <td> Palette Group </td> <td> What you'll name it </td> <td> Purpose of Component </td> </tr><tr><td> <b>TextBox</b> </td> <td>Basic</td> <td style="color:green;">MessageText</td> <td>User will enter message here.</td> </tr><tr><td> <b>Button</b> </td> <td>Basic</td> <td style="color:green;">TextGroupButton</td> <td>User enters the message here</td> </tr><tr><td> <b>Label</b> </td> <td>Basic</td> <td style="color:green;">StatusLabel</td> <td>Reports when texts have been sent</td> </tr><tr><td> <b>Texting</b> </td> <td>Social</td> <td style="color:green;">Texting1</td> <td>The component that sends the texts</td> </tr><tr><td><b>Clock</b> </td> <td>Basic</td> <td style="color:green;">Clock1</td> <td>The component that designates when to send the texts</td> </tr><tr><td> <b>Label</b> </td> <td>Basic</td> <td style="color:green;">GroupHeadingLabel</td> <td>Header for the list of phone numbers that will appear</td> </tr><tr><td> <b>Label</b> </td> <td>Basic</td> <td style="color:green;">MembersLabel</td> <td>This is where the list of phone numbers will appear</td> </tr><tr><td> <b>Horizontal Arrangement</b> </td> <td>Screen Arrangement</td> <td style="color:green;">HorizontalArrangement1</td> <td>To organize the add and remove buttons (pickers) horizontally</td> </tr><tr><td> <b>PhoneNumberPicker</b> </td> <td>Social</td> <td style="color:green;">PhoneNumberPicker1</td> <td>Allow user to choose from numbers in contact list</td> </tr><tr><td> <b>ListPicker</b> </td> <td>Basic</td> <td style="color:green;">RemoveListPicker</td> <td>Allow user to remove number from phone numbers in group</td> </tr><tr><td> <b>TinyDB</b> </td> <td>Basic</td> <td style="color:green;">TinyDB1</td> <td>For storing and retrieving group's phone numbers from database</td> </tr></table><p>Set the properties of the components in the following way:</p> <ul><li>Set the <b style="color:green;">Hint</b> of MessageText to "enter a message"</li> <li>Set the <b style="color:green;">Text</b> of TextGroupButton to "Text Group"</li> <li>Set the <b style="color:green;">Text</b> of StatusLabel to "status".</li> <li>Set the <b style="color:green;">Text</b> of GroupHeadingLabel to "-- Group -- ".</li> <li>Set the <b style="color:green;">Text</b> of MembersLabel to "members will appear here".</li> <li>Set the <b style="color:green;">Text</b> of PhoneNumberPicker1 to "Add Member".</li> <li>Set the <b style="color:green;">Text</b> of RemoverListPicker to "Remove Member"</li> </ul><h4 class="ai-header">Add behaviors to the components </h4> <p>If you completed Text Group, Part I, you should have the following list defined in the Blocks Editor:</p> <p><img src="/sites/all/files/tutorials/textGroup2/phonelist.png" /></p> <p>For this app, you still want the list but you don't want any pre-defined numbers in it-- the numbers will be added by the end-user of the app. So remove the text blocks with the numbers and leave the <span class="ai-blocks">make a list</span> block empty:</p> <p><img src="/sites/all/files/tutorials/textGroup2/emptylist.png" /></p> <p>You'll need to add the following behaviors:</p> <ol><li>When the user clicks the "Add Member" button (the button associated with the PhoneNumberPicker), the picker will appear for the user to choose a number. When the choice is made, the app will add the chosen number to the PhoneNumbers list and update the database.</li> <li>When the user clicks the "Remove Member" button (the button associated with the RemovListPicker), the picker will appear for the user to choose one of the numbers already in PhoneNumbers. When the choice is made, the app will remove the number from the list and update the database.</li> <li>When the app begins, the numbers stored in the database are retrieved and loaded into PhoneNumbers.</li> </ol><h4 class="ai-header">Adding a Phone Number (Member) to the Group </h4> <p>The list of phone numbers in the contact list will appear when the user clicks the PhoneNumberPicker 's associated button: that behavior is built-in to the component. When the user makes a choice from the contact list, PhoneNumberPicker 's AfterPicking event is triggered-- your job is to program this to add the chosen item to the list and update the database.</p> <p>You'll need the following blocks:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Block type </td> <td> Drawer </td> <td> Purpose </td> </tr><tr><td><span class="basicblock">PhoneNumberPicker1.AfterPicking</span></td> <td>PhoneNumberPicker1</td> <td>The event-handler that is triggered when user chooses from contact list</td> </tr><tr><td><span class="ai-blocks">add items to list</span></td> <td>Lists</td> <td>Your goal is to add chosen number to list </td> </tr><tr><td><span class="argblock">global PhoneNumbers</span></td> <td>My Definitions</td> <td>Plug this into list slot of add items to list</td> </tr><tr><td><span class="argblock">PhoneNumberPicker1.PhoneNumber </span></td> <td>PhoneNumberPicker1</td> <td>This holds the chosen phone number, plug into <b>item</b> slot of <span class="ai-blocks">add items to list</span></td> </tr><tr><td><span class="callblock">TinyDB1.StoreValue</span></td> <td>TinyDB1</td> <td>Since the list is updated, need to update the database</td> </tr><tr><td><span class="textblock">text</span> ("textGroup")</td> <td>Text</td> <td>Plug this in as the <b>tag</b> of <span class="callblock">TinyDB1.StoreValue</span></td> </tr><tr><td><span class="argblock">global PhoneNumbers</span></td> <td>My Definitions</td> <td>Plug this in as the <b>value</b> of <span class="callblock">TinyDB1.StoreValue</span></td> </tr><tr><td><span class="setblock">set MembersLabel.Text to</span></td> <td>MembersLabel</td> <td>Display the list on the phone</td> </tr><tr><td><span class="argblock">global PhoneNumbers</span></td> <td>My Definitions</td> <td>plug into <span class="setblock">set MembersLabel.Text to</span></td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/textGroup2/addMember.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>When the user chooses from the PhoneListPicker, the <span class="basicblock">PhoneListPicker1.AfterPicking</span> event is triggered. A PhoneListPicker' s job is to put the user's choice in its PhoneNumber property, so the event-handler just calls <span class="ai-blocks">add items to list</span> to get that choice into the PhoneNumbers list.</p> <p>PhoneNumbers is a variable, and variables are short-term memory that will disappear when the app is closed. Thus, after adding the item to the variable, <span class="callblock">TinyDB1.StoreValue</span> block is used to copy that list to the database-- the phone's long-term memory. The data is tagged with "textGroup". Later, you'll use that tag to re-load the phone number list from the database each time the app is opened.</p> <p>After updating the database, <span class="setblock">set MembersLabel.Text to</span> is called to display the list. This is necessary because list variables are hidden from the user until they are placed in a visible component. Directly placing a list into a label is a rudimentary way of displaying a list-- the list will appear in the form: (111-1111 222-2222). In the next section, you'll program a more elegant list display.</p> <pre class="ai-testing"><b>Test the behavior.</b> On the phone, click "Add Member" and choose a number from the list of contacts that appears. Does the number appear in the MembersLabel? Try adding additional numbers to see how the list is displayed. </pre><h4 class="ai-header">Displaying the List with Items on Separate Lines</h4> <p>At this point the app shows the user the phone numbers that have been added, but not very elegantly. Create a procedure <span class="callblock">displayMembers</span>, which displays the list with each phone number on a separate line. Be sure and call the procedure in the <span class="basicblock">PhonePicker1.AfterPicking</span> event, below the <span class="ai-blocks">add items to list</span> block, so that the updated list is displayed. The call to the procedure should replace the blocks that set the MembersLabel.Text directly to the list. For help displaying the list on separate lines, see <a href="http://explore.appinventor.mit.edu/content/displaying-list">Displaying a List</a>.</p> <p>You'll need the following blocks:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Block type </td> <td> Drawer </td> <td> Purpose </td> </tr><tr><td><span class="callblock">procedure</span> ("displayMembers")</td> <td>Definitions</td> <td>Create a procedure as you'll want to call this from multiple places</td> </tr><tr><td><span class="setblock">set MembersLabel.Text to</span></td> <td>MembersLabel</td> <td>Display the list here</td> </tr><tr><td><span class="textblock">text</span> ("")</td> <td>Text</td> <td>Start the text out empty</td> </tr><tr><td><span class="controlblock">foreach</span></td> <td>Control</td> <td>Iterate through the numbers</td> </tr><tr><td><span class="argblock">name number</span></td> <td>in the <span class="controlblock">foreach</span></td> <td>Name the foreach variable "number". This is the current item of the <span class="controlblock">foreach</span></td> </tr><tr><td><span class="argblock">global PhoneNumbers</span></td> <td>My Definitions</td> <td>Plug into in list slot of <span class="controlblock">foreach</span></td> </tr><tr><td><span class="setblock">set MembersLabel.Text to</span></td> <td>MembersLabel</td> <td>Modify with each of the numbers</td> </tr><tr><td><span class="textblock">make text</span></td> <td>Text</td> <td>Build a text object from multiple parts</td> </tr><tr><td><span class="setblock">MembersLabel.Text</span></td> <td>MembersLabel</td> <td>Add to the label on each iteration of <span class="controlblock">foreach</span></td> </tr><tr><td><span class="textblock">text</span> ("\n")</td> <td>Text</td> <td>Newline character so that next number is on next line</td> </tr><tr><td><span class="argblock">value number</span></td> <td>My Definitions</td> <td>The current number from the list</td> </tr></table><p>Here's how the blocks should look:</p> <p><img src="/sites/all/files/tutorials/textGroup2/displayMembers.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>The <span class="controlblock">foreach</span> in <span class="callblock">displayMembers</span> successively adds a phone number to the end of the label, placing a newline character in between each item.</p> <p>Of course this procedure will not do anything unless you call it: you should add a call to the procedure in the <span class="basicblock">PhonePicker1.AfterPicking</span> event, below the <span class="ai-blocks">add items to list</span> block. The <span class="callblock">call displayMembers</span> block can be found in My Definitions, and it should replace the blocks that directly set the MembersLabel.Text in the list.</p> <pre class="ai-testing"><b>Test the behavior.</b> On the phone, click "Add Member" to choose another number to add to the list. When the modified list is displayed, does each number appear on separate lines?</pre><h4 class="ai-header">Removing a Number from the list</h4> <p>To allow the user to remove a number from the list, you must first get the PhoneNumbers list into the RemoveListPicker that appears when the user clicks "Remove Member". Unlike the PhonePicker component, which gets its data from the phone's contact list, ListPicker requires you to explicilty load the items that will appear. You can do this by setting the Elements property of the ListPicker component. You do this within the <span class="basicblock">RemoveListPicker.BeforePicking</span> event, which is triggered right when the user clicks the button but before the list is shown.</p> <p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/textGroup2/BeforePicking.png" /></p> <p>Next, code the <span class="basicblock">RemoveListPicker.AfterPicking</span> event so that the user's choice is removed from the PhoneNumbers list. You'll need the following blocks:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Block type </td> <td> Drawer </td> <td> Purpose </td> </tr><tr><td><span class="basicblock">RemoveListPicker.AfterPicking</span></td> <td>RemoveListPicker</td> <td>This is triggered after the user chooses a number from the list</td> </tr><tr><td><span class="ai-blocks">remove list item</span></td> <td>Lists</td> <td>Removes the indexth item in a given list</td> </tr><tr><td><span class="argblock">global PhoneNumbers</span></td> <td>My Definitions</td> <td>Plug into list slot of <span class="ai-blocks">remove list item</span></td> </tr><tr><td><span class="ai-blocks">position in list</span></td> <td>Lists</td> <td>Given an item in a list, this gives you its index</td> </tr><tr><td><span class="argblock">RemoveListPicker.Selection</span></td> <td>RemoveListPicker</td> <td>The number chosen by the user, plug into thing slot of <span class="ai-blocks">position in list</span></td> </tr><tr><td><span class="argblock">global PhoneNumbers</span></td> <td>My Definitions</td> <td>Plug into list slot of <span class="ai-blocks">position in list</span></td> </tr><tr><td><span class="callblock">call displayMembers</span></td> <td>My Definitions</td> <td>Once the user removes one, display the modified list</td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/textGroup2/RemoveAfterPicking.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>When the user chooses a number from the RemoveListPicker, the <span class="basicblock">AfterPicking</span> event is triggered. The event-handler uses position in list to get the index of the chosen number, then uses that index to remove the number from the list PhoneNumbers. The database is then updated and the list displayed.</p> <pre class="ai-testing"><b>Test the behavior.</b> On the phone, click "Remove Member" and choose a number from the list that appears. When the list is re-displayed, is the number you chose gone? </pre><h4 class="ai-header">Loading the List from the Database</h4> <p>The app so far works, and when changes are made to the PhoneNumbers list they are also stored in a database. Now you just need to add the behavior such that the data stored in the database is loaded when the app launches.</p> <p>Program the <span class="basicblock">Screen1.Initialize</span> event-handler so that the saved list of phone numbers is retrieved from the database and placed in the PhoneNumbers list and the display is updated to show the list. You'll need to check the retrieved data to make sure there's something there-- after all, the first time the app is used, the database will not have any numbers in it.</p> <p>You'll need the following blocks:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Block type </td> <td> Drawer </td> <td> Purpose </td> </tr><tr><td><span class="setblock">def variable</span> ("valueFromDB") </td> <td>Definitions</td> <td>A temporary variable to hold the retrieved data</td> </tr><tr><td><span class="textblock">text</span> (blank)</td> <td>Text</td> <td>Initial value for the variable can be anything</td> </tr><tr><td><span class="basicblock">Screen1.Initialize</span></td> <td>Screen1</td> <td>This is triggered when app begins</td> </tr><tr><td><span class="setblock">set valueFromDB to</span></td> <td>My Definitions</td> <td>You'll set the variable to the value retrieved from db</td> </tr><tr><td><span class="callblock">TinyDB1.GetValue</span></td> <td>TinyDB1</td> <td>Get the stored response text from the database</td> </tr><tr><td><span class="textblock">text</span> ("textGroup")</td> <td>Text</td> <td>Plug into tag slot of <span class="callblock">GetValue</span>, make sure text is same as was used in StoreValue above</td> </tr><tr><td><span class="controlblock">if test</span></td> <td>Control</td> <td>To ask if the retrieved value has some text</td> </tr><tr><td><span class="ai-blocks">is list?</span></td> <td>Text</td> <td>Check if the data returned is a list (if no data is returned, this will be false)</td> </tr><tr><td><span class="">global valueFromDB</span> (x2)</td> <td>My Definitions</td> <td>This variable holds the value retrieved from <span class="calblock">GetValue</span></td> </tr><tr><td><span class="setblock">set PhoneNumbers to</span></td> <td>PhoneNumbers</td> <td>If we retrieved something, place it in PhoneNumbers</td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/textGroup2/screenInit.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>When the app begins, the <span class="basicblock">Screen1.Initialize</span> event is triggered. The event-handler queries the database with GetValue and the tag "textGroup". If there is a value with that tag in the database, its corresponding value will be placed in the variable valueFromDB. The event-handler checks this value-- is it a list?-- and if it is puts the list into the variable PhoneNumbers and calls displayMembers.</p> <pre class="ai-testing"><b>Test the app.</b> To test the final app, first package and install it on the phone and run it. Live testing won't work in this case because every time you "Restart App", in clears the phone's database: even if you've stored some data persistently, it is cleared when you restart the app. Click Restart App so that the app re-initializes. Does the list of numbers you've entered appear on reload? Try removing all items and testing again. Does the app launch without error?</pre><h4 class="ai-header">Text Group Part II, Final Program</h4> <p><img src="/sites/all/files/tutorials/textGroup2/textGroup2final.png" /></p> <h4 class="ai-header">Variations</h4> <p>Once you get the Text Group app working, you might want to explore some variations. For example,</p> <ul><li>Write a version that records both the contact name and phone number of each member of the group. You can also use ContactPicker to let the user choose from a contact name instead of a number.</li> <li>Write a version that allows the user to define and make use of multiple groups. You'll need a group list in which each item is a list of contacts/numbers, and you'll need a more complex user interface. </li> </ul><h4 class="ai-header">Review</h4> <p>Here are some of the ideas covered in this tutorial:</p> <ul><li>PhoneListPicker allow the user to choose from the phone numbers of his contacts. The choice is placed in the Selection property and the AfterPicking event is called when the user makes a choice.</li> <li>ListPicker is similar to PhoneListPicker but it displays a programmer-defined list, e.g., the list of phone numbers in the group. You define the list that will appear in the ListPicker by setting the Elements property, and you can do this in the BeforePicking event. The user's choice is placed in the Selection property, and the AfterPicking event is triggered when the user makes a choice.</li> <li>TinyDB is used to store data persistently so that it can be loaded each time an app begins. StoreValue stores with a tag to identify the data and the data itself (the value). GetValue returns the data for a given tag. Often, an app will call GetValue when the app begins (Screen1.Initialize) to load the persistent data.</li> <li>Currently, App Inventor does not provide full access and functionality for the contacts and groups of the phone. In this tutorial, you created your own group and app for processing the group (adding,removing, and texting all).</li> </ul><h4 class="ai-header">Scan the Sample App to your Phone</h4> <p>Scan the following barcode onto your phone to install and run the sample app.</p> <p><img src="/sites/all/files/tutorials/textGroup2/textgroup2barcode.png" /></p> <p>Or <a href="/sites/all/files/tutorials/textGroup2/textgroup2.apk">download the apk</a></p> <p>.</p> <h4 class="ai-header">Download Source Code</h4> <p>If you'd like to work with this sample in App Inventor, <a href="/sites/all/files/tutorials/textGroup2/TextGroup2_MIT.zip">download the source code</a> to your computer, then open App Inventor, go to the My Projects page, and choose <b>More Actions | Upload Source</b>.</p> <pre class="ai-box">MIT and Google are grateful to <a href="http://www.appinventor.org/">Professor David Wolber</a>, CS Professor at The University of San Francisco, for developing this tutorial. Done with <span style="color:black;">TextGroup Part 2</span>? Return to the other tutorials <a href="/tutorials">here</a>.</pre></div></div></div><section class="field field-name-field-version field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Version:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-version/app-inventor-1" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">App Inventor 1</a> </li> </ul> </section> <section class="field field-name-field-tutorial-difficulty field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Difficulty:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-difficulty/advanced" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Advanced</a> </li> </ul> </section> <section class="field field-name-field-tutorial-type field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Type:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-type/data-storage" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Data Storage</a> </li> <li class="field-item odd"> <a href="/tutorial-type/sms-texting" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">SMS Texting</a> </li> </ul> </section> Fri, 14 Jun 2013 16:12:40 +0000 aaron 316 at http://dev-explore.appinventor.mit.edu http://dev-explore.appinventor.mit.edu/content/text-group-part-2#comments TextGroup http://dev-explore.appinventor.mit.edu/content/textgroup <div class="field field-name-body field-type-text-with-summary field-label-hidden view-mode-rss"><div class="field-items"><div class="field-item even" property="content:encoded"><style> <!--/*--><![CDATA[/* ><!--*/ li { padding-bottom: 7px; } .basicblock { border: 1px dashed #7AA81C; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E7F2CB; font-size: 9pt; text-wrap: suppress; } .callblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E0D1FF; font-size: 9pt; text-wrap: suppress; } .argblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #E5E5FF; font-size: 9pt; text-wrap: suppress; } .textblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #FADAA0; font-size: 9pt; text-wrap: suppress; } .setblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #C1D5F8; font-size: 9pt; text-wrap: suppress; } .controlblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #FAEDBB; font-size: 9pt; text-wrap: suppress; } .listblock { border: 1px dashed #000000; padding-left: 4px; padding-right: 4px; padding-top: 1px; padding-bottom: 1px; background-color: #FCF5D7; font-size: 9pt; text-wrap: suppress; } /*--><!]]>*/ </style><h2 class="ai-header"><span style="color:green;">TextGroup</span></h2> <h4 class="ai-header">What you're building</h4> <p><img src="/sites/all/files/tutorials/textGroup/textgroup1.png" style="float:right;" /></p> <p>This tutorial introduces the <b>Texting</b> component for sending and processing SMS text messages.You'll build an app that texts a message to a list of phone numbers. 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 <a href="http://explore.appinventor.mit.edu/tutorials">basic tutorials</a> before continuing.</p> <h4 class="ai-header">Getting started</h4> <p>Connect to the App Inventor web site and start a new project. Name it TextGroup, and also set the screen's <b style="color:green;">Title</b> to "TextGroup." Open the Blocks Editor and connect to the phone.</p> <h4 class="ai-header">Introduction</h4> <p>You'll design the TextGroup app so that clicking on a button texts a message to a list of phone numbers. The app introduces the following App Inventor concepts:</p> <ul><li>The <b>Texting</b> component for sending texts and processing SMS texts.</li> <li>List variables to keep track of the numbers to text.</li> <li>The Timer block for repeating operations on a list of data (the list of phone numbers)</li> </ul><h4 class="ai-header">Set up the components</h4> <p>The user interface for <span style="color:green;">TextGroup</span> is simple: it has a text box for entering the message to send, a button to send the message, a label that reports when the message is sent, and a Texting component. Use the Component Designer to create this interface. When you finish, it should look something like the snapshot below (there are also more detailed instructions below the snapshot).</p> <p><img src="/sites/all/files/tutorials/textGroup/textgroup2.png" /></p> <p>The following table describes the components you need. Drag each component from the Palette into the Viewer and name it as specified below:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Component Type </td> <td> Palette Group </td> <td> What you'll name it </td> <td> Purpose of Component </td> </tr><tr><td> <b>TextBox</b> </td> <td>Basic</td> <td style="color:green;">MessageText</td> <td>User will enter message here</td> </tr><tr><td><b>Button</b></td> <td>Basic</td> <td style="color:green;">TextGroupButton</td> <td>User will click here after entering the message</td> </tr><tr><td><b>Label</b></td> <td>Basic</td> <td style="color:green;">StatusLabel</td> <td>Reports when texts have been sent</td> </tr><tr><td><b>Texting</b></td> <td>Social</td> <td style="color:green;">Texting1</td> <td>The component that sends the texts</td> </tr><tr><td><b>Clock</b></td> <td>Basic</td> <td style="color:green;">Clock1</td> <td>Used to designate when to send the texts</td> </tr></table><p>Set the properties of the components in the following way:</p> <ul><li>Set the the <b style="color:green;">Hint</b> of MessageText to "enter a message"</li> <li>Set the <b style="color:green;">Text</b> of TextGroupButton to "Text Group"</li> <li>Set the <b style="color:green;">Text</b> of StatusLabel to "status"</li> <li>Set the <b style="color:green;">TimerEnabled</b> of Clock1 to "false"</li> </ul><h4 class="ai-header">Add behaviors to the components</h4> <p><span style="color:green;">TextGroup</span>'s behavior is as follows: when the user clicks the TextGroupButton, the app should text the message entered by the user in MessageText to all the numbers in a list of phone numbers. When all are sent, it should report the last message sent in the StatusLabel.</p> <p>Text the entered message to one phone number.</p> <p>To introduce yourself to the Texting component, begin by creating a simplified version that texts the message entered by the user to your own phone. To send a text, you need to set two properties of the Texting component: the <b style="color:green;">PhoneNumber</b> and the <b style="color:green;">Message</b>. Once these properties are set, you call the SendMessage function to actually send the text.</p> <p>Open the Blocks Editor and drag out the following blocks:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Block Type </td> <td> Drawer </td> <td> Purpose </td> </tr><tr><td><span class="basicblock">TextGroupButton.Click</span></td> <td>TextGroupButton</td> <td>When the user clicks the button, this event handler is triggered.</td> </tr><tr><td><span class="setblock">set Texting1.Message to</span></td> <td>Texting1</td> <td>Set the <b style="color:green;">Message</b> property before sending</td> </tr><tr><td><span class="argblock">MessageText.Text</span></td> <td>MessageText</td> <td>This is the message the user has entered.</td> </tr><tr><td><span class="setblock">set Texting1.PhoneNumber to</span></td> <td>Texting1</td> <td>Set the <b style="color:green;">PhoneNumber</b> property before sending</td> </tr><tr><td><span class="textblock">text</span> ("1112222")</td> <td>Text</td> <td>Put your own phone number as the text.</td> </tr><tr><td><span class="callblock">Texting1.SendMessage</span></td> <td>Texting1</td> <td>Send the message</td> </tr></table><p>The blocks should look like this (but with a real phone number instead of "1112222")</p> <p><img src="/sites/all/files/tutorials/textGroup/textgroup3.png" /></p> <h4 class="ai-header">How the blocks work</h4> <p>When the user clicks the TextGroupButton, the event-handler is triggered. The first row of blocks gets the text entered by the user (MessageText.Text) and places it into the Message property of the Texting1 component. The second row of blocks sets the PhoneNumber property of the Texting1 component. After the first two rows of blocks, Texting1 knows what to send and who to send it to; the last block just instructs Texting1 to actually send the message.</p> <pre class="ai-testing"><b>Test this behavior.</b> If your phone is connected, the app should appear with the MessageText box and button. Enter a message and click on the TextGroupButton. Does the text get sent? Check your phone (or the phone you sent the text to) to see.</pre><h4 class="ai-header">Texting a list</h4> <p>Next, you'll modify the blocks to text more than one number at a time. You'll first define a <em>global variable</em> to store the data for the app: the phone numbers. Global variables are not seen by the user; they are the app's "hidden" memory. In this case, you'll define the list of phone numbers to which the text is sent.</p> <p>You'll need the following blocks to build a list of phone numbers:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Block Type </td> <td> Drawer </td> <td> Purpose </td> </tr><tr><td><span class="setblock">def variable</span></td> <td>Definitions</td> <td>Defines the <b style="color:green;">PhoneNumbers</b> variable (rename it)</td> </tr><tr><td><span class="listblock">make a list</span></td> <td>Lists</td> <td>Used to define the items of the list</td> </tr><tr><td><span class="textblock">text</span> (3)</td> <td>Text</td> <td>The actual phone numbers</td> </tr></table><p>The blocks should look like this, but with two or three phone numbers of your friends.</p> <p><img src="/sites/all/files/tutorials/textGroup/textgroup4.png" /></p> <p>Note that <span class="textblock">text</span> blocks are used for the phone numbers, not <span class="basicblock">number</span> blocks. This will allow for dashes, which are not allowed in <span class="basicblock">number</span> blocks. <span class="basicblock">number</span> blocks are used for data on which you want to perform mathematical computations.</p> <p>Now you're ready to modify the TextGroupButton event-handler so that it texts each number in the variable PhoneNumbers. </p> <p>You'll need to set up a <span class="controlblock">timer</span> to send a text message to each number in the list. </p> <pre class="ai-box">A <span class="controlblock" style="color;black;">timer</span> performs some action each time the clock ticks. To control the timer, define a global variable named <b>nTicks</b>. 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 <span class="controlblock" style="color;black;">timer</span> is done in two parts. In <span class="basicblock">TextGroupButton.Click</span> we will no longer send the message. Instead, we will initialize the global <span class="controlblock">nTicks</span> variable to the length of the <span class="controlblock">phoneNumbers</span> list. And we will enable the <span class="controlblock">Clock1</span>. The second part is done using a <span class="controlblock">Clock1.Timer</span> block. This block will contain the <span class="controlblock">Texting1.SendMessage</span> command. Each time the clock ticks the following steps will be performed: </pre><ul><li>Select a number from the <span class="controlblock">phoneNumbers</span> list using the <span class="controlblock">nTicks</span> as the index.</li> <li>Set the <span class="controlblock">Texting1.PhoneNumber</span> to the selected number.</li> <li>Send the text message using <span class="controlblock">Texting1.SendMessage</span>.</li> <li>Subtract 1 from the <span class="controlblock">nTicks</span> variable.</li> <li>If the <span class="controlblock">nTicks</span> is less than or equal to 0, disable the clock</li>. This will stop the timer. </ul><p>Reconfigure the <span class="basicblock">Clock.Timer</span> block so it looks like this: </p> <p><img src="/sites/all/files/tutorials/textGroup/clocktimerblock.png" /></p> <p>Reconfigure the <span class="basicblock">TextGroupButton.Click</span> block so it looks like this: </p> <p><img src="/sites/all/files/tutorials/textGroup/buttonclickblock.png" /></p> <h4 class="ai-header">How the blocks work</h4> <p>When the TextGroupButton is clicked, the <b style="color:green;">Texting1.Message</b> property is set to the message entered by the user in MessageText. Then the <span class="controlblock">nTicks</span> variable is set to the length of the <span class="controlblock">phoneNumbers</span> list and the <span class="controlblock">Clock1.TimerEnabled</span> is set to <b>true</b>. This will start the clock ticking. Then in the <span class="controlblock">Clock1.Timer</span> block, the message will be sent to the target phone number and the global variable <span class="controlblock">nTicks</span> will be decremented by 1. The clock will continue ticking and sending messages until <span class="controlblock">nTicks</span> at which point the <span class="controlblock">Clock1.TimerEnabled</span> will be set to <b>false</b> thereby stopping the timer.</p> <pre class="ai-testing"><b>Test the behavior.</b> Click the <b>TextGroupButton</b> on the phone. Is the text sent to all the numbers in the list?</pre><h4 class="ai-header">Reporting the Status of the app</h4> <p>It's always a good idea to give the user feedback when they initiate an operation. In the app you've built thus far, the user clicks on the <span style="color:green;">TextGroupButton</span> and nothing changes in the app's user interface. Though the user might hear the phone's chime for transmitting the text, it's still better to report a status directly in the app.</p> <p>Modify the blocks so that the status label reports the last message sent. You'll need the following blocks:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Block Type </td> <td> Drawer </td> <td> Purpose </td> </tr><tr><td><span class="setblock">set StatusLabel.Text to</span></td> <td>StatusLabel</td> <td>This is where status will appear</td> </tr><tr><td><span class="textblock">join</span></td> <td>Text</td> <td>To concatenate the two parts of the status message</td> </tr><tr><td><span class="textblock">text</span></td> <td>Text</td> <td>Put "last message sent:" in this text block</td> </tr><tr><td><span class="argblock">MessageText.Text</span></td> <td>MessageText</td> <td>The message just sent</td> </tr></table><p>The updated <span class="basicblock">Clock1.Timer</span> blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/textGroup/clocktimerblock2.png" /></p> <h4 class="ai-header">How the blocks work</h4> <p>The join block is used to concatenate two pieces of text. You need it when you want to join data from a variable or property with some other text. If you want to piece together more than two texts, there is also a <span class="textblock">make text</span> block.</p> <p>Test the status behavior. Does the status message report the message that was sent?</p> <h4 class="ai-header">Final program</h4> <p><b>TextGroup, Final Version</b></p> <p><img src="/sites/all/files/tutorials/textGroup/textGroupFinal.png" /></p> <h4 class="ai-header">Variations</h4> <p>Once you get the <span style="color:green;">TextGroup</span> app working, you might want to explore some variations. For example,</p> <ul><li>Write a "random date" version that randomly chooses a number in the list to call. You won't need a <span class="basicblock">Clock.Timer</span> block, but you will need the <span class="listblock">pick random item</span> function in the Lists drawer.</li> <li>Write a version in which "Missing You" is sent to the list of numbers as soon as the app begins (on the <span class="basicblock">Screen.Initialize</span> event). This will allow for quick texting without clicking an additional button.</li> <li>Write a version that allows the <em>user</em> to enter the numbers in the list. This way you don't need to re-program the blocks to change the group you are texting <em>and</em> your app can be used by others! You'll need to use <b>TinyDB</b> to store the numbers after they are entered.</li> </ul><h4 class="ai-header">Review</h4> <p>Here are some of the ideas covered in this tutorial:</p> <ul><li>Instead of placing data directly in event handlers, you can create list variables and program event handlers to process the list.</li> <li>An app can repeat functions on all items of a list using the <span class="basicblock">Clock.Timer</span> block.</li> </ul><h4 class="ai-header">Scan the Sample App to your Phone</h4> <p>Scan the following barcode onto your phone to install and run the sample app. </p><p> <img src="/sites/all/files/tutorials/textGroup/TextGroupBarcode.png" /></p> <p>Or <a href="http://explore.appinventor.mit.edu/sites/all/files/tutorials/textGroup/TextGroup.apk">download the apk</a></p> <h4 class="ai-header">Download Source Code</h4> <p>If you'd like to work with this sample in App Inventor, download the <a href="http://explore.appinventor.mit.edu/sites/all/files/tutorials/textGroup/TextGroup_MIT.zip">source code</a> to your computer, then open App Inventor, go to the My Projects page, and choose <b>More Actions | Upload Source</b>.</p> <pre class="ai-box">MIT and Google are grateful to <a href="http://www.appinventor.org/">Professor David Wolber</a>, CS Professor at The University of San Francisco, for developing this tutorial. Done with <span style="color:black;">TextGroup</span>? Return to the other tutorials <a href="/tutorials">here</a>.</pre></div></div></div><section class="field field-name-field-version field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Version:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-version/app-inventor-1" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">App Inventor 1</a> </li> </ul> </section> <section class="field field-name-field-tutorial-difficulty field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Difficulty:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-difficulty/advanced" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Advanced</a> </li> </ul> </section> <section class="field field-name-field-tutorial-type field-type-taxonomy-term-reference field-label-above view-mode-rss clearfix"> <h2 class="field-label">Tutorial Type:&nbsp;</h2> <ul class="field-items"> <li class="field-item even"> <a href="/tutorial-type/data-storage" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Data Storage</a> </li> <li class="field-item odd"> <a href="/tutorial-type/sms-texting" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">SMS Texting</a> </li> </ul> </section> Fri, 14 Jun 2013 16:12:11 +0000 aaron 315 at http://dev-explore.appinventor.mit.edu http://dev-explore.appinventor.mit.edu/content/textgroup#comments