Loading Swf with bytearray in AIR - apache-flex

We have requirement with the AIR application which loads the flex generated swf which inturn loads the flash generated swf using SWFLoader. This is not working as desired. This gives the following error:
SecurityError: Error #3226: Cannot import a SWF file when LoaderContext.allowCodeImport is false.
This is our AIR application.
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="initApp()">
<mx:Script>
<![CDATA[
import mx.controls.SWFLoader;
[Embed(source="FlexLoadingFlash.swf")]
public var flexMovie:Class;
private function initApp():void {
// First convert the Swf into MovieClip
var movieclip:MovieClip = new flexMovie();
// get the byteArray from movieClip
var byteArray:ByteArray = movieclip.movieClipData;
var swfLoader:SWFLoader = new SWFLoader();
// load bytearray into swfLoader
swfLoader.source = byteArray;
swfLoader.maintainAspectRatio = false;
swfLoader.percentHeight = vbox.height;
swfLoader.percentWidth = vbox.width;
swfLoader.invalidateDisplayList();
swfLoader.invalidateSize();
// now add the swfloader into container
vbox.addChild(swfLoader);
}
]]>
</mx:Script>
<mx:VBox id="vbox" width="100%" height="100%" verticalCenter="0" horizontalCenter="0" cacheAsBitmap="true" >
</mx:VBox>
</mx:WindowedApplication>
Please let me know how can we fix this issue.

Use Loader.loadBytes() to load your SWF. Create an instance of LoaderContext. loadBytes method takes a LoaderContext instance as a parameter. Set the allowCodeImport property of your LoaderContext instance to true and it should work

Or you can just add these three lines before you set the source
var loaderContext: LoaderContext = new LoaderContext();
loaderContext.allowLoadBytesCodeExecution = true;
swfLoader.loaderContext = loaderContext;

<mx:SWFLoader id="swfObj"
width="100%" height="100%"
complete="swfObj_completeHandler(event)"/>
<fx:Script>
<![CDATA[
[Bindable]
[Embed(source="assets/soundbar.swf")]
private static var swfClass:Class;
private var swfSoundBar : MovieClip;
[Bindable] private var mp3Player:MP3Player = MP3Player.getInstance();
protected function init(event:FlexEvent):void
{
swfSoundBar = new swfClass();
var byteArray:ByteArray = swfSoundBar.movieClipData;
var loaderContext: LoaderContext = new LoaderContext();
loaderContext.allowLoadBytesCodeExecution = true;
swfObj.loaderContext = loaderContext;
swfObj.source = byteArray;
}
protected function swfObj_completeHandler(event:Event):void
{
swfSoundBar = SWFLoader(event.target).content as MovieClip;
swfSoundBar.width = 32;
swfSoundBar.height = 14;
swfSoundBarShowHide();
}
protected function swfSoundBarShowHide():void
{
if (swfSoundBar){
if (mp3Player.isPlaying){
swfSoundBar.gotoAndStop(0);
swfSoundBar.stop();
} else {
swfSoundBar.gotoAndPlay(0);
}
}
}
]]>
</fx:Script>

Related

How to position spark List control below StageWebView in Flex Mobile

