This tutorial is written by Rich Interdonato, MIT Master Trainer.
App Inventor allows users to write powerful programs using blocks instead of traditional programming languages. It also allows advanced users to include traditional Java code in their apps by means of its new Extensions feature. What you might not know is that there is another, even older way for you to incorporate traditional programming into your apps. The technique involves using the WebViewer component as a javascript processor, and this blog post will show you how it can be done using a simple example. In it, an HTML file is sent input text from an App Inventor program, it reverses the text using javascript, and then it sends the result back to the App Inventor program as output that the app can use (show to the user in a Message Dialog).
Our example requires an HTML file that includes javascript that can reverse text. The file is included below, and the javascript command that reverses the text is highlighted in
yellow. The javascript in this file is like the other javascript you might find on the Web, except in one way. It includes two special App Inventor Only functions,
window.AppInventor.getWebViewString() and
window.AppInventor.setWebViewString(). It is these two, App Inventor Only functions that allow your apps to communicate with the javascript that runs inside the WebViewer component. By using them creatively, you can leverage the vast number of javascript programs that are freely available on the Web, and some of them can be really useful.
For now, lets keep things simple and create a file called
javascriptWebViewProcessor.html. The complete text of the file follows:
HTML file (included as media/asset: javascriptWebViewProcessor.html)
<!doctype html>
<head>
<title>WebView Javascript Processor</title>
</head>
<body onload="processJavascript();">
<b>This page includes a javascript function that reverses text.</b>
<p>The javascript function incorporates a special App Inventor feature called <i>window.AppInventor.getWebViewString()</i>, which allows App Inventor apps to communicate with the WebViewer component's internal processing of javascript.
<p>This simple example shows how to use the <i>window.AppInventor.getWebViewString()</i> function to pass data to and from the WebViewer component, and thereby an App Inventor app.
<script>
var result = new Date().toString();
var appInventorInput = window.AppInventor.getWebViewString();
function processJavascript() {
if (appInventorInput.length > 0) {
document.write( "WebView InputString received FROM app:
" + appInventorInput );
result = appInventorInput.split("").reverse().join("");
document.write( "<p/>WebView InputString sending BACK TO app:<br/>" +
result );
} else {
document.write( "No WebView InputString set by app at: <br/>" + result );
}
window.AppInventor.setWebViewString( result );
}
</script>
</body>
</html>
Designer
Once the
javascriptWebViewProcessor.html has been created, make a new App Inventor app called SimpleWebviewerJavascriptProcessor, and upload the HTML file as an app media asset. Add a HorizontalArrangement with a:
- TextBox named StringToBeProcessedByJavascriptFile
- Button named btnProcess
Also, using the default names and properties for each, add a:
- WebViewer component
- Notifier component
- Clock component
Blocks Overview
As you can see, the blocks for this app are quite simple. There are 4 variables and 3 event handlers. Each will be explained in individual sections.
Variables
processingIntervalMillis: sets the frequency of calls to the WebViewer component from the App Inventor app
debugMode: used to specify the location of the HTML file that is used by the WebViewer because App Inventor apps use different directories for development and running apps
currentProcessingDurationMillis: tracks the amount of time (in milliseconds) that the WebViewer has been working as it processes the javascript in the HTML file
maximumProcessingDurationMillis: controls the number of milliseconds that the app will wait for a reply from the WebViewer component before determining that it is unresponsive
Screen1.Initialize
This block sets the
WebViewer1.HomeUrl property to either
file:///mnt/sdcard/AppInventor/assets/javascriptWebViewProcessor.html (when
debugMode is TRUE), or
file:///android_asset/javascriptWebViewProcessor.html (when
debugMode is FALSE). It then sets the
Clock1.TimerInterval property to the value of
processingIntervalMillis (50ms), and disables the
Clock1 component so it does not start triggering events as soon as the app starts.
btnProcess.Click
The
javascriptWebViewProcessor.html file processing begins once the user activates the app. This activation is captured by the
btnProcess.Click event handler block, which sets the
WebViewer1.WebViewString component to the value of the
StringToBeProceesedByJavascriptFile.Text. Then the
WebViewer1.GoHome procedure is called, which causes the WebViewer to load the
javascriptWebViewProcessor.html page, thereby starting the javascript processing. Once the javascript processing has begun, the app needs to monitor progress, and so the
Clock1.TimerEnabled property is set to TRUE.
Clock1.Timer
As soon as the
Clock1.TimerEnabled property is set to TRUE, the
Clock1.Timer event begins to fire at an interval set by the
processingIntervalMillis value in the
Screen1.Initialize event. Each time the
Clock1.Timer event is triggered, the app
first checks to see if the value of the
WebViewer1.WebViewString is the same as the
StringToBeProcessedByJavascriptFile.Text. If they are the same, this means that the WebViewer component is still processing the javascript, and it increments the
currentProcessingDurationMillis by the
processingIntervalMillis and then checks to see if the new
currentProcessingDurationMillis value is greater than or equal to the
maximumProcessingDurationMillis value. If it is, the app changes to value of the
WebViewer1.WebViewString property (by concatenating adds the text TIMEOUT to the current value of
StringToBeProcessedByJavascriptFile.Text to make it different from the value of the
StringToBeProcessedByJavascriptFile.Text, and thereby ensure that the next time the
Clock1.Timer event is triggered, the
first check will fail.
As soon as the
first check fails (i.e.
WebViewer1.WebViewString differs from
StringToBeProcessedByJavascriptFile.Text), the app will reset the
StringToBeProcessedByJavascriptFile.Text to an empty string (), set
Clock1.TimerEnabled to FALSE, and then display a message to the user in a Message Dialog that includes the result of the javascript processing, as well as how long it took to do it. The output of the processing is available to the app in the
WebViewer1.WebViewString property, which is set inside the javascript processing file. As a final cleanup action, the
currentProcessingDurationMillis is reset to 0, so the next time the user clicks on
btnProcess, the app will properly calculate the processing duration.
Conclusion
In this article, we have explored how the WebViewer component can be used to process javascript in your App Inventor apps. Our example was simple, but it demonstrated the fact that App Inventor can communicate with running javascript, and this provides you with a foundation for more advanced and useful javascript processing. There is a LOT of javascript code available on the web, and some of it can be used immediately by you to implement advanced features in your apps. In my next blog post, I will explore an advanced application of WebView javascript processing that you can use right away to freely make your apps much more secure. Until then, keep inventing!