The StageWebView Class

0
501

The flash.media.StageWebView uclass is a new class for displaying and interacting with rich HTML content in an AIR for Android application. It is a subclass of the EventDis patcher class.

The flash.media.StageWebView class uses the web control as provided by the native system that may vary slightly from one device to another. There is currently very little interaction between AIR and the HTML content.

StageWebView is not a display object, and is therefore not adding to the displayList. Create a StageWebView object and set its stage property to attach it directly to the stage. It always lies on top of your content, so if you want other objects to occupy the same view area, you must remove it first:

[code]

import flash.media.StageWebView;
var webView:StageWebView = new StageWebView();
webView.stage = this.stage;

[/code]

The size of the area dedicated to web content is defined in the viewPort property as a rectangle. Here it covers the stage width and 75% of the stage height:

[code]

import flash.geom.Rectangle;
var verticalBounds:int = stage.stageHeight*0.75;
webView.viewPort = new Rectangle(0, 0, stage.stageWidth, verticalBounds);

[/code]

To load content, call the loadURL method and pass the URL of the site. Add a listener so that in case an error occurs you can notify your user. Location is the event property for the site to load:

[code]

import flash.events.ErrorEvent;
webView.addEventListener(ErrorEvent.ERROR, onError);
webView.loadURL(“http://www.npr.org”);
function onError(event:ErrorEvent):void {
trace(“not able to reach location: “, event.location);
}

[/code]

Once the website is loaded, you should see it inside your application. It displays and responds as expected.

There are several events you can register to monitor activity and collect information.

You can register for the LocationChangeEvent.LOCATION_CHANGE event that is fired after a location has been reached:

[code]

import flash.events.LocationChangeEvent;
var webView:StageWebView = new StageWebView();
webView.stage = this.stage;
webView.addEventListener(LocationChangeEvent.LOCATION_CHANGE, onChange);
webView.loadURL(“http://www.npr.org”);
function onChange(event:LocationChangeEvent):void {
trace(“you are now at: “, event.location);
var verticalBounds:int = stage.stageHeight*0.75;
webView.viewPort = new Rectangle(0, 0, stage.stageWidth, verticalBounds);
}

[/code]

To avoid displaying an empty screen while loading, only display the view when its content is fully loaded and complete by listening to Event.COMPLETE.

You can register for the LocationChangeEvent.LOCATION_CHANGING event that is fired just before a new web location is requested. You can use this event in three different scenarios. You can prevent navigating to the new location by calling the preventDefault function. Most importantly, you need to catch the event to prevent it from opening the
native browser and leaving your application. Force the new location to load into Stage WebView and its viewPort area:

[code]

webView.addEventListener(LocationChanged.LOCATION_CHANGING, onChanging);
// prevent going to a new location
function onChanging(event:LocationChanged):void {
event.preventDefault();
trace(“sorry, you cannot go to: “, event.location);
}
// load new location in the StageWebView
function onChanging(event:LocationChanged):void {
event.preventDefault();
webView.load(event.location);
}

[/code]

If your application needs to know when StageWebView is in focus, register for the FOCUS_IN and FOCUS_OUT events. These events get dispatched when clicking inside or outside the rectangle area:

[code]

import flash.events.FocusEvent;

webView.addEventListener(FocusEvent.FOCUS_IN, inFocus);
webView.addEventListener(FocusEvent.FOCUS_OUT, outFocus);
function inFocus(event:FocusEvent):void {
trace(“on webview now”);
}
function outFocus(event:FocusEvent):void {
trace(“off webview now”);
}

[/code]

You can force focus when first launching your application:

[code]webView.assignFocus();[/code]

StageWebView has methods that mirror the functionality of a traditional browser toolbar. In fact, you could re-create a navigation user interface if you wanted to simulate the desktop experience.

The title and location properties return the page information. The stop and reload methods will, as their names indicate, stop the loading of a page or reload it.

The historyBack and historyForward methods load pages that were previously visited. Check that the isHistoryBackEnabled and isHistoryForwardEnabled properties are true to ensure the pages exist in either direction. Currently, the history is not available as a whole. If you want to access it, you must store it yourself as the user navigates
through pages.

As mentioned before, StageWebView is not added to the display list. To remove it, use the dispose method. Setting the object and its viewPort property to null is meant to aid in the garbage collection process:

[code]

webView.viewPort = null;
webView.dispose();
webView = null;

[/code]

Design Considerations

You should design your application for the best possible user experience. Toward that end, here are some points you should consider.

First, you have full control of the viewPort dimensions, but not its content. The vast majority of sites are designed only for the desktop. The few that deliver a mobile version take stage dimension into account and offer an adaptive layout and optimized content.

You can reduce the locations to a limited choice using the preventDefault method as previously discussed. If you want to keep the user experience open, prepare your application to accommodate multiple designs as much as possible.

Present your application in landscape mode and full screen. Align your application to the upper-left corner. This will mirror the web experience and users’ expectations:

[code]

import flash.display.StageAlign;
stage.align = StageAlign.TOP_LEFT;

[/code]

If the web page is larger than the viewPort, the StageWebView displays a scroll bar and zoom controls. There is no option to suppress these controls.

If you choose to keep auto-rotation enabled, you need to set a listener for the stage Event.RESIZE event and prevent scaling:

[code]

import flash.display.StageScaleMode;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.addEventListener(Event.RESIZE, onStageResized);

[/code]

When the screen rotates, re-create the viewPort at the appropriate dimension. It does not resize dynamically:

[code]

import flash.events.Event;
function onStageResized(event:Event):void {
webView.viewPort = null;
var verticalBounds:int = stage.stageHeight*0.75;
webView.viewPort =
new Rectangle(0, 0, stage.stageWidth, verticalBounds);
}

[/code]

If you navigate between locations, the native back button takes you back to the previous open application or home. It does not work as a back button for StageWebView. This is the expected behavior, of course, because the Internet content is part of your application, not a separate intent.

Either provide an internal back button or override the native back button. Do consider that this is not a recommended Android practice. It is not possible to put elements on top of StageWebView, but you can simulate a top navigation above it.

The drawViewPortToBitmap function can be used to take a screen capture of the Stage WebView. You can take advantage of this feature to temporarily replace StageWebView with a bitmap and display other elements on top of it.

Local Use

You can package HTML files with your application and then load them inside Stage WebView. You can use this to display a help page for your users, or other HTML content.

Copy the HTML page into a temporary file and load it using its url:

[code]

import flash.filesystem.File;
var file:File = File.applicationDirectory.resolvePath(“assets/test.html”);
var local:File = File.createTempFile();
file.copyTo(local, true);
webView.loadURL(local.url);

[/code]

Note that it is possible to load a local HTML file to make an Ajax XMLHttpRequest on another domain or to make JavaScript calls, as demonstrated in the next paragraph.

The loadString method is for displaying HTML formatted text in the viewPort area. The text must be fully HTML formatted:

[code]

// define the string in html format
var myString:String =
“<html>”
+ “<body bgcolor=”#FFFF33″>”
+ “<font color=”#FF0000″>Hello</font color><br>”
+ “<font color=”#FFFFFF”>Look at me</font color><br><br>”
+ “<a href=”http://www.google.com”>Click Me</a>”
+ “</body>”
+ “</html>”
webView.loadString(myString, “text/html”);
webView.addEventListener(LocationChangeEvent.LOCATION_CHANGING, onChanging);
// load the Google site into the viewport when clicking on the text
function onChanging(event:LocationChangeEvent):void {
event.preventDefault();
webView.loadURL(event.location);
}

[/code]

At the time of this writing, this feature is limited and does not provide much more than a TextField with htmlText would provide. You cannot, for instance, use it to display local assets such as images. It is useful if you want text placed in the StageWebView area.

Mobile Ads

Local use in combination with JavaScript is handy when dealing with mobile ads.

At the time of this writing, mobile advertising providers do not offer an AS3 SDK. Rewriting their AS2 code into AS3 to use in AIR does not seem to get ad impressions. Using StageWebView to simulate the browser model seems to be the best solution.

Go to http://developer.admob.com/wiki/Android or http://developer.admob.com/wiki/Requests for instructions on how to set up an ad request for AdMob before we adapt them for AIR for Android.

You need an embedded HTML page that makes the request to the ad provider with your publisher ID and the StageWebView to load the page and monitor the navigation. The HTML page contains a JavaScript script that fetches the ad. It is important to set manual_mode to true. Set test to true during development or to false to receive live ads.

[code]

<html>
<head>
<title>Get Ad</title>
<script type=”text/javascript”>
var admob_vars = {pubid: ‘YOUR_ID’,
bgcolor: ‘FFFFFF’
text: ‘000000’
test: true,
manual_mode: true
};
function displayAd() {
admob.fetchAd(document.getElementById(‘ad_space’));
}
</script>
<script type=”text/javascript”
src=http://mm.admob.com/static/iphone/iadmob.js></script>
</head>
<body onload=”displayAd()”>
<div id=”ad_space”></div>
</body>
</html>

[/code]

Figure 13-1. AdMob ads with the test property set to true (top) and false (bottom)
Figure 13-1. AdMob ads with the test property set to true (top) and false (bottom)

The ActionScript looks like this. Put your ad request in a try catch block in case the ad provider site is down or it sends back invalid data:

[code]

import flash.media.StageWebView;
import flash.geom.Rectangle;
import flash.events.LocationChangeEvent;
import flash.events.ErrorEvent;
import flash.filesystem.File;
import flash.filesystem.FileMode;
import flash.filesystem.FileStream;
import flash.media.StageWebView;
import flash.net.navigateToURL;
import flash.net.URLRequest;

var view:StageWebView;
var local:File;
view = new StageWebView();
view.stage = this.stage;
view.addEventListener(LocationChangeEvent.LOCATION_CHANGE, onChange);
view.addEventListener(LocationChangeEvent.LOCATION_CHANGING, onChanging);
view.addEventListener(ErrorEvent.ERROR, onViewError);
view.viewPort = new Rectangle(0, 0, 480, 60);

var base:File = File.applicationDirectory.resolvePath(“adView.html”);
local = File.createTempFile();
base.copy(local, true);
try {
view.loadURL(local.url);
} catch(error:Error) {
}
function onChanging(event:LocationChangeEvent):void {
event.preventDefault();
navigateToURL(new URLRequest(event.location));
}
// when the user clicks on the ad
function onChange(event:LocationChangeEvent):void {
if (event.location != local.url) {
navigateToURL(new URLRequest(event.location));
try {
view.loadURL(local.url);
} catch(error:Error) {
}
}
}
function onViewError(error:ErrorEvent):void {
trace(error);
}
[/code]

Services and Authentication

Many services, such as Twitter and Facebook, require an authentication token that can only be obtained via an HTML web page. You can use StageWebView to call authentication methods to extract the token. Service data is then available and can be used inside your AIR application.

OAuth is a robust open standard authorization system. It allows users to hand out tokens instead of usernames and passwords. The token is for a specific site and resources for a defined duration.

Twitter uses OAuth. You can allow users to access their account for authentication and log in to your application so that they can use their account data seamlessly without leaving AIR. You need to register a URL on Twitter to obtain a Consumer key and a Consumer secret, both of which are necessary in your application (see http://twitter.com/oauth/ for more information).

Sandro Ducceschi offers a library called Tweetr that supports pinless OAuth (see http://wiki.swfjunkie.com/tweetr). For a similar application for Facebook, refer to Mark Doherty’s example at http://www.flashmobileblog.com/2010/10/14/facebook-connect-with-air-on-android/.

Limitations

Despite its benefits, StageWebView also has some limitations:

  • You cannot layer elements on top of StageWebView, although you can simulate it by taking a screen grab as described earlier.
  • The interaction between ActionScript and JavaScript is very limited. There is currently no support for ExternalInterface, even though you can fake URLs to communicate from HTML to AS.
  • You should not use StageWebView as a substitute for the native browser. Use it only as a bridge between your application and some needed Internet content. Android devices, for instance, do not support the QuickTime codec.
  • There is no direct way in ActionScript to prevent StageWebView instances from accepting cookies or to clear cookies.
  • The HTMLLoader class is supported in AIR for the desktop but not in AIR for Android.

If your application requires more advanced HTML features, consider using other tools. PhoneGap (http://www.phonegap.com), for instance, is an open source framework for developing applications for Android, iPhone, iPad, BlackBerry, and Symbian devices.