I am developing mobile application for iOS and Android using Flex Mobile. On one of the views I am displaying StageWebView with a Google map and a spark List control to display another data. I am using the StageWebView in order to benefit from Google Maps JavaScript API v3. Example code below:
<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark" title="Clubs" backgroundAlpha="0"
viewActivate="view1_viewActivateHandler(event)"
backKeyPressed="view1_backKeyPressedHandler(event)"
initialize="view1_initializeHandler(event)">
<fx:Script>
<![CDATA[
import flash.sensors.Geolocation;
import mx.collections.ArrayCollection;
import mx.events.FlexEvent;
import spark.events.ViewNavigatorEvent;
private var myWebView:StageWebView;
[Bindable]
private var locations:ArrayCollection;
private var geolocation:Geolocation;
protected function view1_initializeHandler(event:FlexEvent):void
{
myWebView = new StageWebView();
myWebView.stage = this.stage;
}
protected function view1_viewActivateHandler(event:ViewNavigatorEvent):void
{
if(Geolocation.isSupported)
{
geolocation = new Geolocation();
geolocation.addEventListener(GeolocationEvent.UPDATE, onGeolocationChange);
}
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if (myWebView) {
var point:Point = (new Point());
point = localToGlobal(point);
myWebView.viewPort = new Rectangle(point.x,point.y, stage.width,stage.height/3);
}
}
protected function view1_backKeyPressedHandler(event:Event):void
{
if (myWebView)
{
myWebView.viewPort = null;
myWebView = null;
}
navigator.popView();
}
protected function onGeolocationChange(event:GeolocationEvent):void
{
geolocation.removeEventListener(GeolocationEvent.UPDATE, onGeolocationChange);
locations = new ArrayCollection();
var location0:Object = new Object();
location0.lat = event.latitude;
location0.long = event.longitude;
locations.addItem(location0);
var location1:Object = new Object();
location1.lat = "42.697325";
location1.long = "23.315364";
locations.addItem(location1);
var location2:Object = new Object();
location2.lat = "42.696441";
location2.long = "23.321028";
locations.addItem(location2);
var url:String = "http://whozzzin.dev.mediatecture.at/gmapsformobile/map.php";
var counter:Number = 1;
for each(var location:Object in locations)
{
if(counter == 1)
{
url += "?locations["+counter.toString()+"][lat] = " + location.lat;
url += "&locations["+counter.toString()+"][long] = " + location.long;
}
else
{
url += "&locations["+counter.toString()+"][lat] = " + location.lat;
url += "&locations["+counter.toString()+"][long] = " + location.long;
}
counter++;
}
myWebView.loadURL(url);
}
]]>
</fx:Script>
<s:navigationContent>
<s:Button includeInLayout="{Capabilities.version.indexOf('IOS') > -1}" visible="{Capabilities.version.indexOf('IOS') > -1}" id="backButton" label="BACK" click="view1_backKeyPressedHandler(event)"/>
</s:navigationContent>
<s:List width="100%" contentBackgroundAlpha="0" id="placesList" dataProvider="{locations}" labelField="lat">
<s:layout>
<s:TileLayout columnWidth="{(width - 16)/3}"/>
</s:layout>
</s:List>
</s:View>
Currently the list is not visible because it appears behind the StageWebView. My question is how to position the List control exactly after the WebStageView.
You can use the top constraint:
<s:List width="100%" top="{stage.height/3}" id="placesList"
Edit: Put in a trace(stage.height); in your updateDisplayList function and also look at stage.stageHeight. Those values should help you figure out the exact top value to use in this case.
How I solved it:
I get the bottom of the WebStageView and convert a new point from global to local with x =0 and y = myWebView.bootom:
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if (myWebView) {
var point:Point = (new Point(0,this.radiusDropDown.height));
point = localToGlobal(point);
myWebView.viewPort = new Rectangle(point.x,point.y, stage.width,stage.height/3);
this.placesList.y = globalToLocal(new Point(0,myWebView.viewPort.bottom)).y;
}
}

DataBinding in List

I am trying to bind data within a ArrayList to a list, but here only the last element shows up on the list(99), not the entire contents of the arraylist.
private function completeHandler(event:Event):void
{
var xmlData:XML = XML(event.target.data);
trace(xmlData);
var i:int = 0;
for (i;i<100;i++)
{
var arr:ArrayList = new ArrayList();
arr.addItem(i);
trace(arr);
}
list.dataProvider = arr;
}
I am not able to figure out what to do here?
You're creating an ArrayList with one item a 100 times. Replace with this and you should be fine:
var arr:ArrayList = new ArrayList();
for (var i:int = 0; i<100; i++) {
arr.addItem(i);
}
Or better yet, just wrap your XML in an XMLListCollection instead of copying the nodes one by one (assuming it is the actual content of the XML data you want instead of the indices):
private function completeHandler(event:Event):void
{
var xmlData:XML = XML(event.target.data);
list.dataProvider = new XMLListCollection(xmlData.children());
}
(Note that this is not DataBinding: it is just setting the dataProvider property)
check this code this will help you,
you can navigate through all data on the basis of rowcount of list....
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" minWidth="955" minHeight="600">
<mx:Script>
<![CDATA[
import mx.collections.ArrayCollection;
[Bindable]private var _index:int = 0;
private var _coll:ArrayCollection = new ArrayCollection([{name:'ashish',age:'28'},{name:'abhi',age:'29'},{name:'kunal',age:'27'},
{name:'ashish1',age:'28'},{name:'abhi1',age:'29'},{name:'kunal1',age:'27'},
{name:'ashish2',age:'28'},{name:'abhi2',age:'29'},{name:'kunal2',age:'27'},
{name:'ashish3',age:'28'},{name:'abhi3',age:'29'},{name:'kunal3',age:'27'}]);
protected function button1_clickHandler(event:MouseEvent):void
{
// TODO Auto-generated method stub
if((_index-li.rowCount>=0))
_index = _index - li.rowCount;
}
protected function button2_clickHandler(event:MouseEvent):void
{
// TODO Auto-generated method stub
if((_index+li.rowCount<_coll.length))
_index = _index + li.rowCount;
}
]]>
</mx:Script>
<mx:List id="li" dataProvider="{_coll.source.slice(_index,(_index+li.rowCount))}" labelField="name" rowCount="3" width="100"/>
<mx:HBox>
<mx:Button label="<-" click="button1_clickHandler(event)"/>
<mx:Button label="->" click="button2_clickHandler(event)"/>
</mx:HBox>
</mx:Application>

