Explore MIT App Inventor - Data Storage http://dev-explore.appinventor.mit.edu/tutorial-type/data-storage en Pizza Party with Fusion Tables for App Inventor 2 http://dev-explore.appinventor.mit.edu/ai2/pizzaparty <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><div style="float:right;"><img src="http://explore.appinventor.mit.edu/sites/all/files/tutorials/pizzaParty/pizza_party_screenshot.png" style="border:none; padding-left:15;" /><br /><a href="http://explore.appinventor.mit.edu/sites/all/files/ai2tutorials/pizzaParty/PizzaParty_MIT.aia"><img src="http://explore.appinventor.mit.edu/sites/all/files/tutorials/pizzaParty/downloadSourcebutton.png" style="border:none; padding-left:65;" /></a></div> <p>In this tutorial, you will create an app that lets people enter food orders for a pizza party. Orders are stored in a Fusion Table, providing one easy to access place for the data. By reading from the table, the app can easily display the orders that have been entered.</p> <p>A <a href="http://en.wikipedia.org/wiki/Google_Fusion_Tables">Fusion Table</a> is a Google service to support the gathering, managing, sharing, and visualizing of data. Data are stored in multiple tables on Google's <a href="http://en.wikipedia.org/wiki/Cloud_computing">cloud</a>. Individual tables can be merged (or fused) if they contain a common column, and they can be easily visualized on maps and in various kinds of charts and graphs. All of the data are stored in a public table that can be accessed via Google Drive, and allows different users<a href="#note">**</a> to add information to the tables. Coupled with a location sensor, an App Inventor app could post periodic updates of each user's location to a public fusion table. Users could post notes to mark noteworthy locations. For example, a team of botanists could use a Fusion Table app to create an annotated catalog of the trees or plants within a certain geographical area.</p> <p>This tutorial introduces:</p> <ul><li>Using the <b>FusionTables</b> component</li> <li>Using a <b>WebViewer</b> component</li> </ul><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 the app's behavior. If you are not familiar with the basics, try stepping through some of the <a href="/ai2/tutorials">basic tutorials</a> before continuing.</p> <hr /><p><strong>IF YOU DOWNLOAD THE SOURCE CODE</strong> you will need to supply your own API KEY, your own Table ID, and your own TABLE URL in the global variables in the blocks editor. This source code will not work without these additions.</p> <h4 class="ai-header">Creating your own Fusion Table</h4> <p>Creating your own Fusion Tables is as easy as creating a Google document, if you are familiar with that process.</p> <p><img src="/sites/all/files/tutorials/pizzaParty/MoreEvenMoreFusion.jpg" style="float:right" /></p> <ol><li>On the web, login to your Gmail account or any other Google service (e.g., Drive, YouTube).</li> <li>Select the <strong>More &gt; Even more</strong> menu and scroll to the bottom of the page of Google services where you will find the Fusion Tables service under "Innovation", click on the Fusion Tables link.</li> <li>Click "Create New Table"</li> <li>You will be given three options for the new table: "From this computer", "Google Spreadsheets", or "Create an empty table". For the purposes of this tutorial, select <b>Create an empty table</b>.</li> <li>You will see that the new table automatically comes with four columns. Change the column names for your Pizza app by going to <strong>Edit &gt; Change Columns</strong>.You'll rename the four default columns to <em>Date</em> (type=Date), <em>Name</em> (type=Text), <em>Pizza</em> (type=Text), <em>Drink</em> (type=Text). Click save and then add a fifth column by going to the <strong>Edit &gt; Add Column</strong>. Name this fifth column <em>Comment</em> (type=Text). .</li> <li>Leave this window open so that you can come back and get the URL, which you'll need when you set up the properties of the WebViewer component in your app.</li> <li>Click on the <strong>Share</strong> button (top right) to modify the table's permissions. For this tutorial, you can specify a few friends who will use the app. Only people who are explicitly given permission will be able to enter pizza party preferences through your app.</li> </ol><pre class="ai-box"><img src="/sites/all/files/tutorials/pizzaParty/FusionTables_GoogleDrive.png" style="float:right;padding:15px;" />Another way to create a new table: go to Google Drive. Click the <strong>Create</strong> button, then go to <strong>More &gt; Fusion Table</strong>. The picture at right shows what the Create button's menu should look like. If you do not see Fusion Table in your menu, then you may need to contact your school's network administrator. (See Troubleshooting section at the bottom of this page.) </pre><p><strong><a name="note" id="note">NOTE</a> about Sharing Fusion Tables:</strong> To share a FusionTable with others, you have to share it with each person individually, or you can share with a Google Group (see footnote at the end of this tutorial), the same way you would share a private google doc. There is no way to share write privileges to a FusionTable with the public. Public access is restricted to read-only.</p> <h4 class="ai-header">Getting an API Key</h4> <p>In order to use the FusiontablesControl Component you need to acquire a Google Applications Programming Interface (API) key, an API Key. To get an API key, follow these instructions:</p> <ol><li>Go to your <a href="https://code.google.com/apis/console/">Google APIs Console</a> and login if necessary.</li> <li>Select the <strong>Services</strong> item from the menu on the upper left.</li> <li>In the list of services, find the <strong>Fusion Tables API</strong> service and click the toggle button that currently says "off".</li> <li>Read and agree to the terms of service. When you return to the APIs Console page you'll see that the on/off switch next to Fusion Tables API is now "On" and green.</li> <li>Go back up the menu on the upper left of the screen and select the <strong>API Access</strong> item.</li> <li>Your API Key will be in the rectangular box in the section called "Simple API Access". You will need set the "API Key" property of the Fusiontables Control component in any app that you make that uses Fusion Tables. (More info below.)</li> </ol><h4 class="ai-header">Building the App</h4> <p>Connect to the App Inventor web site and start a new project. Name the new project PizzaParty, set the screen's orientation to Portrait and uncheck the Screen's scrollable property. You may also wish to set the Screen's Title property to something other than 'Screen 1'.</p> <h4 class="ai-header">The User Interface</h4> <p>In addition to the FusiontablesControl component, the Pizza Party app makes use of several other types of components. It is assumed that you have learned how to use these in previous lessons. Use the component designer to create the interface for the Search Party. When completed, the designer should look like this:</p> <p><img src="http://explore.appinventor.mit.edu/sites/all/files/ai2tutorials/pizzaParty/designer.png" /></p> <p>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> <td> Settings of Component </td> </tr><tr><td> <b>Label</b> </td> <td>Basic</td> <td style="color:green;">LabelName</td> <td>Shows the text "Your Name:"</td> <td></td> </tr><tr><td> <b>TextBox</b> </td> <td>Basic</td> <td style="color:green;">TextBoxUserName</td> <td>Gets input from user</td> <td>Set the width property to Fill Parent</td> </tr><tr><td> <b>HorizontalArrangement</b> </td> <td>Screen Arrangement</td> <td style="color:green;">HorizontalArrangement1</td> <td>Contains Name Label and Textbox</td> <td></td> </tr><tr><td> <b>ListPicker</b> </td> <td>Basic</td> <td style="color:green;">ListPickerPizza</td> <td>Accesses the list of available pizza types</td> <td> <p>Set the Width property to Fill Parent</p> <p>Set the Text property to "What type of pizza?"</p> <p>Set the ElementsFromString to "Cheese, Pepperoni, Anchovies, Hawaiian"</p> </td> </tr><tr><td> <b>ListPicker</b> </td> <td>Basic</td> <td style="color:green;">ListPickerDrink</td> <td>Accesses the list of available drinks.</td> <td> <p>Set the Width property to Fill Parent</p> <p>Set the Text property to "What type of drink?"</p> <p>Set the ElementsFromString to "Coke, Diet Coke, Sprite, Ginger Ale"</p> </td> </tr><tr><td> <b>Label</b> </td> <td>Basic</td> <td style="color:green;">LabelComment</td> <td>Shows the text "Comments:"</td> <td></td> </tr><tr><td> <b>TextBox</b> </td> <td>Basic</td> <td style="color:green;">TextBoxComments</td> <td>Takes user input</td> <td>Set the width property to Fill Parent</td> </tr><tr><td> <b>HorizontalArrangement</b> </td> <td>Screen Arrangement</td> <td style="color:green;">HorizontalArrangement2</td> <td>Contains Comments Label and Textbox</td> <td></td> </tr><tr><td> <b>Button</b> </td> <td>Basic</td> <td style="color:green;">ButtonSubmit</td> <td>Adds new data to the public fusion table</td> <td> <p>Set the Text to "Submit"</p> <p>Set the width property to Fill Parent</p> </td> </tr><tr><td> <b>WebViewer</b> </td> <td>Not ready for prime time</td> <td style="color:green;">WebViewer1</td> <td>Displays Fusion Table</td> <td>Set width and height properties to Fill Parent</td> </tr><tr><td> <b>FusiontablesControl</b> </td> <td>Not ready for prime time</td> <td style="color:green;">FusiontablesControl1</td> <td>Manages interactions with the app's Fusion Table</td> <td></td> </tr><tr><td> <b>Clock</b> </td> <td>Basic</td> <td style="color:green;">Clock1</td> <td>Used to provide a timestamp each time an order is placed.</td> <td></td> </tr><tr><td> <b>Notifier</b> </td> <td>Other stuff</td> <td style="color:green;">Notifier1</td> <td>Notifies the user of any errors</td> <td></td> </tr></table><h4 class="ai-header">Determining your Fusion Table URL and Table ID</h4> <p>In the blocks editor, you will set the WebViewer component's HomeURL property to point to the URL of your table. To find your Fusion Table's URL:</p> <ol><li>In your browser, navigate to the new Fusion Table you just created.</li> <li>Go to the menu and select <b>Tools &gt; Publish</b>.</li> <li>You'll see a notice saying: "This table is private and will not be visible". Click the blue link that says "Change Visibility".</li> <li>In the list of "Who Has Access", click the blue "Change..." link next to "Private - Only people listed below..."</li> <li>Choose"Public on the Web" or "Anyone with the link". Either of these settings will work for this tutorial. In the future, you should decide this setting based on the sensitivity of your data.</li> <li>Click the green Save button, then the blue Done button.</li> <li>Back on the Fusion Table page, go to the menu bar and select <strong>Tools &gt; Publish</strong>. Select the URL from the top text box (labeled "Send in an email or IM"), copy the URL and return to App Inventor. You will paste the URL into the definition block for the TABLE_URL (see below).</li> <li>The Table ID of the fusion table is contained within the URL after the plus sign. Look for "from+" and then copy all of the characters after the plus. This is the Table's ID. You can also find the Table ID by browsing to your table, then selecting <b>File&gt;About this table</b> in the menu.</li> </ol><h4 class="ai-header">Blocks Editor</h4> <p>Open the Blocks Editor so you can program the app's behavior. First, you will describe the app's variables. Variables whose names are ALL_CAPS are constants -- that is, variables whose values do not change while the program is running. It is good to get into the habit of using this naming convention. Define the following variables and give them the initial values shown in the table.</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Variable Name </td> <td> Initial Value </td> <td> Purpose </td> </tr><tr><td> <b>TABLE_URL</b> </td> <td>text</td> <td>Initialize this global variable to the "published" URL of your fusion table. See instructions above.</td> </tr><tr><td> <b>TABLE_ID</b> </td> <td>text</td> <td>Initialize this global variable to your table ID (e.g. a long string of characters unique to your fusion table). See instructions above.<font></font></td> </tr><tr></tr><tr><td> <b>API_KEY</b> </td> <td>text</td> <td>Initialize this global variable to your own API Key for Google Fusion Tables. See instructions above.</td> </tr><tr><td> <b>UserName</b> </td> <td>text</td> <td>Records the name of the user.</td> </tr><tr><td> <b>pizza</b> </td> <td>text</td> <td>Stores the pizza choice input by the user.</td> </tr><tr><td> <b>drink</b> </td> <td>text</td> <td>Stores the drink choice input by the user.</td> </tr><tr><td> <b>comment</b> </td> <td>text</td> <td>Stores the comment input by the user.</td> </tr></table><p><b>Initializing the App</b></p> <p>It is important to perform some initialization steps whenever the app is started. These are done in the <span class="eventblock">Screen1.Initialize</span> block. For this app we need to set the initial values for the FusionTable component's API Key property (set to global API_KEY) and the WebViewer component's HomeURL property (set to global TABLE_URL). We also tell the app to forget the user's login credentials. This will prompt the user to login to their Google account and give permission to the app to access the Fusion Table. This authentication step will happen only once when the app first tries to access the Fusion Table. Remember, Fusion Tables are only writeable by users who have been given permission by the table's owner. You specify this in the Sharing settings for the Fusion Table (easy to do from the Google Drive webpage.)</p> <p><img src="http://explore.appinventor.mit.edu/sites/all/files/ai2tutorials/pizzaParty/initialblocks.png" /></p> <p>Set up the <span class="callblock-ai2">resetForm</span> procedure as shown below. After recording an entry, this procedure resets the interface back to the original state.</p> <p><img src="http://explore.appinventor.mit.edu/sites/all/files/ai2tutorials/pizzaParty/resetForm.png" /></p> <p><b>List Picker Blocks</b></p> <p>In the designer, you set the choices for the pizza and drink types by filling in the "Selection" property with comma separated lists. These pre-programmed choices will be displayed on the user interface so the user can select their food and drink. Their selections are stored in the pizza and drink variables.</p> <p><img src="http://explore.appinventor.mit.edu/sites/all/files/ai2tutorials/pizzaParty/listpicker.png" /></p> <p><b>Submitting Data</b><br /></p><p></p> <p>Once the user has entered their name, food choices, and comments, they will click the <b>Submit</b> button. The app tests to make sure that the name, pizza, and drink fields have values in them, and prompts the user to try again if any of the required answers are missing. Notice that the <span class="textblock-ai2">text =</span> block is used (find it under Built-in palette, Text drawer). This block compares two strings of text to see if they are equal. If all required information is present, it calls the procedure <span class="callblock-ai2">InsertDataInTable</span> (see below). The blocks for the <span class="eventblock">ButtonSubmit.Click</span> are shown here:</p> <p><img src="http://explore.appinventor.mit.edu/sites/all/files/ai2tutorials/pizzaParty/submit.png" /></p> <p><b>Inserting Data into the Fusion Table</b></p> <p>The FusiontablesControl component is used to send the data to the Fusion Table. This action will create a new row in the Fusion Table, setting the values of the various columns involved. App Inventor makes this easy to do, but you have to be careful that the insert query is formatted correctly.</p> <p>This procedure involves two steps: (1) constructing the insert query and then (2) sending the query to Google's Fusion Table service. The query we want to send will take this format:</p> <p>INSERT INTO<br /><tableid> (<column1>, <column2>, ...) VALUES (<value1>, <value2>, ...)</value2></value1></column2></column1></tableid></p> <p>The words in CAPS are part of the query's syntax. The words in parentheses are values that we need to plug in. First there is a list of (column names) followed by VALUES followed by a list of (value names). The order of the column names and value names must be in the same order so that they match up. An example of what this might look like is shown below. Notice that the values must be enclosed in single quotes:</p> <p>INSERT INTO 191GHtZ_B2 (Name, Date) VALUES ('Sam', '10/10/2012')</p> <p>First, setup a new <span class="callblock-ai2">Procedure With Result</span> that takes a string and returns it surrounded by single quotes. The procedure <span class="callblock-ai2">quotify</span> is used in the InsertDataInTable procedure to place quotes around all of the values in the query. It also takes care of "escaping" any single quotes or apostrophes that are input by the user. You can send single apostrophes as part of a value in the query, so the "replace all" block adds an extra single quote. Two single quotes in a row are interpreted as one single quote.</p> <p><img src="http://explore.appinventor.mit.edu/sites/all/files/ai2tutorials/pizzaParty/quotify.png" /></p> <p>To construct the query we use App Inventor's join text block. Be sure to put spaces where needed, such as before and after the words INSERT INTO and VALUES:</p> <p><img src="/sites/all/files/ai2tutorials/pizzaParty/insertTable.png" /></p> <p>For this app, the column names must match the column names of the table we created earlier (with columns Date, Name, Pizza, Drink, Comment). Their respective values are taken from the procedure's global variables. Note: If you did not use these exact words for your table's columns, then be sure to use your table's column names when you build your query.</p> <p>Don't forget the <span class="callblock-ai2">FusiontablesControl.SendQuery</span> command at the very end of this procedure. It's small in size compared to rest of the procedure, but very important.</p> <p><b>Handling the Response from the Fusion Tables Service</b></p> <p>The <span class="eventblock">FusiontablesControl.GotResult</span> event will be fired when the app receives a response from Google's Fusion Tables Service. For an insert query the service will return the rowID of the new row that was inserted or an error message if something went wrong. In this simple example, we use the "contains" block ( (find it under Built-in palette, Text drawer) to check whether the result string has the rowID in it. If so, then we know that the rowID was received, and we then invoke the <span class="callblock-ai2">WebViewer.GoHome</span> procedure, which reloads the "HomeURL" as specified in the WebViewer's properties. Note that this set of blocks also calls the <span class="callblock-ai2">resetForm</span> procedure. After recording an entry, it resets the interface back to the original state.</p> <p><img src="http://explore.appinventor.mit.edu/sites/all/files/ai2tutorials/pizzaParty/gotresult.png" /></p> <p><b>You're done!</b> Package the app by going to <b>Package for Phone</b> on the Designer. You can now test the App for the purposes of the pizza party. Once you understand this tutorial, you'll be ready to make new Fusion Tables and modify the app to collect different types data from users. Remember, you have to give each user permission to access the Fusion Table. You do this through the Google Fusion Tables interface, not through App Inventor. Luckily, this means you don't have to change the app in order to add more users who can access the table.</p> <h4 class="ai-header">Challenges</h4> <p>This app has the Fusion Table hard-coded into the blocks. Find a way to let users specify their own fusion table ID so that they can host their own Pizza Party.</p> <h4 class="ai-header">Variations</h4> <p>Now that you have a simple app that uses fusion tables and a webviewer working, you might want to build some other apps with Fusion Tables. For example:</p> <ul><li>Include a <b>LocationSensor</b> so that the user's location can be added to the Fusion Table to create a map with notes</li> <li>Make the WebViewer display something other than the table of stored values; perhaps a map or a chart</li> </ul><h4 class="ai-header">Troubleshooting</h4> <p>If you are using a Google Apps for Education account, and you are not able to create a new fusion table (in the "Create" menu of Google Drive you won't even see an option for Fusion Tables), you will need to ask your system administrator to turn this option on for you. Or, you can switch to a standard gmail account. Fusion Tables are not automatically turned on for Google Apps for Education Accounts, your system administrator must make Fusion Tables available to the accounts in your domain.</p> <p>If you are receiving errors when trying to submit to the Fusion Table, especially if the error mentions Authentication, be sure that you have put the correct API Key into the API Key property field on the FusionTables component in the Design Window.</p> <p>To familiarize yourself with fusion tables, have a look around the <a href="http://support.google.com/fusiontables/bin/answer.py?hl=en&amp;answer=2571232">Fusion Tables Web Site</a>. Check out the example gallery to see what kinds of things are possible. Work through this <a href="http://earth.google.com/outreach/tutorial_fusion_sample.html">Fusion Table tutorial</a>, which shows how import some data, create a Fusion Table, and view the data on a map with your browser. You'll need to log in with your Google account.</p> <p><strong>To share a FusionTable with many people</strong>, rather than by entering email addresses individually, you can <strong>create a dedicated Google Group for your app and let all the app users know how to join the group</strong> (maybe through the web page for your app). From the moment you create your app, you share the Fusion Table once with your Google Group (using its email address), and <strong>make sure to give to the group editing rights</strong>. Every user who joins the group will inherit the editing rights automatically.</p> <pre class="ai-box"><span style="color:black;">Done with PizzaParty?</span> <a href="http://explore.appinventor.mit.edu/tutorials">Return to the other tutorials</a>.</pre><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/ai2tutorials/pizzaParty/PizzaParty_MIT.aia">source code</a> to your computer, then open App Inventor, go to the My Projects page, and choose <b>More Actions | Upload Source</b>. Remember to add in your API KEY and Fusion Table information in the Designer and blocks.</p> </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-2" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">App Inventor 2</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/data-storage" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Data Storage</a> </li> </ul> </section> Thu, 08 Aug 2013 15:19:00 +0000 joanie 381 at http://dev-explore.appinventor.mit.edu http://dev-explore.appinventor.mit.edu/ai2/pizzaparty#comments Android, Where's My Car http://dev-explore.appinventor.mit.edu/content/android-wheres-my-car <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;">Android, Where's My Car?</span></h2> <p><a href="/sites/all/files/tutorials/wheresMyCar/wheresMyCar.zip"><img src="/sites/all/files/tutorials/wheresMyCar/android-where.jpg" style="float:right; width:250px;" /></a></p> <p><a href="http://cs.usfca.edu/~wolber/appinventor/bookSplits/ch7AndroidWhere.pdf">Download Refined Version (Book Chapter PDF)</a></p> <p>You parked somewhere near the stadium or bar, but when the concert/party ends you don't have a clue where the car is. The friends you came with are equally as clueless.</p> <p>Fortunately you haven't lost your Android phone that never forgets anything, and you remember you have the hot new app, <span style="color:green;">Android, Where's My Car?</span>. With this app, you click a button when you park your car, and the Android uses its location sensor to record the car's GPS coordinates and address. Later, when you reopen the app, it shows you a map from where you are to the remembered location-- problem solved!</p> <p><a href="http://www.flickr.com/photos/dailylifeofmojo/4128596089/">Photo</a> licensed under <a href="http://creativecommons.org/licenses/by/2.0/deed.en">Creative Commons</a></p> <h4 class="ai-header">Download it, Customize it, Understand it</h4> <p>Below is a customizable, annotated and explained <span style="color:green;">Android, Where's My Car?</span> app. You can:</p> <p><a href="/sites/all/files/tutorials/wheresMyCar/wheresMyCar.zip"><img src="/sites/all/files/tutorials/wheresMyCar/downloadSourcebutton_1.png" style="float:right; width:250px;" /></a></p> <ul><li>Install the app on your phone by scanning a barcode</li> <li>Download the source code blocks for the app, upload them into App Inventor, then customize the app or use it as the basis for another idea you have.</li> <li>Study the annotated blocks and explanations below to better understand the app and App Inventor programming in general.</li> </ul><h4 class="ai-header">Try It</h4> <p>Scan the barcode to your Android phone. It will step you through installing the app.</p> <p>(If you don't have a scanner, download the free ZXing scanner by searching for it at the Android Market)</p> <p><img src="/sites/all/files/tutorials/wheresMyCar/wheresMyCarQRapk.png" /></p> <h4 class="ai-header">Customize It</h4> <p><img src="/sites/all/files/tutorials/wheresMyCar/customize.png" style="float:right;" /></p> <p>Download the source code blocks to create your own customized app.</p> <ol><li>Save the source file (.zip) to your computer (click icon to the right)</li> <li>Do not unzip it-- leave it as a zip file.</li> <li>Open the My Projects page in App Inventor.</li> <li>Select More Actions | Upload Source from the menu.</li> <li>Choose the file you just downloaded</li> </ol><h4 class="ai-header">Understand It</h4> <p>The app demonstrates how to communicate with the Android location sensor, how to record data in the phone's long-term memory (database), and how you can open the Google Maps app from your app to show directions from one one location to another. It makes use of the following App Inventor components:</p> <ul><li><b>Location Sensor</b></li> <li><b>TinyDB</b> -- to store the data</li> <li><b>ActivityStarter</b> -- to open a map</li> </ul><h4 class="ai-header">The User Interface</h4> <p>Here are the components for the <span style="color:green;">Android, Where's My Car?</span> app, as shown in the Component Designer:</p> <p><img src="/sites/all/files/tutorials/wheresMyCar/WheresMyCarDesign.png" /></p> <p>The user interface consists of labels to show location data and buttons to initiate events. Some labels just show static text, e.g., GPSLabel is the text "GPS:" that appears in the user interface. Others, such as CurrentLatLabel, will display dynamic data one the location sensor gets its readings. For these labels, a default value is set (0.0) here in the Component Designer.</p> <p>The ActivityStarter1 component is used to launch the map when the user asks for directions. Its properties are only partially shown above. Here is how they should be specified:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td>Property</td> <td>Value</td> </tr><tr><td> <b>Action</b> </td> <td>android.intent.action.VIEW</td> </tr><tr><td> <b>ActivityPackage</b> </td> <td>com.google.android.apps.maps</td> </tr><tr><td> <b>ActivityClass</b> </td> <td>com.google.android.maps.MapsActivity</td> </tr></table><h4 class="ai-header">The App's Behavior</h4> <p>Here are the blocks for the <span style="color: green;">Android, Where's My Car</span> app (the yellow annotations will also appear when you load this app into App Inventor):</p> <p><img src="/sites/all/files/tutorials/wheresMyCar/WheresMyCarBlocks.png" /></p> <p>Let's examine the four different event-handlers of the app, starting in the top-left and working around in counter-clockwise order.</p> <p><span class="basicblock">LocationSensor1.LocationChanged</span>: This event occurs when the phone's location sensor first gets a reading, or when the phone is moved to produce a new reading, The event-handler just places the readings--latitude, longitude, and current (street) address-- into the corresponding "Current" labels so that they appear on the phone.The RememberButton is also enabled in this event-handler. Its enabled setting should be unchecked in the Component Designer because there is nothing for the user to remember until the sensor gets a reading.</p> <p><span class="basicblock">RememberButton.Click</span>: When the user clicks the RememberButton , the location sensor's current readings are put into the "remember" labels and stored to the database as well. The DirectionsButton is enabled as it now makes sense for the user click on it to see a map (though it will make more sense once the user changes location).</p> <p><span class="basicblock">DirectionsButton.Click</span>: When the user clicks the DirectionsButton , the event-handler builds a URL for a map and calls ActivityStarter to launch the Maps application and load the map. <span class="textblock">make text</span> is used to build the URL to send to the Maps application. The resulting URL consists of the Maps domain along with two crucial parameters, saddr and daddr , which specify the start and destination for the directions. For this app, the saddr is set to the latitude and longitude of the current location, and the daddr is set to the latitude and longitudeo of the location that was "remembered" (the location of your car!).</p> <p><span class="basicblock">Screen1.Initialize</span>: This event is always triggered when an app opens. To understand it, you have to envision the user recording the location of the car, then closing the app, then later re-opening the app. When the app re-opens, the user expects that the location remembered earlier should appear on the phone. To facilitate this, the event-handler queries the database (<span class="callblock">TinyDB.GetValue</span>). If there is indeed a remembered address stored in the database-- the length of the stored address is greater than zero--the remembered latitude, longitude, and street addres are placed in the corresponding labels.</p> <h4 class="ai-header">Variations</h4> <ul><li>Create "Android, Where is Everyone?", an app that lets a group of people track each other's whereabouts. Whether your hiking or at the park, this app could help save time and even lives.</li> <li>Create a "breadcrumb" app that tracks your (phone's) whereabouts by recording each location change. One interesting refinement would be to only record a new "breadcrumb" if the location has changed by a certain amount.</li> </ul><h4 class="ai-header">Review</h4> <p>Lines and paragraphs break automatically.</p> <p>Here are some of the ideas covered in this tutorial:</p> <ul><li>The <b>LocationSensor</b> component can report the phone's latitude, longitude, and current street address. Its LocationChanged event is triggered when sensor gets its first reading and when the reading changes (the phone has moved).</li> <li>The <b>ActivityStarter</b> component can launch any app including Google Maps. For Maps, you set the DataUri property to the URL of the map you want to display. If you want to show directions, the URL will be of the form: <a href="http://maps.google.com/maps/?saddr=0.1,0.1&amp;daddr=0.2,0.2">http://maps.google.com/maps/?saddr=0.1,0.1&amp;daddr=0.2,0.2</a>, where the numbers are GPS coordinates.</li> <li><span class="textblock">make text</span> is used to piece together (concatenate) separate text items into a single text object. It allows you to concatenate dynamic data with static text. With the Maps URL, the GPS coordinates are the dynamic data.</li> <li><b>TinyDB</b> allows you to store data persistently in the phone's database. Whereas the data in a variable or property is lost when an app closes, the data you store in the database can be loaded into your app each time it is opened.</li> </ul><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;">Android, Where's My Car</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/activity-starter" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Activity Starter</a> </li> <li class="field-item odd"> <a href="/tutorial-type/data-storage" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Data Storage</a> </li> <li class="field-item even"> <a href="/tutorial-type/gps" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">GPS</a> </li> </ul> </section> Fri, 14 Jun 2013 16:21:22 +0000 aaron 322 at http://dev-explore.appinventor.mit.edu http://dev-explore.appinventor.mit.edu/content/android-wheres-my-car#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 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 MapTour http://dev-explore.appinventor.mit.edu/content/maptour <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;">MapTour</span></h2> <h4 class="ai-header">What you're building</h4> <p><img src="/sites/all/files/tutorials/mapTour/eiffel-tower.png" style="float:right; width:250px;" /></p> <p><a href="http://cs.usfca.edu/~wolber/appinventor/bookSplits/ch6Map.pdf">Download Refined Version (Book Chapter PDF)</a></p> <p>This is a two-part tutorial introduces the <b>ActivityStarter</b> component for launching arbitrary Android Apps and the <b>ListPicker</b> component for allowing a user to choose from a list of items. You'll build <span style="color:green;">MapTour</span>, an app for visiting French vacation destinations with a single click. Users of your app will be able to visit the Eiffel Tower, the Louvre, and Notre Dame in quick succession.</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>Once you complete the first part of this tutorial, try <a href="http://beta.appinventor.mit.edu/learn/tutorials/maptour/maptour2.html">part 2</a>.</p> <p><br /></p> <p><a href="/sites/all/files/tutorials/mapTour/MapTour.zip"><img src="/sites/all/files/tutorials/mapTour/downloadSourcebutton_0.png" style="float:right; width:250px;" /></a></p> <p><br /><br /><br /></p> <h4 class="ai-header">Getting started</h4> <p>Connect to the App Inventor web site and start a new project. Name it "MapTour", and also set the screen's <b style="color:green;">Title</b> to "MapTour". Open the Blocks Editor and connect to the phone.</p> <h4 class="ai-header">Introduction</h4> <p>You'll design the app so that a list of destinations appears. When the user chooses one, the Google Maps app is launched to display a map of the destination.</p> <p>The tutorial introduces the following App Inventor concepts: </p> <ul><li>The <b>ActivityStarter</b> component for launching Android apps from your app.</li> <li>The <b>ListPicker</b> component for allowing the user to choose from a list of choices. </li> </ul><h4 class="ai-header">Set up the Components</h4> <p>The user interface for MapTour is simple: you'll have a single ListPicker component and an ActivityStarter (non-visible) component. The design view should look like this when you're done:</p> <p><img src="/sites/all/files/tutorials/mapTour/designer.png" /></p> <p>The components listed below were used to create this designer window. Drag each component from the Palette into the Viewer and name it as specified:</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>Image</b> </td> <td>Basic</td> <td style="color:green;">Image1</td> <td>Show a static image of Paris map on screen</td> </tr><tr><td> <b>ListPicker</b> </td> <td>Basic</td> <td style="color:green;">ListPicker1</td> <td>Display the list of destinations</td> </tr><tr><td> <b>ActivityStarter</b> </td> <td>other stuff</td> <td style="color:green;">ActivityStarter1</td> <td>Launches the maps app when a destination is chosen </td> </tr></table><p>Download the file parismap.png onto your computer, then choose Add in the Media section to load it into your project. For it to appear, you'll also need to set this as the <b style="color:green;">Picture</b> property of Image1.</p> <p>The ListPicker component has an associated button-- when the user clicks it, the choices are listed. Set the text of that button by setting the Text property of ListPicker1 to "Choose Destination".</p> <h4 class="ai-header">Setting the properties of ActivityStarter</h4> <p><b>ActivityStarter</b> is a component that lets you launch any Android app-- the browser, Maps, even another one of your own apps. When another app is launched from your app, the user can click the back button to get back to your app. You'll build <span style="color:green;">MapTour</span> so that the Maps application is launched to show particular maps based on the user's choice. The user can then hit the back button to return to your app and choose a different destination.</p> <p>ActivityStarter is a relatively low-level component in that you'll need to set some properties of the component with information familiar to a Java Android SDK programmer, but foreign to the rest of the 99.999% people in the world. Not to worry, however. By copying the sample protocol information provided here, you can easily learn how to launch apps like Maps from the apps you are creating.</p> <p>So, to set-up the ActivityStarter to launch the Maps application, set the following ActivityStarter properties in the Component Designer:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td>Property</td> <td>Value</td> </tr><tr><td>Action</td> <td>android.intent.action.VIEW</td> </tr><tr><td>ActivityPackage</td> <td>com.google.android.apps.maps</td> </tr><tr><td>ActivityClass</td> <td>com.google.android.maps.MapsActivity</td> </tr></table><p>With these values set, you'll be able to launch the Maps application. In the Blocks Editor, you'll set one more property, DataUri , which will allow you to launch Maps with a particular map being displayed. This property must be set in the Blocks Editor because it will change based on whether the user chooses to visit the Louvre, the Eiffel Tower, or the Notre Dame.</p> <h4 class="ai-header">Create a list of destinations</h4> <p>Open the blocks editor and create a variable with the list of destinations for the MapTour. 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="listblock">def variable</span> ("Destinations")</td> <td>Definitions</td> <td>create a list of the destinations</td> </tr><tr><td><span class="listblock">make a list</span></td> <td>Lists</td> <td>use to add the items to the list </td> </tr><tr><td><span class="textblock">text</span> ("Tour Eiffel")</td> <td>Text</td> <td>the first destination</td> </tr><tr><td><span class="textblock">text</span> ("Musee du Louvre")</td> <td>Text</td> <td>the second destination</td> </tr><tr><td><span class="textblock">text</span> ("Cathedrale Notre Dame")</td> <td>Text</td> <td>the third destination</td> </tr></table><p>The blocks will look like this:</p> <p><img src="/sites/all/files/tutorials/mapTour/destinationList.png" /></p> <h4 class="ai-header">Add behaviors to the components</h4> <p>MapTour has two behaviors:</p> <ol><li>When the app begins, the app loads the destinations into the ListPicker component so the user can choose.</li> <li>When the user chooses a destination from the ListPicker, the Maps application is launched to show a map of that destination. For Part I, you'll just open Maps and tell it to invoke a search for the chosen destination</li> </ol><p>The ListPicker component displays a list of items when the user clicks a button. ListPicker has a property Elements . If you set Elements to a list, the items in the list will appear in the ListPicker . For this app, you want to set the ListPicker' s Elements property to the destinations list you just created. Because you want this to happen when the app begins, you'll define this behavior in the <span class="basicblock">Screen1.Initialize</span> event. 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">Screen1.Initialize</span></td> <td>Screen1</td> <td>this event is triggered when the app starts</td> </tr><tr><td><span class="setblock">set ListPicker1.Elements to</span></td> <td>ListPicker1</td> <td>set this property to the list you want to appear </td> </tr><tr><td><span class="argblock">global destinations</span></td> <td>My Definitions</td> <td>the list of destinations</td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/mapTour/screenInit.png" /></p> <h4 class="ai-header">How the blocks work</h4> <p><span class="basicblock">Screen1.Initialize</span> is triggered when the app begins. The event-handler just sets the Elements property of ListPicker so that the three destinations will appear.</p> <pre class="ai-testing"><b>Test this behavior.</b> On the phone, click the button labeled "Choose Destination". The list picker should appear with the three items.</pre><h4 class="ai-header">Launching Maps with a search for the destination</h4> <p>Next, you'll program the behavior that should occur when the user chooses one of the destinations-- the ActivityStarter should launch Maps and search for the selected destination.</p> <p>When the user chooses from the ListPicker component, the <span class="basicblock">ListPicker.AfterPicking</span> event is triggered. In the event-handler for AfterPicking you need to set the DataUri of the ActivityStarter component so it knows which map to open, then you need to launch the app using StartActivity.</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">ListPicker1.AfterPicking</span></td> <td>ListPicker1</td> <td>this event is triggered when the user chooses from ListPicker</td> </tr><tr><td><span class="setblock">set ActivityStarter1.DataUri to</span></td> <td>ActivityStarter1 </td> <td>the DataUri tells Maps which map to open on launch </td> </tr><tr><td><span class="textblock">make text</span></td> <td>Text</td> <td>build the DataUri from two pieces of text</td> </tr><tr><td><span class="textblock">text</span> ("geo:0,0?q=")</td> <td>Text</td> <td>the first part of the DataUri expected by Maps</td> </tr><tr><td><span class="argblock">ListPicker1.Selection</span></td> <td>ListPicker1</td> <td>the item the user chose</td> </tr><tr><td><span class="callblock">ActivityStarter1.StartActivity</span></td> <td>ActivityStarter1</td> <td>launch Maps</td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/mapTour/afterPicking.png" /></p> <h4 class="ai-header">How the blocks work</h4> <p>When the user chooses from the ListPicker, the AfterPicking event is triggered. Whatever item was chosen is in the <span class="argblock">ListPicker1.Selection</span> property. So if the user chose "Musee du Louvre", that value is in ListPicker1 's Selection property.</p> <p>In the Component Designer, you already setup the ActivityStarter componenent with properties so that it will launch the Maps application. Here, you just need to tell it which map to show. The <b style="color:green;">DataUri</b> property of ActivityStarter allows you to specify this using a special protocol.</p> <p>In this case, you want to show the map that would appear if you typed in "Musee du Louvre" in the search box of the Maps application. To do this, the DataUri should be set to:</p> <p style="color:grey;">geo:0,0?q='Musee du Louvre'</p> <p>The <span class="textblock">make a text</span> block creates a text exactly like this, only the text after "q=" will be the choice made by the user.</p> <p>Once the DataUri is set, <span class="callblock">ActivityStarter1.StartActivity</span> launches the Maps app.</p> <pre class="ai-testing"><b>Test this behavior.</b> Restart the app and click the "Choose Destinations" button again. When you choose one of the destinations, does a map of that destination appear? Can you click the back button to return to your app to choose again (you may have to click a couple of times to leave the Maps app).</pre><h4 class="ai-header">Map Tour, Final Program</h4> <p><img src="/sites/all/files/tutorials/mapTour/mapTourFinal.png" /></p> <h4 class="ai-header">Review</h4> <p>Here are some of the ideas covered in this tutorial:</p> <ul><li>The <b>ListPicker</b> component lets the user choose from a list of items. ListPicker' s <b style="color:green;">Elements</b> property holds the list, the <b style="color:green;">Selection</b> property holds the selected item, and the <b style="color:green;">AfterPicking</b> event is triggered when the user chooses.</li> <li>The <b>ActivityStarter</b> component allows your app to launch other apps. This tutorial demonstrated its use with the Maps application, but you can launch a browser or any other Android app as well-- even another one of your own.</li> <li>See the <a href="/content/using-activity-starter">reference page</a> on Activity Starter for more details.</li> <li>Learn more sophisticated uses of the ActivityStarter and the ListPicker components in <a href="/content/map-tour-part-2">MapTour Part 2</a>.</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/mapTour/MapTourBarcode.png" /></p> <p>Or <a href="http://explore.appinventor.mit.edu/sites/all/files/tutorials/mapTour/MapTour.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/mapTour/MapTour.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;">MapTour</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> </ul> </section> Fri, 14 Jun 2013 16:11:48 +0000 aaron 314 at http://dev-explore.appinventor.mit.edu http://dev-explore.appinventor.mit.edu/content/maptour#comments MakeQuiz and TakeQuiz http://dev-explore.appinventor.mit.edu/content/makequiz-and-takequiz <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;">MakeQuiz</span> and <span style="color:green;">TakeQuiz</span></h2> <h4 class="ai-header">What you're building</h4> <p><img src="/sites/all/files/tutorials/makeQuizTakeQuiz/MakeQuizEmulator.png" style="float:right; width: 200px;" /></p> <p><span style="color:green;">MakeQuiz</span> and <span style="color:green;">TakeQuiz</span> are two apps that, in tandem, allow a teacher to create quizzes for a student. Parents can create fun trivia apps for their children during a long road trip, grade school teachers can build "Math Blaster" quizzes, and college students can build quizzes to help their study groups prepare for a final. This tutorial will walk you through creating both the MakeQuiz and the TakeQuiz app.</p> <p>This tutorial is a followup of the <a href="http://explore.appinventor.mit.edu/content/quizme">QuizMe tutorial</a> -- if you haven't completed that tutorial you should do so 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 "MakeQuiz", and also set the screen's <b style="color:green;">Title</b> to "MakeQuiz". Open the Blocks Editor and connect to the phone.</p> <h4 class="ai-header">Introduction</h4> <p>You'll design two apps, MakeQuiz for the "teacher" and TakeQuiz for the "student". With MakeQuiz:</p> <p><a href="http://explore.appinventor.mit.edu/sites/explore.appinventor.mit.edu/files/MakeQuiz.zip"><img src="http://explore.appinventor.mit.edu/sites/explore.appinventor.mit.edu/files/downloadMakeQuizButton.PNG" style="float:right; width: 200px;border:none" /></a></p> <ul><li>The user enters questions and answers in an input form.</li> <li>The previously entered question-answer pairs are displayed.</li> <li>The quiz is stored persistently, in a database.</li> </ul><p>TakeQuiz will work similarly to the <a href="http://explore.appinventor.mit.edu/content/quizme">QuizMe</a> app-- in fact you'll build it using QuizMe as a basis. TakeQuiz will differ in that the questions asked will be those that were entered into the database using MakeQuiz.</p> <p>This tutorial introduces the following App Inventor concepts:</p> <ul><li>Input forms for allowing the user to enter information.</li> <li>Displaying lists: serializing a list variable to display it on separate lines.</li> <li>Persistent data: MakeQuiz will save the quiz questions and answers in a database and TakeQuiz will load them in from the database.</li> <li>Data Sharing: the two apps together illustrate how two phones (and even two apps) can communicate through a shared, web database:</li> </ul><p><img src="/sites/all/files/tutorials/makeQuizTakeQuiz/dbdiagram.png" /></p> <h4 class="ai-header">Set up the Components for MakeQuiz</h4> <p>Use the Component Designer to create the interface for MakeQuiz. 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/makeQuizTakeQuiz/makeQuizComponents.png" /></p> <p>You'll use the following components to create MakeQuiz. 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>HorizontalArrangement</b> </td> <td>Screen Arrangement</td> <td style="color:green;">HorizontalArrangement1</td> <td>Display the question prompt and textbox on one line</td> </tr><tr><td><b>Label</b></td> <td>Basic</td> <td style="color:green;">Label1</td> <td>The "Question:" prompt</td> </tr><tr><td><b>TextBox</b></td> <td>Basic</td> <td style="color:green;">QuestionText</td> <td>User enters questions here </td> </tr><tr><td><b>HorizontalArrangement</b></td> <td>Screen Arrangement</td> <td style="color:green;">HorizontalArrangement2</td> <td>Display the answer prompt and textbox on one line</td> </tr><tr><td><b>Label</b></td> <td>Basic</td> <td style="color:green;">Label2</td> <td>The "Answer:" prompt</td> </tr><tr><td><b>TextBox</b></td> <td>Basic</td> <td style="color:green;">AnswerText</td> <td>User enters answers here </td> </tr><tr><td><b>Button</b></td> <td>Basic</td> <td style="color:green;">SubmitButton</td> <td>User clicks to submit a QA pair.</td> </tr><tr><td><b>Label</b></td> <td>Basic</td> <td style="color:green;">QuestionsAnswersLabel</td> <td>This will display previously entered QAs</td> </tr><tr><td><b>TinyWebDB</b></td> <td>Other Stuff</td> <td style="color:green;">TinyWebDB1</td> <td>To store to and retrieve from database</td> </tr></table><p>Set the properties of the components in the following way:</p> <ul><li>Set the Text of Label1 to "Question": and the Text of Label2 to "Answer":</li> <li>Set the Hint of QuestionText to "enter a question" and the Hint of AnswerText to "enter an answer".</li> <li>Set the Text of SubmitButton to " Submit "</li> <li>Set the Text of QuestionsAnswersLabel to "Questions and Answers".</li> </ul><p>Put <span style="color:green;">Label1</span> and <span style="color:green;">QuestionText</span> in <span style="color:green;">HorizontalArrangement1</span>, and <span style="color:green;">Label2</span> and <span style="color:green;">AnswerText</span> in <span style="color:green;">HorizontalArrangement2</span>.</p> <h4 class="ai-header">Add behaviors to the components</h4> <p>Open the Blocks Editor to add the behaviors for the app. As with the original QuizMe app, you'll first define some <em>global variables</em> for the QuestionList and AnswerList , but this time you won't provide fixed questions and answers. 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> ("QuestionList") </td> <td>Definitions</td> <td>Defines the <b>QuestionList</b> variable (rename it)</td> </tr><tr><td><span class="setblock">def variable</span> ("AnswerList") </td> <td>Definitions</td> <td>Defines the <b>AnswerList</b> variable (rename it)</td> </tr><tr><td><span class="listblock">make a list</span></td> <td>Lists</td> <td>To set up the <b>QuestionList</b> for new items</td> </tr><tr><td><span class="listblock">make a list</span></td> <td>Lists</td> <td>To set up the <b>AnswerList</b> for new items</td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/makeQuizTakeQuiz/lists.png" /></p> <p>Note that, unlike the original QuizMe app, the lists are empty to begin with. This is because with MakeQuiz and TakeQuiz, all data is created by the user of the app (it is <em>user-generated</em>). </p> <h4 class="ai-header">Handle the User's entries</h4> <p>The first behavior you'll build is for handling the user's input. Specifically, when the user enters a question and answer and clicks submit, you'll use <span class="listblock">add item to list</span> blocks to update the QuestionList and AnswerList.</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">SubmitButton.Click </span></td> <td>SubmitButton</td> <td>This event is triggered when the user clicks this button.</td> </tr><tr><td><span class="listblock">add items to list</span> (2)</td> <td>Lists</td> <td>Use these to add the data entered by the user to the lists</td> </tr><tr><td><span class="argblock">global QuestionList</span></td> <td>My Definitions</td> <td>Plug into <b>list</b> slot of <u>first</u> <span class="listblock">add items to list</span></td> </tr><tr><td><span class="argblock">QuestionText.Text</span></td> <td>QuestionText</td> <td>User's entry; plug it into <b>item</b> slot of <u>first</u> <span class="listblock">add items to list</span></td> </tr><tr><td><span class="argblock">global AnswerList</span></td> <td>My Definitions</td> <td>Plug into <b>list</b> slot of <u>second</u> <span class="listblock">add items to list</span></td> </tr><tr><td><span class="argblock">AnswerText.Text</span></td> <td>AnswerText</td> <td>User's entry; plug it into <b>item</b> slot of <u>second</u> <span class="listblock">add items to list</span></td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/makeQuizTakeQuiz/submitNoDisplay.png" /></p> <h4 class="ai-header">How the blocks work</h4> <p><span class="listblock">add items to list</span> appends each item to the end of a list. Here, the app takes the text the user has entered in the QuestionText and AnswerText text boxes and appends each to the corresponding list.</p> <p>The behavior updates the hidden QuestionList and AnswerList variables, but the changes are not shown to the user. To display these lists, create a procedure "displayQAs" which makes a text object with both lists and places it into the QuestionsAnswersLabel. Then make sure to call the procedure at the bottom of <span class="basicblock">SubmitButton.Click</span>. 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> "displayQAs"</td> <td>Definitions</td> <td>Put the code to display questions and answers here</td> </tr><tr><td><span class="setblock">set QuestionsAnswersLabel.Text to</span></td> <td>QuestionsAnswersLabel</td> <td>Display the lists in this label.</td> </tr><tr><td><span class="textblock">make text</span></td> <td>Text</td> <td>Make a text object out of two lists and a colon separator</td> </tr><tr><td><span class="argblock">global QuestionList</span></td> <td>My Definitions</td> <td>Plug into <span class="textblock">make text</span></td> </tr><tr><td><span class="textblock">text</span> (":")</td> <td>Text</td> <td>Plug into <span class="textblock">make text</span></td> </tr><tr><td><span class="argblock">global AnswerList</span></td> <td>My Definitions</td> <td>Plug into <span class="textblock">make text</span></td> </tr><tr><td><span class="callblock">call displayQAs</span></td> <td>My Definitions</td> <td>Place in the bottom of the SubmitButton.Click event-handler</td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/makeQuizTakeQuiz/displayQAs1.png" /></p> <h4 class="ai-header">How the blocks work</h4> <p>The displayQAs procedure is called after the lists are modified in <span class="basicblock">SubmitButton.Click</span>. The QuestionsAnswersLabel is modified to display the two lists separated by a colon. By default, App Inventor displays lists with surrounding parenthesis and spaces between items:</p> <p style="color:grey;">(item1 item2 item3)</p> <p>Of course, this is not the ideal way to display the lists, but it will allow you to test your behavior for now. Later, you'll modify the displayQAs procedure to display a list on separate lines, and to display each question together with its corresponding answer.</p> <pre class="ai-testing"><b>Test this behavior.</b> On the phone, enter a question and answer and click Submit. The app should display the single entry in the QuestionList, a colon, and then the single entry in the AnswerList. Add a second question and answer to make sure the lists are being created correctly.</pre><h4 class="ai-header">Blanking Out the Question and Answer</h4> <p>When a user submits a question-answer pair, you should clear the QuestionText and AnswerText text boxes so they're ready for a new entry.</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">set QuestionText.Text to</span></td> <td>QuestionText</td> <td>To blank out question </td> </tr><tr><td><span class="setblock">set AnswerText.Text to</span></td> <td>AnswerText</td> <td>To blank out answer</td> </tr><tr><td><span class="textblock">text</span> (2 blank ones)</td> </tr></table><p>Here's how the blocks for the updated event handler should look:</p> <p><img src="/sites/all/files/tutorials/makeQuizTakeQuiz/submitWithBlank.png" /></p> <h4 class="ai-header">How the blocks work</h4> <p>When the user submits a new question and answer, they are added to their respective lists. At that point, the text in the QuestionText and AnswerText is "blanked out" with empty text blocks (you create an empty text block by clicking on the the block's text and pressing the delete button).</p> <pre class="ai-testing"><b>Test the behavior.</b> Add some questions and answers. When you submit them, the QuestionText and AnswerText should show only their hint (when a TextBox's Text property is empty it shows the hint until the user clicks it).</pre><h4 class="ai-header">Storing the questions and answers in a database</h4> <p>As is, the app records the questions and answers entered by the user, but only in the phone's temporary memory. All variables in App Inventor are transient data-- they are stored on the phone and the data only "lives" through one run of an app. When the user closes the app, the data is lost.</p> <p>The <span style="color:green;">MakeQuiz</span> app needs to record the questions and answers in a database. This will allow the quiz creator (teacher) to always edit the latest update of the quiz. It will also allow the <span style="color:green;">TakeQuiz</span> app to load the quiz and administer it to the student.</p> <p>You'll use the <b>TinyWebDB</b> component to store and retrieve the QuestionList and AnswerList to and from a database. TinyWebDB allows you to store data in AppInventor-compliant databases that live on the web instead of on the phone.</p> <p>The general scheme for storing a list persistently is the following: each time a new item is added to the list, use TinyWebDB to update the database version of the list. Then each time the app is opened (<span class="callblock">Screen1.Initialize</span> event), reload the database version of the list into the variable.</p> <p>Start by storing the QuestionList and AnswerList in the database each time the user enters a new pair. 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">TinyWebDB1.StoreValue</span></td> <td>TinyWebDB1</td> <td>for storing questions in the database </td> </tr><tr><td><span class="textblock">text</span> ("questions")</td> <td>Text</td> <td>Plug in "questions" as the tag of <span class="callblock">StoreValue</span></td> </tr><tr><td><span class="argblock">global QuestionList</span></td> <td>My Definitions</td> <td>Plug into the value slot of <span class="callblock">StoreValue</span></td> </tr><tr><td><span class="callblock">TinyWebDB1.StoreValue</span></td> <td>TinyWebDB1</td> <td>For storing answers in the database</td> </tr><tr><td><span class="textblock">text</span> ("answers")</td> <td>Text</td> <td>Plug in "answers" as the tag of <span class="callblock">StoreValue</span></td> </tr><tr><td><span class="argblock">global AnswerList</span></td> <td>My Definitions</td> <td>Plug into the value slot of <span class="callblock">StoreValue</span></td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/makeQuizTakeQuiz/submitWithStore.png" /></p> <h4 class="ai-header">How the blocks work</h4> <p>The last two rows of blocks store data in the database. The tag arguments label the data being stored so that you can retrieve it later. The QuestionList is stored with a tag of "questions" while the AnswerList is stored with a tag of "answers".</p> <p>Although our example uses "questions" as the tag, you should actually use your own tag, e.g., "DavesQuestions", because by default, the database entries of all App Inventor programs share the same namespace and so your list of questions may get confused with someone else's list of questions since you both used the same tag. For more information, see <a href="/content/custom-tinywebdb-service">Creating a Custom TinyWebDB service</a>.</p> <pre class="ai-testing"><b>Test this behavior.</b> TinyWebDB stores information on the web, so you can open a browser page to test whether this behavior is working. By default, TinyWebDB stores information at http://appinvtinywebdb.appspot.com . Browse there to check if your data was stored as desired. There will be many entries, as the default service is for testing and is used by many programmers. If you search on the page, you should find the tag-value pairs you just stored. Because the default service is shared amongst programmers and apps, it is only for testing. Fortunately, setting up your own database service is straight forward. For more information, see <a href="/content/custom-tinywebdb-service">Creating a Custom TinyWebDB service</a>.</pre><h4 class="ai-header">Loading Data from the Database</h4> <p>Once the blocks for storing the lists are working, you can add the blocks for loading the lists back in each time the app begins.</p> <p>Loading data from a TinyWebDB database is a two-step process. First, you request the data by calling <span class="callblock">TinyWebDB.GetValue</span> and providing a tag.</p> <p><span class="callblock">GetValue</span> only requests the data from the web service. After the request is made, your app can do other things, such as responding to user actions, while it waits for the TinyWebDB web service to send the data. When the data arrives, a <span class="basicblock">TinyWebDB.GotValue</span> event is triggered. When that event occurs, the data requested is in a variable named "valueFromWebDB". The tag you requested is in the variable "tagFromWebDB". You can access these variables in the <span class="basicblock">TinyWebDB.GotValue</span> event handler.</p> <p>In this case, the app needs to request two things from the TinyWebDB web service, the questions and the answers, so the <span class="basicblock">Screen1.Initialize</span> will make two calls to <span class="callblock">getValue</span>. When the data arrives and the <span class="basicblock">GotValue</span> event-handler is triggered, the app should check the tag to see which request has arrived, and then load the corresponding list. Once the lists are both loaded, <span class="callblock">displayQAs</span> can be called to display them. </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">Screen1.Initialize</span></td> <td>Screen1</td> <td>Event handler triggered when app begins </td> </tr><tr><td><span class="callblock">TinyWebDB.GetValue</span> (2)</td> <td>TinyWebDB</td> <td>To request the stored QuestionList and AnswerList</td> </tr><tr><td><span class="textblock">text</span> ("questions")</td> <td>Text</td> <td>use as the tag to retrieve QuestionList</td> </tr><tr><td><span class="textblock">text</span> ("answers")</td> <td>Text</td> <td>use as the tag to retrieve AnswerList</td> </tr><tr><td><span class="basicblock">TinyWebDB.GotValue</span></td> <td>TinyWebDB1</td> <td>Event handler triggered when data arrives</td> </tr><tr><td><span class="controlblock">ifelse</span></td> <td>Control</td> <td>Need to ask which GetValue request arrived</td> </tr><tr><td><span class="basicblock">=</span> block</td> <td>Math</td> <td>Compare tagFromWebDB to "questions"</td> </tr><tr><td><span class="textblock">text</span> ("questions")</td> <td>Text</td> <td>This is the tag that was used to store QuestionList</td> </tr><tr><td><span class="argblock">value TagFromWebDB</span></td> <td>My Definitions</td> <td>An argument of GotValue, specifies which request</td> </tr><tr><td><span class="setblock">set QuestionList to</span></td> <td>My Definitions</td> <td>If TagFromWebDB is "questions" this list will be set</td> </tr><tr><td><span class="setblock">set AnswerList to</span></td> <td>My Definitions</td> <td>If TagFromWebDB is not "questions" this list will be set</td> </tr><tr><td><span class="argblock">value ValueFromWebDB</span> (2)</td> <td>My Definitions</td> <td>This holds the value returned from database</td> </tr><tr><td><span class="controlblock">if</span></td> <td>Control</td> <td>Check if both the lists are loaded before displaying</td> </tr><tr><td><span class="basicblock">=</span> block</td> <td>Math</td> <td>Compare the lengths of the lists</td> </tr><tr><td><span class="listblock">length of list</span> (2)</td> <td>Lists</td> <td>Check if the length of the lists are the same</td> </tr><tr><td><span class="argblock">global QuestionList</span></td> <td>My Definitions</td> <td>Plug into one of the <span class="listblock">length of list</span> blocks</td> </tr><tr><td><span class="argblock">global AnswerList</span></td> <td>My Definitions</td> <td>Plug into the other <span class="listblock">length of list</span> blocks</td> </tr><tr><td><span class="callblock">call displayQAs</span></td> <td>My Definitions</td> <td>Display the newly loaded questions and answers</td> </tr></table><p>The blocks should look like:</p> <p><img src="/sites/all/files/tutorials/makeQuizTakeQuiz/gotValue.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 <span class="callblock">TinyWebDB1.GetValue</span> twice, once to request the stored QuestionList and once to request the stored AnswerList. After requesting the data, the app can handle other events while it waits for the web database to answer the request. </p><p>When the data arrives from the web database, the <span class="basicblock">TinyWebDB1.GotValue</span> event is triggered. Since two requests were made, it will be triggered twice, once for each list.</p> <p>In the <span class="basicblock">GotValue</span> event-handler, the value returned is in the variable valueFromWebDB . The corresponding tag is in tagFromWebDB . The blocks first check to see which request has returned (even though the request for "questions" is made first in <span class="basicblock">Screen1.Initialize</span>, there is no guarantee that the QuestionList will arrive first). If the tag is "questions", the valueFromWebDB is put into the variable QuestionList. Otherwise (else) it is placed in the AnswerList.</p> <p>At the bottom of <span class="basicblock">GotValue</span>, the app checks to see if both lists have been loaded-- one way to check is to see if the lengths of the lists are the same. If they are, displayQAs is called to display them on the phone.</p> <pre class="ai-testing"><b>Test this behavior.</b> Select Restart Phone in the Blocks Editor. When the app initializes, it should display the previously entered questions and answers. If you close the app and restart again, the previous quiz should still appear.</pre><p>The version of <span style="color:green;">MakeQuiz</span> you've created so far works: the user can create a quiz and it will be stored persistently in the database. If you can accept the fact that the quiz is displayed in an inelegant way, you can proceed to creating the sister app, <span style="color:green;">TakeQuiz</span>, which lets a user step through the quiz and answer the questions.</p> <p>If you'd like to learn how to display list data in a more elegant fashion, step through the steps directly below.</p> <h4 class="ai-header">Displaying a List on Multiple Lines</h4> <p>In the app you've built so far, the question and answer lists are displayed separately and with the default list display format, e.g.,</p> <p style="color:grey;">(question1 question2 question3): (answer1 answer2 answer3)</p> <p>In this section, you'll modify the app so that the newly entered question-answer pairs are displayed in tandem, one on each line:</p> <p style="color:grey;">question1:answer1<br /> question2:answer2<br /> question3:answer3</p> <p>Displaying the data in this way is non-trivial, so you'll start by first displaying a single list, the QuestionList, with each item on a separate line.</p> <pre class="ai-box">To display a list on separate lines, you must serialize it . This means to build a single text object with all the items of the list in it and a special character, '\n', between each item. A "\n" within a text object appears as a newline character when displayed. For example, suppose the user has entered two questions, "What is the capital of California?" and "What is the capital of New York?" and these have succesfully been added to the QuestionList . Your displayQAs procedure should build a text object from QuestionList that looks like: <span style="color:black;">"\nWhat is the capital of California\nWhat is the capital of New York?"</span> When displayed on the phone, such text would appear as: <span style="color:black;">What is the capital of California? What is the capital of New York?</span></pre><p>All the changes will occur in the procedure <span class="callblock">displayQAs</span>. You'll remove the blocks within that procedure, and use a <span class="controlblock">foreach</span> block to build the text by successively adding each question. The blocks within the foreach should add the current item (question) to the end of the text object (QuestionsAnswersLabel) you are building. 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 QuestionsAnswersLabel.Text to</span></td> <td>QuestionsAnswersLabel</td> <td>Initialize the label to empty before repeatedly adding to it</td> </tr><tr><td><span class="textblock">text</span> (blank)</td> <td>Text</td> <td>plug into <span class="setblock">set QuestionsAnswersLabel.Text to</span></td> </tr><tr><td><span class="controlblock">foreach</span></td> <td>Control</td> <td>For each item in list, you'll concatenate it to the QuestionsAnswersLabel</td> </tr><tr><td><span class="argblock">name var</span></td> <td>already in foreach</td> <td>The current item of <span class="controlblock">foreach</span>; rename it to "question"</td> </tr><tr><td><span class="argblock">global QuestionList</span></td> <td>My Definitions</td> <td>Plug this into "in list" slot of <span class="controlblock">foreach</span></td> </tr><tr><td><span class="setblock">set QuestionsAnswersLabel.Text to</span></td> <td>QuestionsAnswersLabel</td> <td>Iteratively build the text</td> </tr><tr><td><span class="textblock">make text</span></td> <td>Text</td> <td>build a text object</td> </tr><tr><td><span class="argblock">QuestionsAnswersLabel.Text</span></td> <td>QuestionsAnswersLabel</td> <td>Plug into make text</td> </tr><tr><td><span class="argblock">value question</span></td> <td>My Definitions</td> <td>Plug into make text (make sure you've renamed foreach variable to "question")</td> </tr><tr><td><span class="textblock">text</span> ("/n")</td> <td>Text</td> <td>New-line character so following text on another line</td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/makeQuizTakeQuiz/displayQA1.png" /></p> <h4 class="ai-header">How the blocks work</h4> <p>When the procedure is called, the <span class="setblock">QuestionsAnswersLabel.Text</span> is first set to the empty text. This is in preparation for the <span class="controlblock">foreach</span> block that comes next, which will build the text in <span class="setblock">QuestionsAnswersLabel.Text</span> incrementally.</p> <p>The <span class="controlblock">foreach</span> block specifies that the blocks within it are to be executed once for every item in QuestionList. If there are three questions, then the inner blocks will be executed three times. Within the foreach, the current item being processed is named question.</p> <p>On each iteration, <span class="argblock">QuestionsAnswersLabel.Text</span> is modified. Each time it is set to its previous value, along with the newline character, "\n" and the current question being processed.</p> <p>When the <span class="controlblock">foreach</span> completes, <span class="argblock">QuestionsAnswersLabel.Text</span> will be text string with all the items separated by newline characters. As the app is processing each iteration of the foreach , the phone display doesn't change. But when it completes, the text in <span class="argblock">QuestionsAnswersLabel.Text</span> is displayed on the phone and the items appear on separate lines.</p> <pre class="ai-testing"><b>Test the behavior.</b> On the phone, add another question and answer and click Submit. Now, only the questions should be displayed and they should be displayed on separate lines.</pre><h4 class="ai-header">Including the answer in DisplayQAs</h4> <p>The displayQAs procedure you've created ignores answers and only displays questions. In the next version of displayQAs , you'll display the data as question-answer pairs, e.g., </p> <p style="color:grey;">What is the capital of California?: Sacramento<br /> What is the capital of New York?: Albany </p> <p>Modify displayQAs. Use an index variable to access each answer as the foreach block walks through the questions. 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 var</span> ("answer")</td> <td>Definitions</td> <td>To temporarily store each answer</td> </tr><tr><td><span class="setblock">def var</span> ("answerIndex")</td> <td>Definitions</td> <td>To keep track of which answer (and question) you're on</td> </tr><tr><td><span class="textblock">text</span> ("text")</td> <td>Text</td> <td>Initialize the variable answer to text.</td> </tr><tr><td><span class="basicblock">number</span> (1)</td> <td>Math</td> <td>Initialize the variable answerIndex to 1</td> </tr><tr><td><span class="setblock">set answerIndex to</span></td> <td>My Definitions</td> <td>Re-initialize answerIndex each time displayQAs called</td> </tr><tr><td><span class="basicblock">number</span> (1)</td> <td>Math</td> <td>To re-initialize answerIndex to 1</td> </tr><tr><td><span class="setblock">set answer to</span></td> <td>My Definitions</td> <td>set this variable each time in the <span class="controlblock">foreach</span></td> </tr><tr><td><span class="listblock">select list item</span></td> <td>Lists</td> <td>select from the list AnswerList</td> </tr><tr><td><span class="argblock">global AnswerList</span></td> <td>My Definitions</td> <td>plug into list slot of <span class="listblock">select list item</span></td> </tr><tr><td><span class="argblock">global answerIndex</span></td> <td>My Definitions</td> <td>Plug into index slot of <span class="listblock">select list item</span></td> </tr><tr><td><span class="listblock">set answerIndex to</span></td> <td>My Definitions</td> <td>In order to increment it each iteration through loop</td> </tr><tr><td><span class="argblock">global answerIndex</span></td> <td>My Definitions</td> <td>To increment answerIndex , add 1 to itself.</td> </tr><tr><td><span class="basicblock">number</span> (1)</td> <td>Math</td> <td>To increment answerIndex</td> </tr></table><p>The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/makeQuizTakeQuiz/displayQAsWithAnswers.png" /></p> <h4 class="ai-header">How the blocks work</h4> <p>The <span class="controlblock">foreach</span> only allows you to iterate through one list. In this case, there are two lists and you need to step through and select each answer as you proceed through the questions. The strategy is to use an index variable, as was done with the currentQuestionIndex in the QuizMe tutorial.</p> <p>For this behavior, the index is a position in the AnswerList, so its named answerIndex. The index is set to 1 before the <span class="controlblock">foreach</span> begins. Within the foreach, it is used to select the current answer from the AnswerList, and then it is incremented.</p> <pre class="ai-testing"><b>Test this behavior.</b> On the phone, add some more question/answer pairs. The display should now show each question with its corresponding answer, and each question:answer pair on separate lines.</pre><h4 class="ai-header">Final MakeQuiz App</h4> <p><img src="/sites/all/files/tutorials/makeQuizTakeQuiz/makeQuizFinal.png" /></p> <h4 class="ai-header">Scan the Sample App to your Phone</h4> <p>Scan the following barcode onto your phone to install and run MakeQuiz. </p><p><img src="/sites/all/files/tutorials/makeQuizTakeQuiz/MakeQuizBarcode.png" /></p> <p>Or <a href="http://explore.appinventor.mit.edu/sites/all/files/tutorials/makeQuizTakeQuiz/MakeQuiz.apk">download the source code</a></p> <p>.</p> <h4 class="ai-header">TakeQuiz</h4> <p><a href="http://explore.appinventor.mit.edu/sites/explore.appinventor.mit.edu/files/TakeQuiz.zip"><img src="http://explore.appinventor.mit.edu/sites/explore.appinventor.mit.edu/files/downloadTakeQuizButton.PNG" style="float:right; width: 200px;" /></a></p> <p>Building TakeQuiz is simpler; it can be built with a few modifications to the <a href="http://explore.appinventor.mit.edu/content/quizme">QuizMe</a> app you completed earlier (if you have not completed that tutorial, do so now before continuing).</p> <p>Begin by opening your QuizMe app, choosing SaveAs, and naming the new project "TakeQuiz." This will leave your QuizMe app as is and allow you to use a copy of it as the basis for TakeQuiz. </p> <p>This version of MakeQuiz/TakeQuiz does not handle images, so first remove the images from the TakeQuiz app: </p> <ol><li>In the Component Designer, choose each image from the media palette and delete it. Also delete the Image1 component, which will remove all references to it from the Blocks Editor.</li> <li>In the Blocks Editor, drag the PictureList to the trash.</li> </ol><p>Since TakeQuiz will work with database data, drag a TinyWebDB component into the Component Designer viewer.</p> <p>Now modify the blocks so that the quiz given to the user is the one from the database. First, since there are no fixed questions and answers, remove all of the actual question and answer text blocks from the make a list blocks within the QuestionList and AnswerList. The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/makeQuizTakeQuiz/emptylists.png" /></p> <p>Now modify your <span class="basicblock">Screen1.Initialize</span> so that it calls <span class="spanblock">TinyWebDB.GetValue</span> twice to load the lists. The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/makeQuizTakeQuiz/screenInit.png" /></p> <p>Finally, drag out a <span class="basicblock">TinyWebDB.GotValue</span> event-handler. This event-handler should look similar to the one used in MakeQuiz, but here you only want to show the first question and none of the answers. The blocks should look like this:</p> <p><img src="/sites/all/files/tutorials/makeQuizTakeQuiz/TakeQuizGotValue.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>When the app begins, <span class="basicblock">Screen1.Initialize</span> is triggered and the app requests the questions and the answers from the web database. When each of those requests arrives, the <span class="basic">TinyWebDB.GotValue</span> event-handler is triggered. The app asks which request has come in, using tagFromWebDB , and places the valueFromWebDB into the appropriate list. If it is the QuestionList being loaded, the first question is selected from QuestionList and displayed.</p> <pre class="ai-testing"><b>Test the behavior.</b> Click <b>Restart Phone App</b>. Does the first question from the quiz you created with MakeQuiz appear? Can you take the quiz just as you did with QuizMe (except for the pictures)?</pre><h4 class="ai-header">Final Program (Take Quiz)</h4> <p><img src="/sites/all/files/tutorials/makeQuizTakeQuiz/takequizfinal.png" /></p> <h4 class="ai-header">Scan the Sample App to your Phone</h4> <p>Scan the following barcode onto your phone to install and run Take Quiz. </p><p><img src="/sites/all/files/tutorials/makeQuizTakeQuiz/TakeQuizBarcode.png" /></p> <p>Or <a href="http://explore.appinventor.mit.edu/sites/all/files/tutorials/makeQuizTakeQuiz/TakeQuiz.apk">download the source code</a>.</p> <h4 class="ai-header">Variations</h4> <p>Once you get MakeQuiz and TakeQuiz working, you might want to explore some variations. For example,</p> <ul><li>Incorporate images into the apps. The images will need to be URLs from the web, and the test creator will need to specify these URLs as a third item in the MakeQuiz form.</li> <li>Allow the user to delete items from the questions and answers. You can let the user choose using the ListPicker component, and you can remove an item with the remove list item</li> <li>Allow multiple quizzes to be created. You'll need to conceptualize the list of quizzes as a list of lists.</li> </ul><h4 class="ai-header">Review</h4> <p>Here are some of the ideas covered in this tutorial: </p> <ul><li>You can store data persistently in a web database with the <b>TinyWebDB</b> component.</li> <li>You retrieve data from a TinyWebDB database by requesting it with GetValue . When the web database returns the data, the <span class="basicblock">TinyWebDB.GotValue</span> event is triggered. At that point, you can put the data in a list or process it in some way.</li> <li>TinyWebDB data can be shared amongst multiple phones and apps.</li> </ul><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;">MakeQuiz</span> and <span style="color:black;">TakeQuiz</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/game" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Game</a> </li> </ul> </section> Fri, 14 Jun 2013 16:11:21 +0000 aaron 313 at http://dev-explore.appinventor.mit.edu http://dev-explore.appinventor.mit.edu/content/makequiz-and-takequiz#comments QuizMe http://dev-explore.appinventor.mit.edu/content/quizme <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[/* ><!--*/ .ButtonText { color:green; font-weight: bold; } 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;">QuizMe</span></h2> <h4 class="ai-header">What you're building</h4> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/quizOnEmulator.png" style="float:right; height: 453px; width: 197px;" /></p> <p><a href="http://cs.usfca.edu/~wolber/appinventor/bookSplits/ch8Quiz.pdf">Download Alternate Example Version (Book Chapter PDF)</a></p> <p><span style="color:green;">QuizMe</span> is a trivia game about baseball, but you can use it as a template to build quizzes on any topic. With <span style="color:green;">QuizMe</span>:</p> <ul><li>The user steps through a series of questions, clicking a button to proceed to the next question.</li> <li>The user enters an answer for each question and the app reports whether each answer is correct or not.</li> </ul><p>With <span style="color:green;">QuizMe</span>, the quiz questions are always the same unless you, the programmer, change them. Later, you can create <a href="/content/makequiz-and-takequiz">MakeQuiz &amp; TakeQuiz</a>, an app that lets users of the app create and modify the quiz questions.</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> <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;">QuizMe</span>, and also set the screen's <strong style="color:green;">Title</strong> to "QuizMe". Open the Blocks Editor and connect to the phone.</p> <p>Also download the following pictures of baseball players and save them on your computer. Later, you'll load these images into your project.</p> <ul><li><a href="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/Larsenberra.jpg">Larsenberra.jpg</a></li> <li><a href="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/dallasbraden.jpg">Dallasbraden.jpg</a></li> <li><a href="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/cyyoung.jpg">Cyyoung.jpg</a></li> <li><a href="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/nolanryan.jpg">Nolanryan.jpg</a></li> </ul><h4 class="ai-header">Introduction</h4> <p>You'll design the quiz game so that the user proceeds from question to question by clicking a Next button, and receives simple correct/incorrect feedback on each answer.</p> <p>This tutorial introduces:</p> <ul><li>Defining and displaying lists of information.</li> <li>Sequencing through a list using an index variable -- a variable that keeps track of a position in a list.</li> <li>Conditional behaviors-- performing certain operations only when a condition is met.</li> <li>Switching an image to show a different picture at different times.</li> </ul><h4 class="ai-header">Set up the Components</h4> <p>Use the component designer to create the interface for <span style="color:green;">QuizMe</span>. When you finish, it should look something like the snapshot below (there are also more detailed instructions below the snapshot).</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/designer.png" /></p> <p>To create this interface, first load the images you downloaded into the project. Click on the Add button in the Media area and select one of the downloaded files (e.g., Larsenberra.jpg). Then do the same for the other three images.</p> <p>Next, create the following components by dragging them from the Palette into the Viewer.</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> <strong>Image</strong> </td> <td>Basic</td> <td style="color:green;">Image1</td> <td>The picture part of the question</td> </tr><tr><td><strong>Label</strong></td> <td>Basic</td> <td style="color:green;">QuestionLabel</td> <td>Displays the current question</td> </tr><tr><td><strong>HorizontalArrangement</strong></td> <td>Screen Arrangement</td> <td style="color:green;">HorizontalArrangement1</td> <td>Organizes the AnswerPrompt and Text</td> </tr><tr><td><strong>Label</strong></td> <td>Basic</td> <td style="color:green;">AnswerPromptLabel</td> <td>Text prompting for an anwer</td> </tr><tr><td><strong>TextBox</strong></td> <td>Basic</td> <td style="color:green;">AnswerText</td> <td>User will enter answer here.</td> </tr><tr><td><strong>Label</strong></td> <td>Basic</td> <td style="color:green;">RightWrongLabel</td> <td>Correct/Incorrect is displayed here.</td> </tr><tr><td><strong>HorizontalArrangement</strong></td> <td>Screen Arrangement</td> <td style="color:green;">HorizontalArrangement2</td> <td>Organizes the AnswerButton and NextButton</td> </tr><tr><td><strong>Button</strong></td> <td>Basic</td> <td style="color:green;">AnswerButton</td> <td>User clicks to submit an answer</td> </tr><tr><td><strong>Button</strong></td> <td>Basic</td> <td style="color:green;">NextButton</td> <td>User clicks to proceed to the next answer</td> </tr></table><p>Set the properties of the components as described below:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td><strong>Component</strong></td> <td><strong>Action</strong></td> </tr><tr><td> <span style="color:green;">Image1</span> </td> <td>Set its <span class="ButtonText">Picture</span> property to "Larsenberra.jpg". This is the first picture that should appear.</td> </tr><tr><td><span style="color:green;">QuestionLabel</span> </td> <td> Change <span class="ButtonText">Text</span> property to "question" </td> </tr><tr><td><span style="color:green;">AnswerPromptLabel</span> </td> <td>Change <span class="ButtonText">Text</span> property to "Enter Answer:". On the Viewer screen, move this label into <span style="color:green;">HorizontalArrangement1</span>. </td> </tr><tr><td><span style="color:green;">AnswerText</span> </td> <td>Change <span class="ButtonText">Hint</span> to "please enter an answer". On the Viewer screen, move <span style="color:green;">AnswerText</span> into <span style="color:green;">HorizontalArrangement1</span>. </td> </tr><tr><td><span style="color:green;">AnswerButton</span> </td> <td>Change <span class="ButtonText">Text</span> property to "Submit". On the Viewer, move the button into <span style="color:green;">HorizontalArrangment2.</span> </td> </tr><tr><td> <span style="color:green;">NextButton</span> </td> <td>Change <span class="ButtonText">Text</span> property to "Next". Move the button into <span style="color:green;">HorizontalArrangement2</span>. </td> </tr><tr><td><span style="color:green;">RightWrongLabel</span></td> <td> Change <span class="ButtonText">Text</span> property to "correct/incorrect". </td> </tr></table><h4 class="ai-header">Add Behaviors to the Components</h4> <p>Open the Blocks Editor to add the behaviors for the app. First, you'll define two list variables, <span style="color:green;">QuestionList</span> to hold the list of questions, and <span style="color:green;">AnswerList</span> to hold the list of corresponding answers.</p> <p>To define the two list variables, 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></td> <td>Definitions</td> <td>Defines the <span style="color:green;">QuestionList</span> variable (rename it)</td> </tr><tr><td><span class="setblock">def variable</span></td> <td>Definitions</td> <td>Defines the <span style="color:green;">AnswerList</span> variable (rename it)</td> </tr><tr><td><span class="listblock">make a list</span></td> <td>Lists</td> <td>Used to insert the items of the <span style="color:green;">QuestionList</span></td> </tr><tr><td><span class="textblock">text</span> (3 of them)</td> <td>Text</td> <td>The actual questions</td> </tr><tr><td><span class="listblock">make a list</span></td> <td>Lists</td> <td>Used to insert the items of the <span style="color:green;">AnswerList</span></td> </tr><tr><td><span class="textblock">text</span> (3 of them)</td> <td>Text</td> <td>The actual answers</td> </tr></table><p>You create global variables by dragging in a <span class="setblock">def variable</span> block from the Definitions drawer and double-clicking the default name "variable" to change its name. The <span class="setblock">def variable</span> block has a slot for the initial value of the variable. The variable can represent a number or text, or even a list, for which you can plug in a <span class="listblock">make a list</span> block into the variable definition.</p> <p>The blocks should look like this:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/questionlist.png" /></p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/AnswerList.png" /></p> <h4 class="ai-header">Define the Hidden Index Variable</h4> <p>Each time the user clicks the <span style="color:green;">NextButton</span> to proceed through the quiz, the app needs to remember which question it is on. In programming, to remember something, you define a new variable. In this case, the app needs to remember the current question number -- the index into the list <span style="color:green;">QuestionList</span>.</p> <p>To create the variable <span style="color:green;">currentQuestionIndex</span>, 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></td> <td>Definitions</td> <td>Defines the <span style="color:green;">currentQuestionIndex</span> variable (rename it)</td> </tr><tr><td><span class="basicblock">number</span> (1)</td> <td>Math</td> <td>Set the initial value of <span style="color:green;">currentQuestionIndex</span> to 1</td> </tr></table><p>The blocks should look like this:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/index.png" /></p> <h4 class="ai-header">Display the first question</h4> <p>To start, you'll ignore the answers and just work on the behavior to sequence through the questions. The desired behavior is the following: when the app starts, the first question should appear in the label named <span style="color:green;">QuestionLabel</span>. When the user clicks the <span style="color:green;">NextButton</span>, the second question should appear. When the user clicks again, the third should appear. When the last question is reached, clicking the <span style="color:green;">NextButton</span> should result in the first question once again appearing in the <span color="color:green;">QuestionLabel</span>.</p> <pre class="ai-box">With App Inventor, you select particular items in a list with the <span class="listblock">select list item</span> block. The block asks you to specify the list and an index--a position in the list. If a list has three items, the indexes 1, 2, and 3 are valid.</pre><p>For <span style="color:green;">QuizMe</span>, when the app starts, the app should choose the first question in the list and display it in the <span style="color:green;">QuestionLabel</span> component.</p> <p>For this app initialization behavior, 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">Screen1.Initialize</span></td> <td>Screen1</td> <td>When the app begins, this event-handler is triggered.</td> </tr><tr><td><span class="setblock">set QuestionLabel.Text to</span></td> <td>QuestionLabel</td> <td>Need to put the first question in <span style="color:green;">QuestionLabel</span></td> </tr><tr><td><span class="listblock">select list item</span></td> <td>Lists</td> <td>Need to select the first question from <span style="color:green;">QuestionLabel</span></td> </tr><tr><td><span class="argblock">global QuestionList</span></td> <td>My Definitions</td> <td>The list to select from</td> </tr><tr><td><span class="basicblock">number</span> (1)</td> <td>Math</td> <td>Select the first question by using an index of 1</td> </tr></table><p>The blocks should look like this:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/screenInit.png" /></p> <h4 class="ai-header">How the blocks work</h4> <p>The <span class="basicblock">Screen1.Initialize</span> event is triggered when the app begins. The first item of the variable <span style="color:green;">QuestionList</span> is selected and placed into <span class="basicblock">QuestionLabel.Text</span>. So when the app begins, the user will see the first question.</p> <pre class="ai-testing"><strong style="color:black;">Test this behavior.</strong> Click <span style="color:black;">Restart Phone App</span> (or <span style="color:black;">Connect Phone</span> if not connected). What appears on the phone? If you created the <span style="color:black;">QuestionList</span> as described above, the first item of <span style="color:black;">QuestionList</span>, "Who pitched a perfect game in the World Series?", should appear in the <span style="color:black;">QuestionLabel</span>.</pre><h4 class="ai-header">Iterating Through the Questions</h4> <p>Now program the behavior of the <span style="color:green;">NextButton</span>. You've already defined the <span class="listblock">currentQuestionIndex</span> to remember the question the user is on. When <span style="color:green;">NextButton</span> is clicked, the app needs to increment this variable, e.g., change it from 1 to 2 or from 2 to 3, etc., and then use the resulting value to select the new "current" question. For this behavior, 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">NextButton.Click</span></td> <td>NextButton</td> <td>When user clicks Next, this event-handler is triggered.</td> </tr><tr><td><span class="setblock">set currentQuestionIndex to</span></td> <td>My Definitions</td> <td>Need to put the first question in <span style="color:green;">QuestionLabel</span></td> </tr><tr><td><span class="basicblock">+</span> block</td> <td>Math</td> <td>Used to increment <span style="color:green;">currentQuestionIndex</span></td> </tr><tr><td><span class="argblock">global currentQuestionIndex</span></td> <td>My Definitions</td> <td>New value will be old value + 1</td> </tr><tr><td><span class="basicblock">number</span> (1)</td> <td>Math</td> <td>For the + 1</td> </tr><tr><td><span class="setblock">set QuestionLabel.Text to</span></td> <td>QuestionLabel</td> <td>Need to display the next question here</td> </tr><tr><td><span class="listblock">select list item</span></td> <td>Lists</td> <td>Need to select the first question from <span style="color:green;">QuestionList</span></td> </tr><tr><td><span class="argblock">global QuestionList</span></td> <td>My Definitions</td> <td>Plug into <span style="color:green;">list</span> slot of call select list item</td> </tr><tr><td><span class="argblock">global currentQuestionIndex</span></td> <td>My Definitions</td> <td>Plug into <span style="color:green;">index</span> slot of call select list item, we want nth item</td> </tr></table><p>The blocks should look like this:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/firstNext.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>The first row of blocks increments the variable <span style="color:green;">currentQuestionIndex</span>. If <span style="color:green;">currentQuestionIndex</span> has a 1 in it, it is changed to 2. If it has a 2, it is changed to 3, and so on. Once the <span style="color:green;">currentQuestionIndex</span> variable has been changed, the app uses it to select the "current" question.</p> <p>Recall that in the <span class="basicblock">Screen.Initialize</span> event-handler, the app selected the first question to display:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/selectFirst.png" /></p> <p>When the <span style="color:green;">NextButton</span> is clicked, the app doesn't choose the first item in the list, or the 2nd or 3rd, it chooses the <span class="listblock">currentQuestionIndex</span>-th item.</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/chooseCurrent.png" /></p> <p>The blocks are executed in a right-to-left manner. The app first evaluates the index parameter of <span class="listblock">select list item</span>, which is the variable <span style="color:green;">currentQuestionIndex</span>. The number is stored in <span style="color:green;">currentQuestionIndex</span> is used as the index when the select list item is executed.</p> <p>When the <span style="color:green;">NextButton</span> is clicked for the first time, the increment blocks will set <span style="color:green;">currentQuestionIndex</span> from 1 to 2, so the app will select the second item from <span style="color:green;">QuestionList</span>, "who pitched the first perfect game of 2010?". The second time <span style="color:green;">NextButton</span> is clicked, <span style="color:green;">currentQuestionIndex</span> will be set from 2 to 3, and the app will select the 3rd question in the list, "who pitched the first perfect game of the modern era?"</p> <pre class="ai-testing"><strong style="color:black;">Test this behavior.</strong> Test the behavior of the <span style="color:black;">NextButton</span> to see if the app is working correctly thus far. To test, play the role of the user and click the <span style="color:black;">NextButton</span> on the phone. Does the phone display the second question, "Who pitched the first perfect game of 2010?" It should, and the third question should appear when you click the <span style="color:black;">NextButton</span> again. If this is working, pat yourself on the back quickly, and then go on. Try clicking the <span style="color:black;">NextButton</span> again (a third time). You should see an error: "Attempting to get item 4 of a list of length 3". The app has a bug-- do you know what the problem is?</pre><p>The problem with the app is that it always increments the <span style="color:green;">currentQuestionIndex</span> variable when the <span style="color:green;">NextButton</span> is clicked. When <span style="color:green;">currentQuestionIndex</span> is already 3 and the user clicks the <span style="color:green;">NextButton</span>, the app changes <span style="color:green;">currentQuestionIndex</span> from 3 to 4, then calls select list item to get the <span style="color:green;">currentQuestionIndex</span>-th , or in this case, the 4th item. Since there are only three items in the variable <span style="color:green;">QuestionList</span>, Android complains.</p> <p>What the app needs to do is ask a question-- check a condition-- when the <span style="color:green;">NextButton</span> is clicked, and execute different blocks dependending on the answer. One way to ask the question is to ask, "is the variable <span style="color:green;">currentQuestionIndex</span> already 3?" If the answer is yes, you should set <span style="color:green;">currentQuestionIndex</span> back to 0 so the user is taken back to the first question.</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="controlblock">if test then-do</span></td> <td>Control</td> <td>To ask if user is on last question</td> </tr><tr><td><span class="basicblock">=</span> block</td> <td>Math</td> <td>to test if <span class="listblock">currentQuestionIndex</span> is 3</td> </tr><tr><td><span class="setblock">global currentQuestionIndex</span></td> <td>My Definitions</td> <td>-</td> </tr><tr><td><span class="basicblock">number</span> 3</td> <td>Math</td> <td>3 is number of items in the list</td> </tr><tr><td><span class="setblock">set currentQuestionIndex to</span></td> <td>My Definitions</td> <td>set to 0 to go back to first question</td> </tr><tr><td><span class="basicblock">Number</span> 0</td> <td>Math</td> <td>set to 0 because next blocks will increment to 1</td> </tr></table><p>The modified <span class="basicblock">NextButton.Click</span> event-handler should look like this:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/nextif3.png" /></p> <h4 class="ai-header">How the blocks work</h4> <p>When the <span style="color:green;">NextButton</span> is clicked, the app first checks to see if <span style="color:green;">currentQuestionIndex</span> has a 3 in it. If it does, <span style="color:green;">currentQuestionIndex</span> is set back to 0 so that when 1 is added to it with the blocks below, it will be 1 and the quiz will loop back to display the first question. Note that only the blocks inset within the <span class="controlblock">if-test-then-do</span> block are dependent on the condition-- the increment and set <span class="setblock">QuestionLabel.Text to</span> blocks are executed under all conditions.</p> <pre class="ai-testing"><strong style="color:black;">Test this behavior.</strong> Because variables like <span style="color:black;">currentQuestionIndex</span> are hidden, they are often the source of bugs in a program. Fortunately, App Inventor provides a way to make such hidden variables transparent during testing. Specifically, App Inventor allows you to "watch" how the value of a variable change as an app progresses. For this test, right-click the <span style="color:black;">currentQuestionIndex</span> def block in the Blocks Editor and choose <span style="color:black;">Watch</span>. Then choose <span style="color:black;">Restart Phone App</span>. The def block will then appear with a watch box showing the initial value of <span style="color:black;">currentQuestionIndex</span> (which should be 1): <img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/watchindex1.png" /> Now pick up the phone and click the <span style="color:black;">NextButton</span>. The second question, "who pitched the most recent perfect game in the major leagues?" should appear in the <span style="color:black;">QuestionLabel</span> on the phone, as before. On the App Inventor screen, 2 should appear in the <span style="color:black;">currentQuestionIndex</span> memory cell: <img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/watchindex2.png" /> When you click again, the third question should appear on the phone and 3 should appear in the memory cell . If you click again, 1 should appear in <span style="color:black;">currentQuestionIndex</span> and the first question on the phone.</pre><h4 class="ai-header">A Maintainable App: Making it Easy to Modify the Questions</h4> <p>Next, you'll modify the app to make it easy to add and remove elements from the list. You'll rewrite the blocks so that they'll work on any list, not just one with exactly three items. To begin, add a fourth question to <span style="color:green;">QuestionList</span> and another answer into <span style="color:green;">AnswerList</span>. The blocks should look like this:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/listsWith4.png" /></p> <pre class="ai-testing"><strong style="color:black;">Test the modified app.</strong> Click the <span style="color:black;">NextButton</span> a number of times. You should see that the fourth question never appears, no matter how many times you click <span style="color:black;">Next</span>.</pre><p>The problem is that the test to see if the user is on the last question is too specific: it asks if the <span style="color:green;">currentQuestionIndex</span> variable is 3:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/if3.png" /></p> <p>You could just change the number 3 to a 4, and the app would again work correctly. The problem with this solution, however, is that each time you modify the questions and answers, you also have to remember to make this change. Such dependencies in a computer program often lead to bugs, especially as an app grows in complexity. It's much better to set the program up so that it will work no matter how many questions there are. Such generality is even more important when the list you are working with changes dynamically, e.g., a quiz app that allows the user to add new questions.</p> <p>The better solution is to ask the question in a more general way. You really want to know if the current question the user is on-- the value of <span style="color:green;">currentQuestionIndex</span> -- is as large as the number of items in <span style="color:green;">QuestionList</span>. If the app asks the question in this more general manner, it will work even when you add to or remove items from the <span style="color:green;">QuestionList</span>. To modify the <span class="basicblock">NextButton.Click</span> event-handler you'll replace the previous test that referred directly to 3. 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="listblock">length of list</span></td> <td>Lists</td> <td>asks how many items are in <span style="color:green;">QuestionList</span></td> </tr><tr><td><span class="argblock">global QuestionList</span></td> <td>My Definitions</td> <td>put into list slot of <span class="listblock">length of list</span></td> </tr></table><p>Your <span class="basicblock">NextButton.Click</span> event-handler should now appear as:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/nextWithLength.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>The <span class="controlblock">if</span> test now compares the <span style="color:green;">currentQuestionIndex</span> to the length of the <span style="color:green;">QuestionList</span>. So if <span style="color:green;">currentQuestionIndex</span> has a 4 in it, and the length of the <span style="color:green;">QuestionList</span> is 4, then the <span style="color:green;">currentQuestionIndex</span> will be set to 0 (and then 1 after the increment operation in the first row of blocks after the if). Note that, because the blocks no longer refer to 3 or any specific size, the behavior will work no matter how many items are in the list.</p> <pre class="ai-testing"><strong style="color:black;">Test the modified behavior.</strong> When you click the <span style="color:black;">NextButton</span>, does the app now sequence through the four questions, moving to the first one after the fourth?</pre><h4 class="ai-header">Switching the Image for Each Question</h4> <p>The current app shows the same image, no matter what question is being asked. You can change this so an image pertaining to each question appears when the <span style="color:green;">NextButton</span> is clicked. Earlier, you added four pictures as media for the project. Now, you'll create a third list, <span style="color:green;">PictureList</span>, with the names of the image files as its items. and you'll modify the <span class="basicblock">NextButton.Click</span> event-handler to switch the picture each time.</p> <p>First, create a <span style="color:green;">PictureList</span> and initialize it with the names of the image files. Be sure that the names are exactly the same as the names of the files that were loaded in to the media of the project. Here's how the blocks for the <span style="color:green;">PictureList</span> should look:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/pictureList.png" /></p> <p>Next, you need to modify the <span class="basicblock">NextButton.Click</span> event-handler so that it modifies the picture depending on what question the user is on. If you set the <strong style="color:green;">Image.Picture</strong> property to a file name of an image that has been loaded, that image will appear. To modify <span class="basicblock">NextButton.Click</span>, 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 Image1.Picture to </span></td> <td>Image1</td> <td>set this to change the picture</td> </tr><tr><td><span class="listblock">select list item</span></td> <td>Lists</td> <td>need to select the picture corresponding to current question</td> </tr><tr><td><span class="argblock">global PictureList</span></td> <td>My Definitions</td> <td>select a file name from this list</td> </tr><tr><td><span class="argblock">global currentQuestionIndex</span></td> <td>My Definitions</td> <td>select the <span style="color:green;">currentQuestionIndex</span>-th item</td> </tr></table><p>Here is how the blocks should look:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/nextWithImageSwitch.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>The <span style="color:green;">currentQuestionIndex</span> serves as the index for the <span style="color:green;">QuestionList</span> and the <span style="color:green;&gt;PictureList&lt;/span&gt;. When &lt;span style=" color:green="">currentQuestionIndex</span> is 1, the app selects the first question and the first picture. When <span style="color:green;">currentQuestionIndex</span> is 2, the app selects the second question and second picture. Of course this scheme depends on the lists being in sync and indeed they are. For instance, the first picture, LarsenBerra.jpg, is a picture of Don Larsen, and Don Larsen is the answer to the first question, "Who pitched a perfect game in the World Series?" Test the modified behavior. Does a different image appear each time you click the <span style="color:green;">NextButton</span>?</p> <h4 class="ai-header">Evaluating Answers</h4> <p>Next, you'll add blocks that report whether the user has answered a question correctly or not. The user enters the answer in <span style="color:green;">AnswerText</span> and then clicks the <span style="color:green;">AnswerButton</span>. The app must compare the user's entry with the answer to the "current" question, using an <span class="controlblock">ifelse</span> block to check. The <span style="color:green;">RightWrongLabel</span> should be modified to report whether or not the answer is correct. You'll need the following blocks for this behavior:</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">AnswerButton.Click</span></td> <td>AnswerButton</td> <td>the behavior is triggered when user clicks the <span style="color:green;">AnswerButton</span></td> </tr><tr><td><span class="controlblock">ifelse</span></td> <td>Control</td> <td>if answer is correct, do one thing, else do another</td> </tr><tr><td><span class="basicblock">=</span> block</td> <td>Math</td> <td>to ask if answer is correct</td> </tr><tr><td><span class="argblock">AnswerText.Text</span></td> <td>AnswerText</td> <td>the user's answer is in this textbox</td> </tr><tr><td><span class="listblock">select list item</span></td> <td>Lists</td> <td>to select the current answer from <span style="color:green;">AnswerList</span></td> </tr><tr><td><span class="argblock">global AnswerList</span></td> <td>My Definitions</td> <td>The list to select from</td> </tr><tr><td><span class="argblock">global currentQuestionIndex</span></td> <td>My Definitions</td> <td>the question number (and answer number) the user is on</td> </tr><tr><td><span class="setblock">set RightWrongLabel.Text to</span></td> <td>RightWrongLabel</td> <td>report the answer here</td> </tr><tr><td><span class="textblock">text block</span> "correct"</td> <td>Text</td> <td>if answer is right</td> </tr><tr><td><span class="setblock">set RightWrongLabel.Text to</span></td> <td>RightWrongLabel</td> <td>report the answer here</td> </tr><tr><td><span class="textblock">text block</span> "incorrect"</td> <td>Text</td> <td>if answer is wrong</td> </tr></table><p>The blocks should look like this:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/answerButton.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>The <span class="controlblock">ifelse</span> test reads, "is the user's answer (<strong style="color:green;">AnswerText.Text</strong>) equal to the <span style="color:green;">currentQuestionIndex</span>-th item in the <span style="color:green;">AnswerList</span>?" If <span style="color:green;">currentQuestionIndex</span> is 1, the app will compare the user's answer with the first item in <span style="color:green;">AnswerList</span>, "Don Larsen". If <span style="color:green;">currentQuestionIndex</span> is 2, the app will compare the user's answer with the second answer in the list, "Dallas Braden", and so on. If the test result is positive, the <span class="controlblock">then-do</span> blocks are executed and the <span style="color:green;">RightWrongLabel</span> is set to "correct!". If the test is false, the <span class="controlblock">else-do</span> blocks are executed and the <span style="color:green;">RightWrongLabel</span> is set to "incorrect".</p> <pre class="ai-testing"><strong style="color:black;">Test the modified app.</strong> Try answering one of the questions. It should report whether or not you answered the question exactly as is specified in the <span style="color:black;">AnswerList</span>. Test with both a correct and incorrect answer (because text is being compared, the test is case-sensitive). Click the <span style="color:black;">NextButton</span> and answer a second question. Does it still work? It should, but you might notice that when you click the <span style="color:black;">NextButton</span>, the "correct"/"incorrect" text and the previous answer are still there. Though it's fairly innocuous, such user interface issues will definitely be noticed by the users of your app.</pre><p>To blank out the <span style="color:green;">RightWrongLabel</span> and the <span style="color:green;">AnswerText</span>, you'll put the following blocks within the <span class="basicblock">NextButton.click</span> event-handler:</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 RightWrongLabel.Text to</span></td> <td>RightWrongLabel</td> <td>the label to blank out</td> </tr><tr><td><span class="textblock">text</span> (blank)</td> <td>Text</td> <td>When <span style="color:green;">Next</span> is clicked, erase old answer critique</td> </tr><tr><td><span class="setblock">set AnswerText.Text to</span></td> <td>AnswerText</td> <td>the user's answer from previous question</td> </tr><tr><td><span class="textblock">text</span> (blank)</td> <td>Text</td> <td>When <span style="color:green;">Next</span> is clicked, erase old answer</td> </tr></table><p>The blocks should look like this:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/nextButtonFinal.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>When the <span style="color:green;">NextButton</span> is clicked, the user is moving on to the next question, so the top two rows of the event-handler blank out the <span style="color:green;">RightWrongLabel</span> and the <span style="color:green;">AnswerText</span>.</p> <pre class="ai-testing"><strong style="color:black;">Test this behavior.</strong> Answer a question and click <span style="color;black;">Submit</span>, then click the <span style="color:black;">NextButton</span>. Does your previous answer and the apps critique disappear?</pre><h4 class="ai-header">Final Program</h4> <p><strong><span style="color:green;">QuizMe</span>! Final Version:</strong></p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/quizmeFinal.png" /></p> <p>Package the final version of the app by choosing <strong>Package For Phone | Barcode</strong> from the Component Designer menu. When the barcode appears, use the barcode scanner on your phone to download and install the app.</p> <h4 class="ai-header">Variations</h4> <p>Once you get a quiz working, you might want to explore some variations. For example,</p> <ul><li>Instead of just showing images for each question, try playing a sound clip or a short video. With sound, you can turn your quiz app into a <span style="color:green;">Name That Tune</span> app.</li> <li>The quiz is very rigid in terms of what is accepted as a valid answer. There are a number of ways to modify this. One is to use the text.contains block to see if the user's answer is contained in the actual answer. Another is to provide multiple answers for each question, and check by iterating (using <span class="controlblock">foreach</span>) through them to see if any match.</li> <li>Transform the quiz so that it is multiple-choice. The list of answers will need to be a list of lists, with each sub-list holding the answer choices. Use the <strong>ListPicker</strong> component to allow the user to choose an answer.</li> </ul><h4 class="ai-header">Review</h4> <p>Here are some of the ideas covered in this tutorial:</p> <ul><li>Apps can be written in a general manner so that they work with any data list.</li> <li>Index variables are used to track the current position within a list. When you increment them, be careful about reaching the end of the list.</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/quizMe/QuizMeBarcode.png" /></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/quizMe/QuizMe.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;">QuizMe</span>? Return to the other tutorials <a href="/tutorials">here</a>.</pre><style> <!--/*--><![CDATA[/* ><!--*/ .ButtonText { color:green; font-weight: bold; } 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;">QuizMe</span></h2> <h4 class="ai-header">What you're building</h4> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/quizOnEmulator.png" style="float:right; height: 453px; width: 197px;" /></p> <p><a href="http://cs.usfca.edu/~wolber/appinventor/bookSplits/ch8Quiz.pdf">Download Alternate Example Version (Book Chapter PDF)</a></p> <p><span style="color:green;">QuizMe</span> is a trivia game about baseball, but you can use it as a template to build quizzes on any topic. With <span style="color:green;">QuizMe</span>:</p> <ul><li>The user steps through a series of questions, clicking a button to proceed to the next question.</li> <li>The user enters an answer for each question and the app reports whether each answer is correct or not.</li> </ul><p>With <span style="color:green;">QuizMe</span>, the quiz questions are always the same unless you, the programmer, change them. Later, you can create <a href="/content/makequiz-and-takequiz">MakeQuiz &amp; TakeQuiz</a>, an app that lets users of the app create and modify the quiz questions.</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/content/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;">QuizMe</span>, and also set the screen's <strong style="color:green;">Title</strong> to "QuizMe". Open the Blocks Editor and connect to the phone.</p> <p>Also download the following pictures of baseball players and save them on your computer. Later, you'll load these images into your project.</p> <ul><li><a href="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/Larsenberra.jpg">Larsenberra.jpg</a></li> <li><a href="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/dallasbraden.jpg">Dallasbraden.jpg</a></li> <li><a href="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/cyyoung.jpg">Cyyoung.jpg</a></li> <li><a href="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/nolanryan.jpg">Nolanryan.jpg</a></li> </ul><h4 class="ai-header">Introduction</h4> <p>You'll design the quiz game so that the user proceeds from question to question by clicking a Next button, and receives simple correct/incorrect feedback on each answer.</p> <p>This tutorial introduces:</p> <ul><li>Defining and displaying lists of information.</li> <li>Sequencing through a list using an index variable -- a variable that keeps track of a position in a list.</li> <li>Conditional behaviors-- performing certain operations only when a condition is met.</li> <li>Switching an image to show a different picture at different times.</li> </ul><h4 class="ai-header">Set up the Components</h4> <p>Use the component designer to create the interface for <span style="color:green;">QuizMe</span>. When you finish, it should look something like the snapshot below (there are also more detailed instructions below the snapshot).</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/designer.png" /></p> <p>To create this interface, first load the images you downloaded into the project. Click on the Add button in the Media area and select one of the downloaded files (e.g., Larsenberra.jpg). Then do the same for the other three images.</p> <p>Next, create the following components by dragging them from the Palette into the Viewer.</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> <strong>Image</strong> </td> <td>Basic</td> <td style="color:green;">Image1</td> <td>The picture part of the question</td> </tr><tr><td><strong>Label</strong></td> <td>Basic</td> <td style="color:green;">QuestionLabel</td> <td>Displays the current question</td> </tr><tr><td><strong>HorizontalArrangement</strong></td> <td>Screen Arrangement</td> <td style="color:green;">HorizontalArrangement1</td> <td>Organizes the AnswerPrompt and Text</td> </tr><tr><td><strong>Label</strong></td> <td>Basic</td> <td style="color:green;">AnswerPromptLabel</td> <td>Text prompting for an anwer</td> </tr><tr><td><strong>TextBox</strong></td> <td>Basic</td> <td style="color:green;">AnswerText</td> <td>User will enter answer here.</td> </tr><tr><td><strong>Label</strong></td> <td>Basic</td> <td style="color:green;">RightWrongLabel</td> <td>Correct/Incorrect is displayed here.</td> </tr><tr><td><strong>HorizontalArrangement</strong></td> <td>Screen Arrangement</td> <td style="color:green;">HorizontalArrangement2</td> <td>Organizes the AnswerButton and NextButton</td> </tr><tr><td><strong>Button</strong></td> <td>Basic</td> <td style="color:green;">AnswerButton</td> <td>User clicks to submit an answer</td> </tr><tr><td><strong>Button</strong></td> <td>Basic</td> <td style="color:green;">NextButton</td> <td>User clicks to proceed to the next answer</td> </tr></table><p>Set the properties of the components as described below:</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td><strong>Component</strong></td> <td><strong>Action</strong></td> </tr><tr><td> <span style="color:green;">Image1</span> </td> <td>Set its <span class="ButtonText">Picture</span> property to "Larsenberra.jpg". This is the first picture that should appear.</td> </tr><tr><td><span style="color:green;">QuestionLabel</span> </td> <td> Change <span class="ButtonText">Text</span> property to "question" </td> </tr><tr><td><span style="color:green;">AnswerPromptLabel</span> </td> <td>Change <span class="ButtonText">Text</span> property to "Enter Answer:". On the Viewer screen, move this label into <span style="color:green;">HorizontalArrangement1</span>. </td> </tr><tr><td><span style="color:green;">AnswerText</span> </td> <td>Change <span class="ButtonText">Hint</span> to "please enter an answer". On the Viewer screen, move <span style="color:green;">AnswerText</span> into <span style="color:green;">HorizontalArrangement1</span>. </td> </tr><tr><td><span style="color:green;">AnswerButton</span> </td> <td>Change <span class="ButtonText">Text</span> property to "Submit". On the Viewer, move the button into <span style="color:green;">HorizontalArrangment2.</span> </td> </tr><tr><td> <span style="color:green;">NextButton</span> </td> <td>Change <span class="ButtonText">Text</span> property to "Next". Move the button into <span style="color:green;">HorizontalArrangement2</span>. </td> </tr><tr><td><span style="color:green;">RightWrongLabel</span></td> <td> Change <span class="ButtonText">Text</span> property to "correct/incorrect". </td> </tr></table><h4 class="ai-header">Add Behaviors to the Components</h4> <p>Open the Blocks Editor to add the behaviors for the app. First, you'll define two list variables, <span style="color:green;">QuestionList</span> to hold the list of questions, and <span style="color:green;">AnswerList</span> to hold the list of corresponding answers.</p> <p>To define the two list variables, 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></td> <td>Definitions</td> <td>Defines the <span style="color:green;">QuestionList</span> variable (rename it)</td> </tr><tr><td><span class="setblock">def variable</span></td> <td>Definitions</td> <td>Defines the <span style="color:green;">AnswerList</span> variable (rename it)</td> </tr><tr><td><span class="listblock">make a list</span></td> <td>Lists</td> <td>Used to insert the items of the <span style="color:green;">QuestionList</span></td> </tr><tr><td><span class="textblock">text</span> (3 of them)</td> <td>Text</td> <td>The actual questions</td> </tr><tr><td><span class="listblock">make a list</span></td> <td>Lists</td> <td>Used to insert the items of the <span style="color:green;">AnswerList</span></td> </tr><tr><td><span class="textblock">text</span> (3 of them)</td> <td>Text</td> <td>The actual answers</td> </tr></table><p>You create global variables by dragging in a <span class="setblock">def variable</span> block from the Definitions drawer and double-clicking the default name "variable" to change its name. The <span class="setblock">def variable</span> block has a slot for the initial value of the variable. The variable can represent a number or text, or even a list, for which you can plug in a <span class="listblock">make a list</span> block into the variable definition.</p> <p>The blocks should look like this:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/questionlist.png" /></p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/AnswerList.png" /></p> <h4 class="ai-header">Define the Hidden Index Variable</h4> <p>Each time the user clicks the <span style="color:green;">NextButton</span> to proceed through the quiz, the app needs to remember which question it is on. In programming, to remember something, you define a new variable. In this case, the app needs to remember the current question number -- the index into the list <span style="color:green;">QuestionList</span>.</p> <p>To create the variable <span style="color:green;">currentQuestionIndex</span>, 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></td> <td>Definitions</td> <td>Defines the <span style="color:green;">currentQuestionIndex</span> variable (rename it)</td> </tr><tr><td><span class="basicblock">number</span> (1)</td> <td>Math</td> <td>Set the initial value of <span style="color:green;">currentQuestionIndex</span> to 1</td> </tr></table><p>The blocks should look like this:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/index.png" /></p> <h4 class="ai-header">Display the first question</h4> <p>To start, you'll ignore the answers and just work on the behavior to sequence through the questions. The desired behavior is the following: when the app starts, the first question should appear in the label named <span style="color:green;">QuestionLabel</span>. When the user clicks the <span style="color:green;">NextButton</span>, the second question should appear. When the user clicks again, the third should appear. When the last question is reached, clicking the <span style="color:green;">NextButton</span> should result in the first question once again appearing in the <span color="color:green;">QuestionLabel</span>.</p> <pre class="ai-box">With App Inventor, you select particular items in a list with the <span class="listblock">select list item</span> block. The block asks you to specify the list and an index--a position in the list. If a list has three items, the indexes 1, 2, and 3 are valid.</pre><p>For <span style="color:green;">QuizMe</span>, when the app starts, the app should choose the first question in the list and display it in the <span style="color:green;">QuestionLabel</span> component.</p> <p>For this app initialization behavior, 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">Screen1.Initialize</span></td> <td>Screen1</td> <td>When the app begins, this event-handler is triggered.</td> </tr><tr><td><span class="setblock">set QuestionLabel.Text to</span></td> <td>QuestionLabel</td> <td>Need to put the first question in <span style="color:green;">QuestionLabel</span></td> </tr><tr><td><span class="listblock">select list item</span></td> <td>Lists</td> <td>Need to select the first question from <span style="color:green;">QuestionLabel</span></td> </tr><tr><td><span class="argblock">global QuestionList</span></td> <td>My Definitions</td> <td>The list to select from</td> </tr><tr><td><span class="basicblock">number</span> (1)</td> <td>Math</td> <td>Select the first question by using an index of 1</td> </tr></table><p>The blocks should look like this:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/screenInit.png" /></p> <h4 class="ai-header">How the blocks work</h4> <p>The <span class="basicblock">Screen1.Initialize</span> event is triggered when the app begins. The first item of the variable <span style="color:green;">QuestionList</span> is selected and placed into <span class="basicblock">QuestionLabel.Text</span>. So when the app begins, the user will see the first question.</p> <pre class="ai-testing"><strong style="color:black;">Test this behavior.</strong> Click <span style="color:black;">Restart Phone App</span> (or <span style="color:black;">Connect Phone</span> if not connected). What appears on the phone? If you created the <span style="color:black;">QuestionList</span> as described above, the first item of <span style="color:black;">QuestionList</span>, "Who pitched a perfect game in the World Series?", should appear in the <span style="color:black;">QuestionLabel</span>.</pre><h4 class="ai-header">Iterating Through the Questions</h4> <p>Now program the behavior of the <span style="color:green;">NextButton</span>. You've already defined the <span class="listblock">currentQuestionIndex</span> to remember the question the user is on. When <span style="color:green;">NextButton</span> is clicked, the app needs to increment this variable, e.g., change it from 1 to 2 or from 2 to 3, etc., and then use the resulting value to select the new "current" question. For this behavior, 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">NextButton.Click</span></td> <td>NextButton</td> <td>When user clicks Next, this event-handler is triggered.</td> </tr><tr><td><span class="setblock">set currentQuestionIndex to</span></td> <td>My Definitions</td> <td>Need to put the first question in <span style="color:green;">QuestionLabel</span></td> </tr><tr><td><span class="basicblock">+</span> block</td> <td>Math</td> <td>Used to increment <span style="color:green;">currentQuestionIndex</span></td> </tr><tr><td><span class="argblock">global currentQuestionIndex</span></td> <td>My Definitions</td> <td>New value will be old value + 1</td> </tr><tr><td><span class="basicblock">number</span> (1)</td> <td>Math</td> <td>For the + 1</td> </tr><tr><td><span class="setblock">set QuestionLabel.Text to</span></td> <td>QuestionLabel</td> <td>Need to display the next question here</td> </tr><tr><td><span class="listblock">select list item</span></td> <td>Lists</td> <td>Need to select the first question from <span style="color:green;">QuestionList</span></td> </tr><tr><td><span class="argblock">global QuestionList</span></td> <td>My Definitions</td> <td>Plug into <span style="color:green;">list</span> slot of call select list item</td> </tr><tr><td><span class="argblock">global currentQuestionIndex</span></td> <td>My Definitions</td> <td>Plug into <span style="color:green;">index</span> slot of call select list item, we want nth item</td> </tr></table><p>The blocks should look like this:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/firstNext.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>The first row of blocks increments the variable <span style="color:green;">currentQuestionIndex</span>. If <span style="color:green;">currentQuestionIndex</span> has a 1 in it, it is changed to 2. If it has a 2, it is changed to 3, and so on. Once the <span style="color:green;">currentQuestionIndex</span> variable has been changed, the app uses it to select the "current" question.</p> <p>Recall that in the <span class="basicblock">Screen.Initialize</span> event-handler, the app selected the first question to display:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/selectFirst.png" /></p> <p>When the <span style="color:green;">NextButton</span> is clicked, the app doesn't choose the first item in the list, or the 2nd or 3rd, it chooses the <span class="listblock">currentQuestionIndex</span>-th item.</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/chooseCurrent.png" /></p> <p>The blocks are executed in a right-to-left manner. The app first evaluates the index parameter of <span class="listblock">select list item</span>, which is the variable <span style="color:green;">currentQuestionIndex</span>. The number is stored in <span style="color:green;">currentQuestionIndex</span> is used as the index when the select list item is executed.</p> <p>When the <span style="color:green;">NextButton</span> is clicked for the first time, the increment blocks will set <span style="color:green;">currentQuestionIndex</span> from 1 to 2, so the app will select the second item from <span style="color:green;">QuestionList</span>, "who pitched the first perfect game of 2010?". The second time <span style="color:green;">NextButton</span> is clicked, <span style="color:green;">currentQuestionIndex</span> will be set from 2 to 3, and the app will select the 3rd question in the list, "who pitched the first perfect game of the modern era?"</p> <pre class="ai-testing"><strong style="color:black;">Test this behavior.</strong> Test the behavior of the <span style="color:black;">NextButton</span> to see if the app is working correctly thus far. To test, play the role of the user and click the <span style="color:black;">NextButton</span> on the phone. Does the phone display the second question, "Who pitched the first perfect game of 2010?" It should, and the third question should appear when you click the <span style="color:black;">NextButton</span> again. If this is working, pat yourself on the back quickly, and then go on. Try clicking the <span style="color:black;">NextButton</span> again (a third time). You should see an error: "Attempting to get item 4 of a list of length 3". The app has a bug-- do you know what the problem is?</pre><p>The problem with the app is that it always increments the <span style="color:green;">currentQuestionIndex</span> variable when the <span style="color:green;">NextButton</span> is clicked. When <span style="color:green;">currentQuestionIndex</span> is already 3 and the user clicks the <span style="color:green;">NextButton</span>, the app changes <span style="color:green;">currentQuestionIndex</span> from 3 to 4, then calls select list item to get the <span style="color:green;">currentQuestionIndex</span>-th , or in this case, the 4th item. Since there are only three items in the variable <span style="color:green;">QuestionList</span>, Android complains.</p> <p>What the app needs to do is ask a question-- check a condition-- when the <span style="color:green;">NextButton</span> is clicked, and execute different blocks dependending on the answer. One way to ask the question is to ask, "is the variable <span style="color:green;">currentQuestionIndex</span> already 3?" If the answer is yes, you should set <span style="color:green;">currentQuestionIndex</span> back to 0 so the user is taken back to the first question.</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="controlblock">if test then-do</span></td> <td>Control</td> <td>To ask if user is on last question</td> </tr><tr><td><span class="basicblock">=</span> block</td> <td>Math</td> <td>to test if <span class="listblock">currentQuestionIndex</span> is 3</td> </tr><tr><td><span class="setblock">global currentQuestionIndex</span></td> <td>My Definitions</td> <td>-</td> </tr><tr><td><span class="basicblock">number</span> 3</td> <td>Math</td> <td>3 is number of items in the list</td> </tr><tr><td><span class="setblock">set currentQuestionIndex to</span></td> <td>My Definitions</td> <td>set to 0 to go back to first question</td> </tr><tr><td><span class="basicblock">Number</span> 0</td> <td>Math</td> <td>set to 0 because next blocks will increment to 1</td> </tr></table><p>The modified <span class="basicblock">NextButton.Click</span> event-handler should look like this:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/nextif3.png" /></p> <h4 class="ai-header">How the blocks work</h4> <p>When the <span style="color:green;">NextButton</span> is clicked, the app first checks to see if <span style="color:green;">currentQuestionIndex</span> has a 3 in it. If it does, <span style="color:green;">currentQuestionIndex</span> is set back to 0 so that when 1 is added to it with the blocks below, it will be 1 and the quiz will loop back to display the first question. Note that only the blocks inset within the <span class="controlblock">if-test-then-do</span> block are dependent on the condition-- the increment and set <span class="setblock">QuestionLabel.Text to</span> blocks are executed under all conditions.</p> <pre class="ai-testing"><strong style="color:black;">Test this behavior.</strong> Because variables like <span style="color:black;">currentQuestionIndex</span> are hidden, they are often the source of bugs in a program. Fortunately, App Inventor provides a way to make such hidden variables transparent during testing. Specifically, App Inventor allows you to "watch" how the value of a variable change as an app progresses. For this test, right-click the <span style="color:black;">currentQuestionIndex</span> def block in the Blocks Editor and choose <span style="color:black;">Watch</span>. Then choose <span style="color:black;">Restart Phone App</span>. The def block will then appear with a watch box showing the initial value of <span style="color:black;">currentQuestionIndex</span> (which should be 1): <img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/watchindex1.png" /> Now pick up the phone and click the <span style="color:black;">NextButton</span>. The second question, "who pitched the most recent perfect game in the major leagues?" should appear in the <span style="color:black;">QuestionLabel</span> on the phone, as before. On the App Inventor screen, 2 should appear in the <span style="color:black;">currentQuestionIndex</span> memory cell: <img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/watchindex2.png" /> When you click again, the third question should appear on the phone and 3 should appear in the memory cell . If you click again, 1 should appear in <span style="color:black;">currentQuestionIndex</span> and the first question on the phone.</pre><h4 class="ai-header">A Maintainable App: Making it Easy to Modify the Questions</h4> <p>Next, you'll modify the app to make it easy to add and remove elements from the list. You'll rewrite the blocks so that they'll work on any list, not just one with exactly three items. To begin, add a fourth question to <span style="color:green;">QuestionList</span> and another answer into <span style="color:green;">AnswerList</span>. The blocks should look like this:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/listsWith4.png" /></p> <pre class="ai-testing"><strong style="color:black;">Test the modified app.</strong> Click the <span style="color:black;">NextButton</span> a number of times. You should see that the fourth question never appears, no matter how many times you click <span style="color:black;">Next</span>.</pre><p>The problem is that the test to see if the user is on the last question is too specific: it asks if the <span style="color:green;">currentQuestionIndex</span> variable is 3:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/if3.png" /></p> <p>You could just change the number 3 to a 4, and the app would again work correctly. The problem with this solution, however, is that each time you modify the questions and answers, you also have to remember to make this change. Such dependencies in a computer program often lead to bugs, especially as an app grows in complexity. It's much better to set the program up so that it will work no matter how many questions there are. Such generality is even more important when the list you are working with changes dynamically, e.g., a quiz app that allows the user to add new questions.</p> <p>The better solution is to ask the question in a more general way. You really want to know if the current question the user is on-- the value of <span style="color:green;">currentQuestionIndex</span> -- is as large as the number of items in <span style="color:green;">QuestionList</span>. If the app asks the question in this more general manner, it will work even when you add to or remove items from the <span style="color:green;">QuestionList</span>. To modify the <span class="basicblock">NextButton.Click</span> event-handler you'll replace the previous test that referred directly to 3. 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="listblock">length of list</span></td> <td>Lists</td> <td>asks how many items are in <span style="color:green;">QuestionList</span></td> </tr><tr><td><span class="argblock">global QuestionList</span></td> <td>My Definitions</td> <td>put into list slot of <span class="listblock">length of list</span></td> </tr></table><p>Your <span class="basicblock">NextButton.Click</span> event-handler should now appear as:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/nextWithLength.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>The <span class="controlblock">if</span> test now compares the <span style="color:green;">currentQuestionIndex</span> to the length of the <span style="color:green;">QuestionList</span>. So if <span style="color:green;">currentQuestionIndex</span> has a 4 in it, and the length of the <span style="color:green;">QuestionList</span> is 4, then the <span style="color:green;">currentQuestionIndex</span> will be set to 0 (and then 1 after the increment operation in the first row of blocks after the if). Note that, because the blocks no longer refer to 3 or any specific size, the behavior will work no matter how many items are in the list.</p> <pre class="ai-testing"><strong style="color:black;">Test the modified behavior.</strong> When you click the <span style="color:black;">NextButton</span>, does the app now sequence through the four questions, moving to the first one after the fourth?</pre><h4 class="ai-header">Switching the Image for Each Question</h4> <p>The current app shows the same image, no matter what question is being asked. You can change this so an image pertaining to each question appears when the <span style="color:green;">NextButton</span> is clicked. Earlier, you added four pictures as media for the project. Now, you'll create a third list, <span style="color:green;">PictureList</span>, with the names of the image files as its items. and you'll modify the <span class="basicblock">NextButton.Click</span> event-handler to switch the picture each time.</p> <p>First, create a <span style="color:green;">PictureList</span> and initialize it with the names of the image files. Be sure that the names are exactly the same as the names of the files that were loaded in to the media of the project. Here's how the blocks for the <span style="color:green;">PictureList</span> should look:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/pictureList.png" /></p> <p>Next, you need to modify the <span class="basicblock">NextButton.Click</span> event-handler so that it modifies the picture depending on what question the user is on. If you set the <strong style="color:green;">Image.Picture</strong> property to a file name of an image that has been loaded, that image will appear. To modify <span class="basicblock">NextButton.Click</span>, 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 Image1.Picture to </span></td> <td>Image1</td> <td>set this to change the picture</td> </tr><tr><td><span class="listblock">select list item</span></td> <td>Lists</td> <td>need to select the picture corresponding to current question</td> </tr><tr><td><span class="argblock">global PictureList</span></td> <td>My Definitions</td> <td>select a file name from this list</td> </tr><tr><td><span class="argblock">global currentQuestionIndex</span></td> <td>My Definitions</td> <td>select the <span style="color:green;">currentQuestionIndex</span>-th item</td> </tr></table><p>Here is how the blocks should look:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/nextWithImageSwitch.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>The <span style="color:green;">currentQuestionIndex</span> serves as the index for the <span style="color:green;">QuestionList</span> and the <span style="color:green;&gt;PictureList&lt;/span&gt;. When &lt;span style=" color:green="">currentQuestionIndex</span> is 1, the app selects the first question and the first picture. When <span style="color:green;">currentQuestionIndex</span> is 2, the app selects the second question and second picture. Of course this scheme depends on the lists being in sync and indeed they are. For instance, the first picture, LarsenBerra.jpg, is a picture of Don Larsen, and Don Larsen is the answer to the first question, "Who pitched a perfect game in the World Series?" Test the modified behavior. Does a different image appear each time you click the <span style="color:green;">NextButton</span>?</p> <h4 class="ai-header">Evaluating Answers</h4> <p>Next, you'll add blocks that report whether the user has answered a question correctly or not. The user enters the answer in <span style="color:green;">AnswerText</span> and then clicks the <span style="color:green;">AnswerButton</span>. The app must compare the user's entry with the answer to the "current" question, using an <span class="controlblock">ifelse</span> block to check. The <span style="color:green;">RightWrongLabel</span> should be modified to report whether or not the answer is correct. You'll need the following blocks for this behavior:</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">AnswerButton.Click</span></td> <td>AnswerButton</td> <td>the behavior is triggered when user clicks the <span style="color:green;">AnswerButton</span></td> </tr><tr><td><span class="controlblock">ifelse</span></td> <td>Control</td> <td>if answer is correct, do one thing, else do another</td> </tr><tr><td><span class="basicblock">=</span> block</td> <td>Math</td> <td>to ask if answer is correct</td> </tr><tr><td><span class="argblock">AnswerText.Text</span></td> <td>AnswerText</td> <td>the user's answer is in this textbox</td> </tr><tr><td><span class="listblock">select list item</span></td> <td>Lists</td> <td>to select the current answer from <span style="color:green;">AnswerList</span></td> </tr><tr><td><span class="argblock">global AnswerList</span></td> <td>My Definitions</td> <td>The list to select from</td> </tr><tr><td><span class="argblock">global currentQuestionIndex</span></td> <td>My Definitions</td> <td>the question number (and answer number) the user is on</td> </tr><tr><td><span class="setblock">set RightWrongLabel.Text to</span></td> <td>RightWrongLabel</td> <td>report the answer here</td> </tr><tr><td><span class="textblock">text block</span> "correct"</td> <td>Text</td> <td>if answer is right</td> </tr><tr><td><span class="setblock">set RightWrongLabel.Text to</span></td> <td>RightWrongLabel</td> <td>report the answer here</td> </tr><tr><td><span class="textblock">text block</span> "incorrect"</td> <td>Text</td> <td>if answer is wrong</td> </tr></table><p>The blocks should look like this:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/answerButton.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>The <span class="controlblock">ifelse</span> test reads, "is the user's answer (<strong style="color:green;">AnswerText.Text</strong>) equal to the <span style="color:green;">currentQuestionIndex</span>-th item in the <span style="color:green;">AnswerList</span>?" If <span style="color:green;">currentQuestionIndex</span> is 1, the app will compare the user's answer with the first item in <span style="color:green;">AnswerList</span>, "Don Larsen". If <span style="color:green;">currentQuestionIndex</span> is 2, the app will compare the user's answer with the second answer in the list, "Dallas Braden", and so on. If the test result is positive, the <span class="controlblock">then-do</span> blocks are executed and the <span style="color:green;">RightWrongLabel</span> is set to "correct!". If the test is false, the <span class="controlblock">else-do</span> blocks are executed and the <span style="color:green;">RightWrongLabel</span> is set to "incorrect".</p> <pre class="ai-testing"><strong style="color:black;">Test the modified app.</strong> Try answering one of the questions. It should report whether or not you answered the question exactly as is specified in the <span style="color:black;">AnswerList</span>. Test with both a correct and incorrect answer (because text is being compared, the test is case-sensitive). Click the <span style="color:black;">NextButton</span> and answer a second question. Does it still work? It should, but you might notice that when you click the <span style="color:black;">NextButton</span>, the "correct"/"incorrect" text and the previous answer are still there. Though it's fairly innocuous, such user interface issues will definitely be noticed by the users of your app.</pre><p>To blank out the <span style="color:green;">RightWrongLabel</span> and the <span style="color:green;">AnswerText</span>, you'll put the following blocks within the <span class="basicblock">NextButton.click</span> event-handler:</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 RightWrongLabel.Text to</span></td> <td>RightWrongLabel</td> <td>the label to blank out</td> </tr><tr><td><span class="textblock">text</span> (blank)</td> <td>Text</td> <td>When <span style="color:green;">Next</span> is clicked, erase old answer critique</td> </tr><tr><td><span class="setblock">set AnswerText.Text to</span></td> <td>AnswerText</td> <td>the user's answer from previous question</td> </tr><tr><td><span class="textblock">text</span> (blank)</td> <td>Text</td> <td>When <span style="color:green;">Next</span> is clicked, erase old answer</td> </tr></table><p>The blocks should look like this:</p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/nextButtonFinal.png" /></p> <h4 class="ai-header">How the Blocks Work</h4> <p>When the <span style="color:green;">NextButton</span> is clicked, the user is moving on to the next question, so the top two rows of the event-handler blank out the <span style="color:green;">RightWrongLabel</span> and the <span style="color:green;">AnswerText</span>.</p> <pre class="ai-testing"><strong style="color:black;">Test this behavior.</strong> Answer a question and click <span style="color;black;">Submit</span>, then click the <span style="color:black;">NextButton</span>. Does your previous answer and the apps critique disappear?</pre><h4 class="ai-header">Final Program</h4> <p><strong><span style="color:green;">QuizMe</span>! Final Version:</strong></p> <p><img src="http://beta.appinventor.mit.edu/learn/tutorials/quizme/QuizMeAssets/quizmeFinal.png" /></p> <p>Package the final version of the app by choosing <strong>Package For Phone | Barcode</strong> from the Component Designer menu. When the barcode appears, use the barcode scanner on your phone to download and install the app.</p> <h4 class="ai-header">Variations</h4> <p>Once you get a quiz working, you might want to explore some variations. For example,</p> <ul><li>Instead of just showing images for each question, try playing a sound clip or a short video. With sound, you can turn your quiz app into a <span style="color:green;">Name That Tune</span> app.</li> <li>The quiz is very rigid in terms of what is accepted as a valid answer. There are a number of ways to modify this. One is to use the text.contains block to see if the user's answer is contained in the actual answer. Another is to provide multiple answers for each question, and check by iterating (using <span class="controlblock">foreach</span>) through them to see if any match.</li> <li>Transform the quiz so that it is multiple-choice. The list of answers will need to be a list of lists, with each sub-list holding the answer choices. Use the <strong>ListPicker</strong> component to allow the user to choose an answer.</li> </ul><h4 class="ai-header">Review</h4> <p>Here are some of the ideas covered in this tutorial:</p> <ul><li>Apps can be written in a general manner so that they work with any data list.</li> <li>Index variables are used to track the current position within a list. When you increment them, be careful about reaching the end of the list.</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/quizMe/QuizMeBarcode.png" /></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/quizMe/QuizMe.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> <p>Or <a href="http://explore.appinventor.mit.edu/sites/all/files/tutorials/quizMe/QuizMe.apk">download the apk</a></p> <p>.</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;">QuizMe</span>? Return to the other tutorials <a href="http://explore.appinventor.mit.edu/content/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/game" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Game</a> </li> </ul> </section> Fri, 14 Jun 2013 16:10:24 +0000 aaron 312 at http://dev-explore.appinventor.mit.edu http://dev-explore.appinventor.mit.edu/content/quizme#comments Pizza Party with Fusion Tables http://dev-explore.appinventor.mit.edu/content/pizzaparty <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><div style="float:right;"><img src="http://explore.appinventor.mit.edu/sites/all/files/tutorials/pizzaParty/pizza_party_screenshot.png" style="border:none; padding-left:15;" /><br /><a href="http://explore.appinventor.mit.edu/sites/all/files/tutorials/pizzaParty/PizzaParty_FillInYourKeyAndID.zip"><img src="http://explore.appinventor.mit.edu/sites/all/files/tutorials/pizzaParty/downloadSourcebutton.png" style="border:none; padding-left:65;" /></a></div> <p>In this tutorial, you will create an app that lets people enter food orders for a pizza party. Orders are stored in a Fusion Table, providing one easy to access place for the data. By reading from the table, the app can easily display the orders that have been entered.</p> <p>A <a href="http://en.wikipedia.org/wiki/Google_Fusion_Tables">Fusion Table</a> is a Google service to support the gathering, managing, sharing, and visualizing of data. Data are stored in multiple tables on Google's <a href="http://en.wikipedia.org/wiki/Cloud_computing">cloud</a>. Individual tables can be merged (or fused) if they contain a common column, and they can be easily visualized on maps and in various kinds of charts and graphs. All of the data are stored in a public table that can be accessed via Google Drive, and allows different users<a href="#note">**</a> to add information to the tables. Coupled with a location sensor, an App Inventor app could post periodic updates of each user's location to a public fusion table. Users could post notes to mark noteworthy locations. For example, a team of botanists could use a Fusion Table app to create an annotated catalog of the trees or plants within a certain geographical area.</p> <hr /><p>This tutorial introduces:</p> <ul><li>Using the <b>FusionTables</b> component</li> <li>Using a <b>WebViewer</b> component</li> </ul><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 the app's behavior. If you are not familiar with the basics, try stepping through some of the <a href="http://appinventor.mit.edu/explore/tutorials.html">basic tutorials</a> before continuing.</p> <hr /><p><strong>IF YOU DOWNLOAD THE SOURCE CODE</strong> you will need to supply your own API KEY, your own Table ID, and your own TABLE URL in the global variables in the blocks editor. This source code will not work without these additions.</p> <h4 class="ai-header">Creating your own Fusion Table</h4> <p>Creating your own Fusion Tables is as easy as creating a Google document, if you are familiar with that process.</p> <p><img src="/sites/all/files/tutorials/pizzaParty/MoreEvenMoreFusion.jpg" style="float:right" /></p> <ol><li>On the web, login to your Gmail account or any other Google service (e.g., Drive, YouTube).</li> <li>Select the <strong>More &gt; Even more</strong> menu and scroll to the bottom of the page of Google services where you will find the Fusion Tables service under "Innovation", click on the Fusion Tables link.</li> <li>Click "Create New Table"</li> <li>You will be given three options for the new table: "From this computer", "Google Spreadsheets", or "Create an empty table". For the purposes of this tutorial, select <b>Create an empty table</b>.</li> <li>You will see that the new table automatically comes with four columns. Change the column names for your Pizza app by going to <strong>Edit &gt; Change Columns</strong>.You'll rename the four default columns to <em>Date</em> (type=Date), <em>Name</em> (type=Text), <em>Pizza</em> (type=Text), <em>Drink</em> (type=Text). Click save and then add a fifth column by going to the <strong>Edit &gt; Add Column</strong>. Name this fifth column <em>Comment</em> (type=Text). .</li> <li>Leave this window open so that you can come back and get the URL, which you'll need when you set up the properties of the WebViewer component in your app.</li> <li>Click on the <strong>Share</strong> button (top right) to modify the table's permissions. For this tutorial, you can specify a few friends who will use the app. Only people who are explicitly given permission will be able to enter pizza party preferences through your app.</li> </ol><pre class="ai-box"><img src="/sites/all/files/tutorials/pizzaParty/FusionTables_GoogleDrive.png" style="float:right;padding:15px;" />Another way to create a new table: go to Google Drive. Click the <strong>Create</strong> button, then go to <strong>More &gt; Fusion Table</strong>. The picture at right shows what the Create button's menu should look like. If you do not see Fusion Table in your menu, then you may need to contact your school's network administrator. (See Troubleshooting section at the bottom of this page.) </pre><p><strong><a name="note" id="note">NOTE</a> about Sharing Fusion Tables:</strong> To share a FusionTable with others, you have to invite each person individually, the same way you would share a private google doc. There is no way to share write privileges to a FusionTable with the public. Public access is restricted to read-only.</p> <h4 class="ai-header">Getting an API Key</h4> <p>In order to use the FusiontablesControl Component you need to acquire a Google Applications Programming Interface (API) key, an API Key. To get an API key, follow these instructions:</p> <ol><li>Go to your <a href="https://code.google.com/apis/console/">Google APIs Console</a> and login if necessary.</li> <li>Select the <strong>Services</strong> item from the menu on the upper left.</li> <li>In the list of services, find the <strong>Fusion Tables API</strong> service and click the toggle button that currently says "off".</li> <li>Read and agree to the terms of service. When you return to the APIs Console page you'll see that the on/off switch next to Fusion Tables API is now "On" and green.</li> <li>Go back up the menu on the upper left of the screen and select the <strong>API Access</strong> item.</li> <li>Your API Key will be in the rectangular box in the section called "Simple API Access". You will need set the "API Key" property of the Fusiontables Control component in any app that you make that uses Fusion Tables. (More info below.)</li> </ol><h4 class="ai-header">Building the App</h4> <p>Connect to the App Inventor web site and start a new project. Name the new project PizzaParty, set the screen's orientation to Portrait and uncheck the Screen's scrollable property. You may also wish to set the Screen's Title property to something other than 'Screen 1'.</p> <h4 class="ai-header">The User Interface</h4> <p>In addition to the FusiontablesControl component, the Pizza Party app makes use of several other types of components. It is assumed that you have learned how to use these in previous lessons. Use the component designer to create the interface for the Search Party. When completed, the designer should look like this:</p> <p><img src="http://explore.appinventor.mit.edu/sites/all/files/tutorials/pizzaParty/pp_designer.png" /></p> <p>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> <td> Settings of Component </td> </tr><tr><td> <b>Label</b> </td> <td>Basic</td> <td style="color:green;">LabelName</td> <td>Shows the text "Your Name:"</td> <td></td> </tr><tr><td> <b>TextBox</b> </td> <td>Basic</td> <td style="color:green;">TextBoxUserName</td> <td>Gets input from user</td> <td>Set the width property to Fill Parent</td> </tr><tr><td> <b>HorizontalArrangement</b> </td> <td>Screen Arrangement</td> <td style="color:green;">HorizontalArrangement1</td> <td>Contains Name Label and Textbox</td> <td></td> </tr><tr><td> <b>ListPicker</b> </td> <td>Basic</td> <td style="color:green;">ListPickerPizza</td> <td>Accesses the list of available pizza types</td> <td> <p>Set the Width property to Fill Parent</p> <p>Set the Text property to "What type of pizza?"</p> <p>Set the ElementsFromString to "Cheese, Pepperoni, Anchovies, Hawaiian"</p> </td> </tr><tr><td> <b>ListPicker</b> </td> <td>Basic</td> <td style="color:green;">ListPickerDrink</td> <td>Accesses the list of available drinks.</td> <td> <p>Set the Width property to Fill Parent</p> <p>Set the Text property to "What type of drink?"</p> <p>Set the ElementsFromString to "Coke, Diet Coke, Sprite, Ginger Ale"</p> </td> </tr><tr><td> <b>Label</b> </td> <td>Basic</td> <td style="color:green;">LabelComment</td> <td>Shows the text "Comments:"</td> <td></td> </tr><tr><td> <b>TextBox</b> </td> <td>Basic</td> <td style="color:green;">TextBoxComments</td> <td>Takes user input</td> <td>Set the width property to Fill Parent</td> </tr><tr><td> <b>HorizontalArrangement</b> </td> <td>Screen Arrangement</td> <td style="color:green;">HorizontalArrangement2</td> <td>Contains Comments Label and Textbox</td> <td></td> </tr><tr><td> <b>Button</b> </td> <td>Basic</td> <td style="color:green;">ButtonSubmit</td> <td>Adds new data to the public fusion table</td> <td> <p>Set the Text to "Submit"</p> <p>Set the width property to Fill Parent</p> </td> </tr><tr><td> <b>WebViewer</b> </td> <td>Not ready for prime time</td> <td style="color:green;">WebViewer1</td> <td>Displays Fusion Table</td> <td>Set width and height properties to Fill Parent</td> </tr><tr><td> <b>FusiontablesControl</b> </td> <td>Not ready for prime time</td> <td style="color:green;">FusiontablesControl1</td> <td>Manages interactions with the app's Fusion Table</td> <td></td> </tr><tr><td> <b>Clock</b> </td> <td>Basic</td> <td style="color:green;">Clock1</td> <td>Used to provide a timestamp each time an order is placed.</td> <td></td> </tr><tr><td> <b>Notifier</b> </td> <td>Other stuff</td> <td style="color:green;">Notifier1</td> <td>Notifies the user of any errors</td> <td></td> </tr></table><h4 class="ai-header">Determining your Fusion Table URL and Table ID</h4> <p>In the blocks editor, you will set the WebViewer component's HomeURL property to point to the URL of your table. To find your Fusion Table's URL:</p> <ol><li>In your browser, navigate to the new Fusion Table you just created.</li> <li>Go to the menu and select <b>Tools &gt; Publish</b>.</li> <li>You'll see a notice saying: "This table is private and will not be visible". Click the blue link that says "Change Visibility".</li> <li>In the list of "Who Has Access", click the blue "Change..." link next to "Private - Only people listed below..."</li> <li>Choose"Public on the Web" or "Anyone with the link". Either of these settings will work for this tutorial. In the future, you should decide this setting based on the sensitivity of your data.</li> <li>Click the green Save button, then the blue Done button.</li> <li>Back on the Fusion Table page, go to the menu bar and select <strong>Tools &gt; Publish</strong>. Select the URL from the top text box (labeled "Send in an email or IM"), copy the URL and return to App Inventor. You will paste the URL into the definition block for the TABLE_URL (see below).</li> <li>The Table ID of the fusion table is contained within the URL after the plus sign. Look for "from+" and then copy all of the characters after the plus. This is the Table's ID. You can also find the Table ID by browsing to your table, then selecting <b>File&gt;About this table</b> in the menu.</li> </ol><h4 class="ai-header">Blocks Editor</h4> <p>Open the Blocks Editor so you can program the app's behavior. First, you will describe the app's variables. Variables whose names are ALL_CAPS are constants -- that is, variables whose values do not change while the program is running. It is good to get into the habit of using this naming convention. Define the following variables and give them the initial values shown in the table.</p> <table><tr style="background-color: #EFEFEF; font-weight: bold;"><td> Variable Name </td> <td> Initial Value </td> <td> Purpose </td> </tr><tr><td> <b>TABLE_URL</b> </td> <td>text</td> <td>Initialize this global variable to the "published" URL of your fusion table. See instructions above.</td> </tr><tr><td> <b>TABLE_ID</b> </td> <td>text</td> <td>Initialize this global variable to your table ID (e.g. a long string of characters unique to your fusion table). See instructions above.<font></font></td> </tr><tr></tr><tr><td> <b>API_KEY</b> </td> <td>text</td> <td>Initialize this global variable to your own API Key for Google Fusion Tables. See instructions above.</td> </tr><tr><td> <b>UserName</b> </td> <td>text</td> <td>Records the name of the user.</td> </tr><tr><td> <b>pizza</b> </td> <td>text</td> <td>Stores the pizza choice input by the user.</td> </tr><tr><td> <b>drink</b> </td> <td>text</td> <td>Stores the drink choice input by the user.</td> </tr><tr><td> <b>comment</b> </td> <td>text</td> <td>Stores the comment input by the user.</td> </tr></table><p><b>Initializing the App</b></p> <p>It is important to perform some initialization steps whenever the app is started. These are done in the <span class="basicblock">Screen1.Initialize</span> block. For this app we need to set the initial values for the FusionTable component's API Key property (set to global API_KEY) and the WebViewer component's HomeURL property (set to global TABLE_URL). We also tell the app to forget the user's login credentials. This will prompt the user to login to their Google account and give permission to the app to access the Fusion Table. This authentication step will happen only once when the app first tries to access the Fusion Table. Remember, Fusion Tables are only writeable by users who have been given permission by the table's owner. You specify this in the Sharing settings for the Fusion Table (easy to do from the Google Drive webpage.)</p> <p><img src="http://explore.appinventor.mit.edu/sites/all/files/tutorials/pizzaParty/pp_var_init.png" /></p> <p>Set up the <span class="callblock">resetForm</span> procedure as shown below. After recording an entry, this procedure resets the interface back to the original state.</p> <p><img src="http://explore.appinventor.mit.edu/sites/all/files/tutorials/pizzaParty/pp_reset_form.png" /></p> <p><b>List Picker Blocks</b></p> <p>In the designer, you set the choices for the pizza and drink types by filling in the "Selection" property with comma separated lists. These pre-programmed choices will be displayed on the user interface so the user can select their food and drink. Their selections are stored in the pizza and drink variables.</p> <p><img src="http://explore.appinventor.mit.edu/sites/all/files/tutorials/pizzaParty/listpicker.PNG" /></p> <p><b>Submitting Data</b><br /></p><p></p> <p>Once the user has entered their name, food choices, and comments, they will click the <b>Submit</b> button. The app tests to make sure that the name, pizza, and drink fields have values in them, and prompts the user to try again if any of the required answers are missing. Notice that the <span class="textblock">text =</span> block is used (find it under Built-in palette, Text drawer). This block compares two strings of text to see if they are equal. Do not use the math "equals" block. If all required information is present, it calls the procedure <span class="callblock">InsertDataInTable</span> (see below). The blocks for the <span class="basicblock">ButtonSubmit.Click</span> are shown here:</p> <p><img src="http://explore.appinventor.mit.edu/sites/all/files/tutorials/pizzaParty/pp_submit.png" /></p> <p><b>Inserting Data into the Fusion Table</b></p> <p>The FusiontablesControl component is used to send the data to the Fusion Table. This action will create a new row in the Fusion Table, setting the values of the various columns involved. App Inventor makes this easy to do, but you have to be careful that the insert query is formatted correctly.</p> <p>This procedure involves two steps: (1) constructing the insert query and then (2) sending the query to Google's Fusion Table service. The query we want to send will take this format:</p> <p>INSERT INTO<br /><tableid> (<column1>, <column2>, ...) VALUES (<value1>, <value2>, ...)</value2></value1></column2></column1></tableid></p> <p>The words in CAPS are part of the query's syntax. The words in parentheses are values that we need to plug in. First there is a list of (column names) followed by VALUES followed by a list of (value names). The order of the column names and value names must be in the same order so that they match up. An example of what this might look like is shown below. Notice that the values must be enclosed in single quotes:</p> <p>INSERT INTO 191GHtZ_B2 (Name, Date) VALUES ('Sam', '10/10/2012')</p> <p>First, setup a new <span class="callblock">Procedure With Result</span> that takes a string and returns it surrounded by single quotes. The procedure <span class="callblock">quotify</span> is used in the InsertDataInTable procedure to place quotes around all of the values in the query. It also takes care of "escaping" any single quotes or apostrophes that are input by the user. You can send single apostrophes as part of a value in the query, so the "replace all" block adds an extra single quote. Two single quotes in a row are interpreted as one single quote.</p> <p><img src="http://explore.appinventor.mit.edu/sites/all/files/tutorials/pizzaParty/pp_quotify.png" /></p> <p>To construct the query we use App Inventor's make text block. Be sure to put spaces where needed, such as before and after the words INSERT INTO and VALUES:</p> <p><img src="/sites/all/files/tutorials/pizzaParty/pp_insert_data.png" /></p> <p>For this app, the column names must match the column names of the table we created earlier (with columns Date, Name, Pizza, Drink, Comment). Their respective values are taken from the procedure's global variables. Note: If you did not use these exact words for your table's columns, then be sure to use your table's column names when you build your query.</p> <p>Don't forget the <span class="callblock">FusiontablesControl.SendQuery</span> command at the very end of this procedure. It's small in size compared to rest of the procedure, but very important.</p> <p><b>Handling the Response from the Fusion Tables Service</b></p> <p>The <span class="basicblock">FusiontablesControl.GotResult</span> event will be fired when the app receives a response from Google's Fusion Tables Service. For an insert query the service will return the rowID of the new row that was inserted or an error message if something went wrong. In this simple example, we use the "contains" block ( (find it under Built-in palette, Text drawer) to check whether the result string has the rowID in it. If so, then we know that the rowID was received, and we then invoke the <span class="callblock">WebViewer.GoHome</span> procedure, which reloads the "HomeURL" as specified in the WebViewer's properties. Note that this set of blocks also calls the <span class="callblock">resetForm</span> procedure. After recording an entry, it resets the interface back to the original state.</p> <p><img src="http://explore.appinventor.mit.edu/sites/all/files/tutorials/pizzaParty/pp_ft_gotresult.png" /></p> <p><b>You're done!</b> Package the app by going to <b>Package for Phone</b> on the Designer. You can now test the App for the purposes of the pizza party. Once you understand this tutorial, you'll be ready to make new Fusion Tables and modify the app to collect different types data from users. Remember, you have to give each user permission to access the Fusion Table. You do this through the Google Fusion Tables interface, not through App Inventor. Luckily, this means you don't have to change the app in order to add more users who can access the table.</p> <h4 class="ai-header">Challenges</h4> <p>This app has the Fusion Table hard-coded into the blocks. Find a way to let users specify their own fusion table ID so that they can host their own Pizza Party.</p> <h4 class="ai-header">Variations</h4> <p>Now that you have a simple app that uses fusion tables and a webviewer working, you might want to build some other apps with Fusion Tables. For example:</p> <ul><li>Include a <b>LocationSensor</b> so that the user's location can be added to the Fusion Table to create a map with notes</li> <li>Make the WebViewer display something other than the table of stored values; perhaps a map or a chart</li> </ul><h4 class="ai-header">Troubleshooting</h4> <p>If you are using a Google Apps for Education account, and you are not able to create a new fusion table (in the "Create" menu of Google Drive you won't even see an option for Fusion Tables), you will need to ask your system administrator to turn this option on for you. Or, you can switch to a standard gmail account. Fusion Tables are not automatically turned on for Google Apps for Education Accounts, your system administrator must make Fusion Tables available to the accounts in your domain.</p> <p>If you are receiving errors when trying to submit to the Fusion Table, especially if the error mentions Authentication, be sure that you have put the correct API Key into the API Key property field on the FusionTables component in the Design Window.</p> <p>To familiarize yourself with fusion tables, have a look around the <a href="http://support.google.com/fusiontables/bin/answer.py?hl=en&amp;answer=2571232">Fusion Tables Web Site</a>. Check out the example gallery to see what kinds of things are possible. Work through this <a href="http://earth.google.com/outreach/tutorial_fusion_sample.html">Fusion Table tutorial</a>, which shows how import some data, create a Fusion Table, and view the data on a map with your browser. You'll need to log in with your Google account.</p> <pre class="ai-box"><span style="color:black;">Done with PizzaParty?</span> <a href="/tutorials">Return to the other tutorials</a>.</pre><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/pizzaParty/PizzaParty.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> </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/data-storage" typeof="skos:Concept" property="rdfs:label skos:prefLabel" datatype="">Data Storage</a> </li> </ul> </section> Fri, 14 Jun 2013 16:09:56 +0000 aaron 311 at http://dev-explore.appinventor.mit.edu http://dev-explore.appinventor.mit.edu/content/pizzaparty#comments