Get ByteArray from external asset image synchronously in Flex

I have a var that holds the String value for the path to an image.
How can I use that to get the ByteArray from that image synchronously?
Thank you.
You can't do it synchronously. But here is how you do it asynchronously.
<?xml version="1.0" encoding="utf-8"?>
<fx:Script>
<![CDATA[
public function init():void {
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
loader.load(new URLRequest(encodeURI("http://www.google.com/logos/2011/mary_blair-2011-hp.jpg")));
}
private function onComplete(e:Event)
{
//of course if all you were doing is displaying an image its better to do:
//image.source = e.currentTarget.content;
var bytes:ByteArray = LoaderInfo(e.currentTarget).bytes;
//binary output
trace(bytes);
var imageLoader:Loader = new Loader();
imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageComplete);
imageLoader.loadBytes(bytes, null);
}
private function onImageComplete(e:Event):void
{
image.source = LoaderInfo(e.currentTarget).content;
}
]]>
</fx:Script>
<s:Image id="image" />

ItemRenderer Switching URLLoader Images

I don't even know how to explain this behavior but I'll try. I am loading images from an external url that requires basic auth so I am using URLLoader to load the image from a unique ID. The ID gets passed to the itemrenderer which then proceeds to load the image. But the images switch around on their own when I scroll. If I load more than 7 images or so it starts repeating images....
Youtube video of error:
http://www.youtube.com/watch?v=ZYoqlS14gWQ
Relevant code:
<s:ItemRenderer name="RandomItemRenderer" creationComplete="init();"
xmlns:fx="http://ns.adobe.com/mxml/2009"
xmlns:s="library://ns.adobe.com/flex/spark"
xmlns:mx="library://ns.adobe.com/flex/mx"
autoDrawBackground="false">
<s:states>
<s:State name="normal" />
<s:State name="hovered" />
<s:State name="selected" />
</s:states>
<fx:Script>
<![CDATA[
import flash.net.URLLoader;
import flash.net.URLLoaderDataFormat;
import flash.net.URLRequest;
import flash.net.URLRequestHeader;
import flash.net.URLRequestMethod;
import mx.utils.ObjectProxy;
import customclasses.Settings;
[Bindable] private var coverArtImage:Image;
private var myCoverArtLoader:URLLoader;
[Bindable] private var coverArtSource:String;
private function init():void {
get_coverArt();
}
private function get_coverArt(): void {
if (!data.coverArt) {
set_nullCoverArt();
} else {
var requestString:String = "/rest/getCoverArt.view?v=1.5.0&c=AirSub&id=" + data.coverArt;
var requestURL:String = Settings.ServerURL + requestString;
myCoverArtLoader = new URLLoader();
var myRequest:URLRequest = new URLRequest();
var authHeader:URLRequestHeader = new URLRequestHeader();
authHeader.name = 'Authorization';
authHeader.value = 'Basic ' + Settings.EncryptedCreds();
myRequest.requestHeaders.push(authHeader);
myRequest.url = requestURL;
myRequest.method = URLRequestMethod.GET;
myCoverArtLoader.dataFormat = URLLoaderDataFormat.BINARY;
myCoverArtLoader.addEventListener(Event.COMPLETE, set_coverArt);
myCoverArtLoader.addEventListener(IOErrorEvent.IO_ERROR, set_failedCoverArt);
myCoverArtLoader.load(myRequest);
}
}
private function set_coverArt(evt:Event) : void {
coverArtImage = new Image();
coverArtImage.source = myCoverArtLoader.data;
myCoverArtLoader.removeEventListener(Event.COMPLETE, set_coverArt);
}
private function set_nullCoverArt() : void {
coverArtImage = new Image();
coverArtImage.source = "assets/nullCoverArt.jpg";
}
private function set_failedCoverArt() : void {
coverArtImage = new Image();
coverArtImage.source = "assets/nullCoverArt.jpg";
myCoverArtLoader.addEventListener(IOErrorEvent.IO_ERROR, set_nullCoverArt);
}
]]>
</fx:Script>
<s:Image source.normal="assets/coverOutline.png" source.selected="assets/coverOutlineYellow.png" source.hovered="assets/coverOutlineYellow.png"
height="226" width="226" />
<s:VGroup top="4.5" bottom="5" width="200" horizontalAlign="center" letterSpacing="10"
paddingBottom="5" paddingTop="9" verticalAlign="middle" x="13.5">
<s:Image id="ui_imgCoverArt" width="200" height="200" source="{coverArtImage.source}"/>
<s:Label text="{data.title}" width="160" styleName="RandomList" />
</s:VGroup>
ItemRenderers are reusable and cached, i.e. there are only limited count created in List to fill its area (rowCount +- couple). And when you scroll, new renderers are not instantiated, instead the one renderer that was scrolled out goes up or down and is filled with new data.
That's why you can not rely on creationComplete event, it will be fired only once for each instance of renderer.
The solution is to override data setter and build there the behaviour needed:
override public function set data(value:Object):void
{
super.data = value;
get_coverArt();
}
Useful link: How flex itemRenderer works ? (their life cycle)

Does Flex support the data uri scheme?

I wish to pass many small PNG files as base64 encoded URIs within an XML response, but there seems to be no way to make flex present these images. I was thinking of the data uri scheme, but it appears not to be supported.
Proposed solutions
Use Loader.LoadBytes
Tried it and it doesn't seem to work (none of the events are triggered).
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" width="1276" height="849" creationComplete="drawImage()">
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.utils.Base64Decoder;
private function loaderCompleteHandler(event:Event):void {
Alert.show("loader done");
}
private function errorHandler(e:IOErrorEvent):void {
Alert.show("error" + e.toString());
}
public function drawImage() : void
{
var b64png : String = "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IAAAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1JREFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jqch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0vr4MkhoXe0rZigAAAABJRU5ErkJggg==";
var l : Loader = new Loader();
var decoder : Base64Decoder = new Base64Decoder();
decoder.decode(b64png);
var bytes : ByteArray = decoder.flush();
l.addEventListener(Event.COMPLETE, loaderCompleteHandler);
l.addEventListener(IOErrorEvent.IO_ERROR, errorHandler);
l.loadBytes(bytes);
}
]]>
</mx:Script>
<mx:Image x="10" y="10" width="155" height="118" id="image1"/>
</mx:Application>
Can someone please tell me what I did wrong?
If you decode the image data into a ByteArray then you can use Loader.loadBytes(byteArray) to load it as an image.
You could use something this to load the image:
var deco64:Base64Decoder = new Base64Decoder;
deco64.decode("base64StringWithTheImageData");
var arrBytes:ByteArray = deco64.toByteArray();
img.load(arrBytes);
Hope this helps!
You can assign the byte array returned from the decoder directly to the image's source property.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.utils.Base64Decoder;
private function init():void {
var b64png : String = "iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9YGARc5KB0XV+IAAAAddEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAF1JREFUGNO9zL0NglAAxPEfdLTs4BZM4DIO4C7OwQg2JoQ9LE1exdlYvBBeZ7jqch9//q1uH4TLzw4d6+ErXMMcXuHWxId3KOETnnXXV6MJpcq2MLaI97CER3N0vr4MkhoXe0rZigAAAABJRU5ErkJggg==";
var decoder : Base64Decoder = new Base64Decoder();
decoder.decode(b64png);
var bytes : ByteArray = decoder.flush();
img.source = bytes;
}
]]>
</mx:Script>
<mx:Image id="img" />
</mx:Application>

Resources