<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="http://feedproxy.google.com/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feedproxy.google.com/~d/styles/itemcontent.css"?><rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">

<channel>
	<title>The Tech Labs » AIR</title>
	
	<link>http://www.thetechlabs.com</link>
	<description>Adobe Air, Flash and Flex Tutorials</description>
	<pubDate>Thu, 23 Oct 2008 22:58:12 +0000</pubDate>
	<generator>http://wordpress.org/?v=abc</generator>
	<language>en</language>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feedproxy.google.com/TheTechLabs_air" type="application/rss+xml" /><item>
		<title>Creating a AIR Web Service Package with Flash and AS3- Base Service Class And Simple Twitter API</title>
		<link>http://feedproxy.google.com/~r/TheTechLabs_air/~3/9GmCJYhawBo/</link>
		<comments>http://www.thetechlabs.com/flash/creating-a-as3-web-service-package-base-service-class-and-simple-twitter-api/#comments</comments>
		<pubDate>Wed, 03 Sep 2008 22:18:47 +0000</pubDate>
		<dc:creator>Reynaldo Columna</dc:creator>
		
		<category><![CDATA[AIR]]></category>

		<category><![CDATA[Flash]]></category>

		<category><![CDATA[Latest]]></category>

		<category><![CDATA[Webservices]]></category>

		<category><![CDATA[adobe air]]></category>

		<category><![CDATA[adobe flash]]></category>

		<category><![CDATA[API]]></category>

		<category><![CDATA[as3]]></category>

		<category><![CDATA[how-to]]></category>

		<category><![CDATA[tutorial]]></category>

		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://www.thetechlabs.com/?p=99</guid>
		<description><![CDATA[<p>In this series of tutorials, you will learn the basics of architecting a simple but powerful Web Services Package. We&#8217;ll start off by creating a class which will be the base of all our services and then continue to create a very simple Twitter API.</p>
<small><em>posted in <a href="http://www.thetechlabs.com/category/air/">AIR</a> by Reynaldo Columna <a href="http://www.thetechlabs.com/flash/creating-a-as3-web-service-package-base-service-class-and-simple-twitter-api/#comments">Leave A Comment</a><br />&copy;2008 <a href="http://www.thetechlabs.com">The Tech Labs</a>. All Rights Reserved.</em></small>]]></description>
			<content:encoded><![CDATA[<p>In this series of tutorials, you will learn the basics of architecting a simple but powerful Web Services Package. We&#8217;ll start off by creating a class which will be the base of all our services and then continue to create a very simple Twitter API.<em><strong></strong></em></p>
<p><em><strong>Please note that due to changes in the Twitter cross domain policy, the API we will create in this tutorial will not work online, but works perfectly in an AIR application.</strong></em></p>
<h3>Requirements</h3>
<p><strong>Adobe Flash CS3</strong></p>
<p><a title="Try / buy" onclick="javascript:pageTracker._trackPageview('/outbound/article/http://www.adobe.com/products/flash/');" href="http://www.adobe.com/products/flash/" onclick="javascript:pageTracker._trackPageview('oclicks/http://www.adobe.com/products/flash/');" target="_blank">Try / Buy</a></p>
<p><strong>Adobe Intergrated Runtime (AIR) for Flash CS3</strong></p>
<p><a title="Download AIR for Flash CS3" href="http://www.adobe.com/products/air/develop/flash/" onclick="javascript:pageTracker._trackPageview('oclicks/http://www.adobe.com/products/air/develop/flash/');" target="_blank">Download</a></p>
<p><strong>Source Files</strong></p>
<p><a title="Download Source Files" onclick="javascript:pageTracker._trackPageview('/downloads/tutorials/files/flash/fcarrera/flickrsearchengine/flickrsearchengine_sfiles.zip');" href="http://thetechlabs.com/tutorials/files/flash/Reynaldo/Webservice/AIR Webservice Tutorial.zip" onclick="javascript:pageTracker._trackPageview('downloads/tutorials/files/flash/Reynaldo/Webservice/AIR Webservice Tutorial.zip');" target="_self">Download</a></p>
<h3>Pre-Requesites</h3>
<p>Some experience in creating classes in AS3, if you dont know how to, don&#8217;t worry, you&#8217;ll learn here <img src='http://www.thetechlabs.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<h3>Create the package</h3>
<p>The first thing we need to do before actually coding anything is to create our class package. Creating a class package is very simple, we&#8217;ll create it right on our desktop. All you have to do is create a folder and name it &#8220;com&#8221;, that will be the main package. inthe &#8220;com&#8221; folder, create another folder identifying the name of your webservices collection, I&#8217;ll call mine &#8220;reyco1&#8243;. now in the &#8220;reyco1&#8243; folder create another folder called &#8220;webservices&#8221;. That&#8217;s it! package creation complete. Now how easy was that? Very!</p>
<p>Now create create an Actionscript file, name it &#8220;WebService.as&#8221; and save it in the &#8220;webservices&#8221; folder. This is the class that all our apis, such as Twitter API will use to connect to their respective web services. Crack that puppy open and lets get to the coding goodness!</p>
<h3>Coding the main WebService class</h3>
<p>All AS3 classes are contained within a package. We already created our package in the first step, basically a few nested folders. So we&#8217;ll start our class as so:</p>
<pre>package com.reyco1.webservice
{

}</pre>
<p>Now we need to import the classes that we need from flash in order to make our class work:</p>
<pre>package com.reyco1.webservice
{
	import flash.events.*;
	import flash.net.*;
}</pre>
<p>Our class will extend the &#8220;EventDispatcher&#8221; class which means that we will be able to dispatch much needed events. It will have additional properties and methods which we will include in later steps. In the meantime your class should look like this:</p>
<pre>package com.reyco1.webservice
{
	import flash.events.*;
	import flash.net.*;

	public class WebService extends EventDispatcher
	{

    }

}</pre>
<p>Now we create the variables that we will need in order to connect to the online web services. We&#8217;ll need a URLRequest, a URLLoader and a URLVariables along with a generic data variable that will hold information returned from the web service. We want to make this data variable public so that it can be accessed from outside the class:</p>
<pre>package com.helix.webservice
{
	import flash.events.*;
	import flash.net.*;

	public class WebService extends EventDispatcher
	{

		private var request:URLRequest;
		private var loader:URLLoader;
		private var variables:URLVariables;
		public var data:*

    }

}</pre>
<p>Now that we have all the variables we need, we can go ahead and create our constructor. The class constructor is a special method always named the same name as the class. Constructors can accept arguments or not. Our&#8217;s will accept one, an HTTP Request (I&#8217;ll explain in a bit). We&#8217;ll call the EventDispatcher constructor by calling the &#8220;super&#8221; method and then set our request variable to accept the HTTP Request:</p>
<pre>package com.reyco1.webservice
{
	import flash.events.*;
	import flash.net.*;

	public class WebService extends EventDispatcher
	{

		private var request:URLRequest;
		private var loader:URLLoader;
		private var variables:URLVariables;
		public var data:*

		public function WebService($servicePath:String)
		{
        	super();
			request = new URLRequest($servicePath);
		}
    }
}</pre>
<p>Now we&#8217;ll create our one and only method. This is the method that will make the call to the webservice. Our method will accept 2 arguments: the first is a &#8220;method&#8221; (String); we can set this method to be either &#8220;POST&#8221; or &#8220;GET&#8221;, ours will default to &#8220;POST&#8221;. Usually web services will tell you which method to use in order to access their information. The second argument is &#8220;arguments&#8221; (Object); This will hold any parameters that you would need to make your service call. Again, web services will tell you if you need to send any additional information with your call, ours defaults to null.</p>
<pre>package com.reyco1.webservice
{
	import flash.events.*;
	import flash.net.*;

	public class WebService extends DispatchExtension
	{

		private var request:URLRequest;
		private var loader:URLLoader;
		private var variables:URLVariables;
		public var data:*

		public function WebService($servicePath:String)
		{
			request = new URLRequest($servicePath);
		}

		/**
		 * Makes the web service call
		 * @param $method either POST or GET. POST is set as default
		 * @param $arguments any parameters or variables that you need to make with your web service call
		 *
		 */
		public function call($method:String="POST", $arguments:Object=null):void
		{
			variables = new URLVariables();
			if($arguments != null){
				for (var i:* in $arguments){
					variables[i] = $arguments[i];
				}
				request.data = variables;
			}
			request.method = $method.toUpperCase();
			loader = new URLLoader(request);
			loader.addEventListener(Event.COMPLETE, handleRequestLoaded);
			loader.addEventListener(IOErrorEvent.IO_ERROR, handleRequestError);
			loader.load(request);
		}
    }
}</pre>
<p>Now let me explain what&#8217;s going on up there. Basically we set up our URLVariables by recursing through the &#8220;$arguments&#8221; parameter if it&#8217;s not set to null and set it to the &#8220;data&#8221; proerty or our URLRequest. Then we specify the call method by setting the URLRequest&#8217;s &#8220;method&#8221; property to the &#8220;$method&#8221; parameter. We then continue to set up out URLLoader instance by adding the COMPLETE and IOErrorEvent event listeners. Finally we make the service call by executing the URLLoader&#8217;s call method and passing it the URLRequest.</p>
<p>Now we create the handlers for our event listeners:</p>
<pre>package com.reyco1.webservice
{
	import flash.events.*;
	import flash.net.*;

	public class WebService extends EventDispatcher
	{

		private var request:URLRequest;
		private var loader:URLLoader;
		private var variables:URLVariables;
		public var data:*

		public function WebService($servicePath:String)
		{
			request = new URLRequest($servicePath);
		}

		/**
		 * Makes the web service call
		 * @param $method either POST or GET. POST is set as default
		 * @param $arguments any parameters or variables that you need to make with your web service call
		 *
		 */
		public function call($method:String="POST", $arguments:Object=null):void
		{
			variables = new URLVariables();
			if($arguments != null){
				for (var i:* in $arguments){
					variables[i] = $arguments[i];
				}
				request.data = variables;
			}
			request.method = $method.toUpperCase();
			loader = new URLLoader(request);
			loader.addEventListener(Event.COMPLETE, handleRequestLoaded);
			loader.addEventListener(IOErrorEvent.IO_ERROR, handleRequestError);
			loader.load(request);
		}

		private function handleRequestLoaded($event:Event):void
		{
			data = $event.target.data;
			this.dispatchEvent(new Event(&#8221;RESULT&#8221;, false));
		}

		private function handleRequestError($event:IOErrorEvent):void
		{
			data = {};
			this.dispatchEvent(new Event(&#8221;FAULT&#8221;, false));
		}

	}
}</pre>
<p>In the handlers, if the call is successfull, it sets the data variable to the data that the web service returns and fires a &#8220;RESULT&#8221; event. If the call is unsuccessful, set the data to an empty object and fires teh &#8220;FAULT&#8221; event. Now we&#8217;ll add getter and setter functions so that we may be able to dynamically set the web service path whenever we want.</p>
<pre>package com.reyco1.webservice
{
	import flash.events.*;
	import flash.net.*;

	public class WebService extends EventDispatcher
	{

		private var request:URLRequest;
		private var loader:URLLoader;
		private var variables:URLVariables;
		public var data:*

		public function WebService($servicePath:String)
		{
			request = new URLRequest($servicePath);
		}

		/**
		 * Makes the web service call
		 * @param $method either POST or GET. POST is set as default
		 * @param $arguments any parameters or variables that you need to make with your web service call
		 *
		 */
		public function call($method:String="POST", $arguments:Object=null):void
		{
			variables = new URLVariables();
			if($arguments != null){
				for (var i:* in $arguments){
					variables[i] = $arguments[i];
				}
				request.data = variables;
			}
			request.method = $method.toUpperCase();
			loader = new URLLoader(request);
			loader.addEventListener(Event.COMPLETE, handleRequestLoaded);
			loader.addEventListener(IOErrorEvent.IO_ERROR, handleRequestError);
			loader.load(request);
		}

		private function handleRequestLoaded($event:Event):void
		{
			data = $event.target.data;
			this.dispatchEvent(new Event(&#8221;RESULT&#8221;, false));
		}

		private function handleRequestError($event:IOErrorEvent):void
		{
			data = {};
			this.dispatchEvent(new Event(&#8221;FAULT&#8221;, false));
		}

		public function set servicePath($path:String):void
		{
			request = new URLRequest($path);
		}

		public function get servicePath():String
		{
			return request.url;
		}

	}
}</pre>
<p>That&#8217;s basically all we need for our base WebService calss! Was it easy? Hope so <img src='http://www.thetechlabs.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> lets fire it up by using it in a simple Twitter API!</p>
<h3>Using the newly created WebService class to make a simple Twitter API</h3>
<p>Create a new Actionscript file, name it &#8220;Twitter&#8221; and save it to the webservices folder. We want this class to fire events also, so lets start off by extending the EventDispatcher class. Remember also to import the flash events package and our WebService class too:</p>
<pre>package com.reyco1.webservice
{
	import com.reyco1.webservice.WebService;
	import flash.events.*;

	public class Twitter extends EventDispatcher
	{
    }

}</pre>
<p>Cool, let&#8217;s create a static private var that will hold an HTTP Status method provided by the online Twitter API. You can find all the Twiiter methods over at the Twitter RESR API Docunebtation here: http://apiwiki.twitter.com/REST+API+Documentation#HTTPStatusCodes. We&#8217;re going to use only one for now. The call to get all the Tweets on the public timeline.</p>
<p>We&#8217;re also going to need a private variable to represent our WebService class and a public array variable which will hold the parsed xml returned byt Twitter:</p>
<pre>package com.reyco1.webservice
{
	import com.reyco1.webservice.WebService;
	import flash.events.*;

	public class Twitter extends EventDispatcher
	{

		private static var PUBLIC_TIMELINE:String = "http://twitter.com/statuses/public_timeline.xml";

		private var twitterService:WebService;
		public var result:Array;

        }
    }
}</pre>
<p>Ok, now let&#8217;s set up our constructor. In the constructor we will set up our instance of the WebService class. Remeber that the WebService class needs to accept a service path in it&#8217;s constructor, so we&#8217;ll pass it our PUBLIC_TIMELINE path as default. We also set the result array to an empty array;</p>
<pre>package com.reyco1.webservice
{
	import com.reyco1.webservice.WebService;
	import flash.events.*;

	public class Twitter extends EventDispatcher
	{

		private static var PUBLIC_TIMELINE:String = "http://twitter.com/statuses/public_timeline.xml";

		private var twitterService:WebService;
		public var result:Array;

		public function Twitter()
		{
			twitterService = new WebService(Twitter.PUBLIC_TIMELINE);
			twitterService.addEventListener("RESULT", handleResult);
			twitterService.addEventListener("FAULT", handleFault);

			result = [];
		}
    }
}</pre>
<p>before we set up the handlers for the events fired from the WebService class, let&#8217;s create the method which will make the request for the public timeline to Twitter:</p>
<pre>package com.reyco1.webservice
{
	import com.reyco1.webservice.WebService;
	import flash.events.*;

	public class Twitter extends EventDispatcher
	{

		private static var PUBLIC_TIMELINE:String = "http://twitter.com/statuses/public_timeline.xml";

		private var twitterService:WebService;
		public var result:Array;

		public function Twitter()
		{
			twitterService = new WebService(Twitter.PUBLIC_TIMELINE);
			twitterService.addEventListener("RESULT", handleResult);
			twitterService.addEventListener("FAULT", handleFault);

			result = [];
		}

		public function requestPublicTimeline():void
		{
			twitterService.servicePath = Twitter.PUBLIC_TIMELINE;
			twitterService.call();
		}
    }
}</pre>
<p>What we&#8217;re doing is dynamically setting the service path on the Webservices class. Now, you may be asking yourself, &#8220;didn&#8217;t we just add that path up in the constructor?&#8221;, the answer is, yes. Never the less, it is good practice for us to set it up here again, basically because later on when we add new methods, we&#8217;ll have to set it to the new service path anyway, so might as well get used to it from now <img src='http://www.thetechlabs.com/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>Afther the path is set, then we then execute the call method of our WebService class. Nothig is going to happen unless we add our event handlers, so let&#8217;s get to it!</p>
<pre>package com.reyco1.webservice
{
	import com.reyco1.webservice.WebService;
	import flash.events.*;

	public class Twitter extends EventDispatcher
	{

		private static var PUBLIC_TIMELINE:String = "http://twitter.com/statuses/public_timeline.xml";

		private var twitterService:WebService;
		public var result:Array;

		public function Twitter()
		{
			twitterService = new WebService(Twitter.PUBLIC_TIMELINE);
			twitterService.addEventListener("RESULT", handleResult);
			twitterService.addEventListener("FAULT", handleFault);

			result = [];
		}

		public function requestPublicTimeline():void
		{
			twitterService.servicePath = Twitter.PUBLIC_TIMELINE;
			twitterService.call();
		}

		private function handleFault($event:Event):void
		{
			result = [];
			this.dispatchEvent(new Event(&#8221;FAULT&#8221;));
		}

		private function handleResult($event:Event):void
		{
			var myResult:XML = new XML(twitterService.data);
			result = parseResult(myResult);
			this.dispatchEvent(new Event(&#8221;RESULT&#8221;));
		}
    }
}</pre>
<p>Nothing too fancy in the fault event handler. Basically, all it does is is just set the reult array to an ampty array and fires a new Fault event. Now, the good stuff is in the result event handler. The response we get from Twitter will be a String in an XML format, so once it arrives, we need make sure flash reads it as XML. Once that is done, we pass it to a method named &#8220;parseResult&#8221; which we will be seeing in the next step. Essentially, all the &#8220;parseResult&#8221; method does is take the xml returned from Twitter and parse it into an array of objects, better known as a &#8220;Collection&#8221;. Once the XML is parsed, it is set as the &#8220;result&#8221; array and a &#8220;RESULT&#8221; event is fired. Let&#8217;s see that crazy &#8220;parseResult&#8221; method:</p>
<pre>package com.reyco1.webservice
{
	import com.reyco1.webservice.WebService;
	import flash.events.*;

	public class Twitter extends EventDispatcher
	{

		private static var PUBLIC_TIMELINE:String = "http://twitter.com/statuses/public_timeline.xml";

		private var twitterService:WebService;
		public var result:Array;

		public function Twitter()
		{
			twitterService = new WebService(Twitter.PUBLIC_TIMELINE);
			twitterService.addEventListener("RESULT", handleResult);
			twitterService.addEventListener("FAULT", handleFault);

			result = [];
		}

		public function requestPublicTimeline():void
		{
			twitterService.servicePath = Twitter.PUBLIC_TIMELINE;
			twitterService.call();
		}

		private function handleResult($event:Event):void
		{
			var myResult:XML = new XML(twitterService.data);
			result = parseResult(myResult);
			this.dispatchEvent(new Event(&#8221;RESULT&#8221;));
		}

		private function handleFault($event:Event):void
		{
			result = [];
			this.dispatchEvent(new Event(&#8221;FAULT&#8221;));
		}

		private function parseResult($result:XML):Array
		{
			var objectArray:Array = [];
			var xml:XML = $result
			var xmllist:XMLList = xml.children();

			for(var a:Number = 0; a &lt; xmllist.length(); a++){
				var object:Object = {};
				for(var b:Number = 0; b&lt; xmllist[a].children().length(); b++){
					var item:XML = xmllist[a].children()[b];
					if(item.localName().toString() == &#8220;user&#8221;){
						var userObject:Object = {};
						for(var c:Number = 0; c &lt; item.children().length(); c++){
							userObject[item.children()[c].localName().toString()] = item.children()[c].toString()
						}
						object[item.localName()] = userObject;
					}else{
						object[item.localName()] = item.toString();
					}
				}
				objectArray.push(object);
			}
			return objectArray;
		}
	}
}</pre>
<p>With the &#8220;parseResult&#8221; method, each node in the XML will be parsed into an object containing the following values:</p>
<pre>id
in_reply_to_status_id
source
created_at
user
  -- name
  -- url
  -- screen_name
  -- description
  -- followers_count
  -- id
  -- protected
  -- profile_image_url
  -- location
in_reply_to_user_id
text
favorited
truncated</pre>
<p>I think it&#8217;s time we test ride this puppy! Let&#8217;s get to it!</p>
<h3>Creating a Twitter Application with our newly created Twitter API</h3>
<p>Crack open an fla file and set the stage dimensions to 310 by 400. We&#8217;ll start off by creating a container for each Tweet. The container movieclip will hold the users profile image and a textfield to display the user&#8217;s tweet. Each of a users profile image is 48 x 48, so create a square on the stage 48 x 48 and create a static text field which says &#8220;LOADING&#8221; and place it right at the center of the box. Now convert the whole thing into a MovieClip and instance name it &#8220;imageContainer&#8221;;</p>
<p><img class="alignnone size-full wp-image-101" title="image001" src="http://www.thetechlabs.com/wp-content/uploads/2008/09/image001.png" alt="" width="141" height="119" /></p>
<p><img src="images/image001.png" border="0" alt="" /></p>
<p>Now create a dynamic text field with the following properties:</p>
<p><img class="alignnone size-full wp-image-103" title="image002" src="http://www.thetechlabs.com/wp-content/uploads/2008/09/image002.png" alt="" width="500" height="89" /></p>
<p><img src="images/image002.png" border="0" alt="" /></p>
<p>Set the text field right next to the imageContainer and create a MovieClip out of the whole thing. So essentially at this point you should have ONE MovieClip on the stage. This movie clip should contain an 48 x 48 MovieClip instance named &#8220;imageContainer&#8221; and a dynamic text field instance named &#8220;twitTxt&#8221;</p>
<p><img class="alignnone size-full wp-image-104" title="image003" src="http://www.thetechlabs.com/wp-content/uploads/2008/09/image003.png" alt="" width="372" height="101" /></p>
<p><img src="images/image003.png" border="0" alt="" /></p>
<p>Drag another 5 instances of the newly created movie clip from the library and drop them on the stage and align them one under the other like shown below and instance name then item0, item1, item2, item3, item4 and item5:</p>
<p><img class="alignnone size-full wp-image-102" title="image004" src="http://www.thetechlabs.com/wp-content/uploads/2008/09/image004.png" alt="" width="297" height="310" /></p>
<p><img src="images/image004.png" border="0" alt="" /></p>
<p>That&#8217;s it for the timeline stuff. Select the first frame and open up your actions panel. The first thing we need to do is import our Twitter class;</p>
<pre>import com.reyco1.webservice.Twitter</pre>
<p>Now we create an instance of our Twitter API and have it listen to the &#8220;RESULT&#8221; event:</p>
<pre>import com.reyco1.webservice.Twitter

var twitter:Twitter = new Twitter();
twitter.addEventListener("RESULT", handleResult);</pre>
<p>Let&#8217;s set up the handler for the result event. In the handler we pass the first 6 of the objects in the result array to a &#8220;populateItem&#8221; method along with its index. Let&#8217;s take a look at the event handler and then we&#8217;ll see what that &#8220;populateItem&#8221; method looks like.</p>
<pre>import com.reyco1.webservice.Twitter

var twitter:Twitter = new Twitter();
twitter.addEventListener("RESULT", handleResult);

function handleResult($event:Event):void
{
	var result:Array = twitter.result;
	for(var a:Number = 0; a &lt; 6; a++){
		populateItem(a, result[a]);
	}
}</pre>
<p>Below we&#8217;ve added the &#8220;populateItem&#8221; method. Basically, this method adds the tweet to the text field in the item on stage along with adding the image to it&#8217;s imageContainer.</p>
<pre>import com.reyco1.webservice.Twitter

var twitter:Twitter = new Twitter();
twitter.addEventListener("RESULT", handleResult);

function handleResult($event:Event):void
{
	var result:Array = twitter.result;
	for(var a:Number = 0; a &lt; 6; a++){
		populateItem(a, result[a]);
	}
}

function populateItem($itemIndex:Number, $twitObject:Object):void
{
	this["item"+$itemIndex].twitText.text = $twitObject.text;
	var imageLoader:Loader = new Loader();
	this["item"+$itemIndex].imageContainer.addChild(imageLoader);
	var imagePath:URLRequest = new URLRequest($twitObject.user.profile_image_url);
	imageLoader.load(imagePath);
}</pre>
<p>Now all we have to do is call the &#8220;requestPublicTimeline&#8221; method of our Twitter API and we&#8217;re DONE!!</p>
<pre>import com.reyco1.webservice.Twitter

var twitter:Twitter = new Twitter();
twitter.addEventListener("RESULT", handleResult);

function handleResult($event:Event):void
{
	var result:Array = twitter.result;
	for(var a:Number = 0; a &lt; 6; a++){
		populateItem(a, result[a]);
	}
}

function populateItem($itemIndex:Number, $twitObject:Object):void
{
	this["item"+$itemIndex].twitText.text = $twitObject.text;
	var imageLoader:Loader = new Loader();
	this["item"+$itemIndex].imageContainer.addChild(imageLoader);
	var imagePath:URLRequest = new URLRequest($twitObject.user.profile_image_url);
	imageLoader.load(imagePath);
}

twitter.requestPublicTimeline();</pre>
<h3>Publishing your file as an AIR application</h3>
<p>Publishing a flash file as an AIR application cannot be any easier:</p>
<ul>
<li>Download the runtime installation file.</li>
<li>Double-click the runtime installation file.</li>
<li>In the installation window, follow the prompts to complete the installation.</li>
</ul>
<p>Once installed, follow these steps:</p>
<ul>
<li>Open your Flash FLA file.</li>
<li>If you’re opening a new Flash File (ActionScript 3.0), save it. If you don’t save it, a warning appears when you do the next step.</li>
<li>Select Commands &gt; AIR - Application And Installer Settings. An alert box appears, asking if you want to convert the file to Adobe AIR publish settings.</li>
<li>Click OK to convert the FLA file to Adobe AIR publish settings. The AIR - Application And Installer Settings dialog box appears.</li>
</ul>
<p>Simply publish your and all should work! I hope you have enjoyed this article and have been able to learn lots of stuff! In the next few tutorials, I&#8217;ll be adding more complex methods to our Twitter API and Hopefully at the end of the series, we&#8217;ll have a fully working Twitter Application!</p>
<p class="addtoany_share_save_container">
    <a class="a2a_dd addtoany_share_save" onmouseover="a2a_show_dropdown(this)" onmouseout="a2a_onMouseOut_delay()" href="http://www.addtoany.com/share_save?sitename=The%20Tech%20Labs&amp;siteurl=http%3A%2F%2Fwww.thetechlabs.com%2F&amp;linkname=Creating%20a%20AIR%20Web%20Service%20Package%20with%20Flash%20and%20AS3-%20Base%20Service%20Class%20And%20Simple%20Twitter%20API&amp;linkurl=http%3A%2F%2Fwww.thetechlabs.com%2Fflash%2Fcreating-a-as3-web-service-package-base-service-class-and-simple-twitter-api%2F" onclick="javascript:pageTracker._trackPageview('oclicks/http://www.addtoany.com/share_save?sitename=The%20Tech%20Labs&amp;siteurl=http%3A%2F%2Fwww.thetechlabs.com%2F&amp;linkname=Creating%20a%20AIR%20Web%20Service%20Package%20with%20Flash%20and%20AS3-%20Base%20Service%20Class%20And%20Simple%20Twitter%20API&amp;linkurl=http%3A%2F%2Fwww.thetechlabs.com%2Fflash%2Fcreating-a-as3-web-service-package-base-service-class-and-simple-twitter-api%2F');"><img src="http://www.thetechlabs.com/wp-content/plugins/add-to-any/share_save_171_16.gif" width="171" height="16" alt="Share/Save/Bookmark"/></a>

	</p><img src="http://feedproxy.google.com/~r/TheTechLabs_air/~4/9GmCJYhawBo" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.thetechlabs.com/flash/creating-a-as3-web-service-package-base-service-class-and-simple-twitter-api/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.thetechlabs.com/flash/creating-a-as3-web-service-package-base-service-class-and-simple-twitter-api/</feedburner:origLink></item>
		<item>
		<title>How to build a contact manager in AIR using XML - Part 2</title>
		<link>http://feedproxy.google.com/~r/TheTechLabs_air/~3/t1GsW73rOeI/</link>
		<comments>http://www.thetechlabs.com/xml/how-to-build-a-contact-manager-in-air-using-xml-part-2/#comments</comments>
		<pubDate>Thu, 17 Jul 2008 22:36:15 +0000</pubDate>
		<dc:creator>Pedro Furtado</dc:creator>
		
		<category><![CDATA[AIR]]></category>

		<category><![CDATA[Interfaces]]></category>

		<category><![CDATA[XML]]></category>

		<category><![CDATA[adobe air]]></category>

		<category><![CDATA[binding]]></category>

		<category><![CDATA[components]]></category>

		<category><![CDATA[contact manager]]></category>

		<category><![CDATA[data]]></category>

		<category><![CDATA[datagrid]]></category>

		<category><![CDATA[mxml]]></category>

		<guid isPermaLink="false">http://www.thetechlabs.com/?p=82</guid>
		<description><![CDATA[<p>In this second, and final, part we&#8217;ll be learning how to save and edit the file and how to setup the bindings between the xml and our component.</p>
<small><em>posted in <a href="http://www.thetechlabs.com/category/air/">AIR</a> by Pedro Furtado <a href="http://www.thetechlabs.com/xml/how-to-build-a-contact-manager-in-air-using-xml-part-2/#comments">Leave A Comment</a><br />&copy;2008 <a href="http://www.thetechlabs.com">The Tech Labs</a>. All Rights Reserved.</em></small>]]></description>
			<content:encoded><![CDATA[<p>Its finally time for the second part of the AIR Contact Manager.</p>
<p>In this second, and final, part we&#8217;ll be learning how to save and edit the file and how to setup the bindings between the xml and our component.</p>
<p><a title="Download Source Files" href="http://www.thetechlabs.com/tutorials/files/Air/pfurtado/FEXMLContacts.zip" onclick="javascript:pageTracker._trackPageview('downloads/tutorials/files/Air/pfurtado/FEXMLContacts.zip');" target="_self"><strong>Download the source files</strong></a></p>
<p>On a side note, we&#8217;ll be using the application storage folder to save the data because AIR doesn&#8217;t allow us to write to the application folder itself.</p>
<pre>&lt;mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" initialize="{init()}"
	layout="absolute" height="451" width="542"&gt;
&lt;!-- // --&gt;
	&lt;mx:Script&gt;
		&lt;![CDATA[
			import mx.collections.XMLListCollection;
			private var myXml:XML;
			private var myLoader:URLLoader;
			[Bindable]
			private var myContactsCol:XMLListCollection;</pre>
<p>Note the introduction of the [Bindable] above &#8216;private var myContactsCol:XMLListCollection;&#8217; this tells the Flex components that they can use this variable to listen to changes and to change it&#8217;s values.</p>
<pre>public function init():void
			{
				var dataFile:File = new File('app-storage:/data.xml');
				if(dataFile.exists){
					var myReq:URLRequest = new URLRequest('app-storage:/data.xml');
					myLoader = new URLLoader()
					myLoader.addEventListener(Event.COMPLETE, dataComplete);
					myLoader.load(myReq);
				}else{
					createFile()
				}

			}</pre>
<p>We start by initializing the application, as we saw in the previous part, but this time we need to check if the XML file already exists in our<br />
application storage. And if it doesn&#8217;t we need to create it.<br />
Note: You&#8217;ll notice that we&#8217;re referencing the file as app-storage:/data.xml, app-storage is a shortcut that AIR give you to your local storage location on the users machine.</p>
<pre>private function createFile():void
			{
				var xmlToSave:XML = new XML('&lt;flashEnabled&gt;&lt;contacts&gt;' +
						'&lt;contact&gt;' +
						'&lt;name&gt;Contact1&lt;/name&gt;' +
						'&lt;telephone&gt;&lt;/telephone&gt;' +
						'&lt;email&gt;&lt;/email&gt;' +
						'&lt;/contact&gt;' +
						'&lt;/contacts&gt;' +
						'&lt;/flashEnabled&gt;')
				var saveStr:String = xmlToSave.toXMLString();

				var file:File = new File('app-storage:/data.xml');
				var fs:FileStream = new FileStream();
				fs.open(file, FileMode.WRITE);

				fs.writeUTFBytes(saveStr);
				fs.close();
				init();
			}</pre>
<p>Since the file wasn&#8217;t found we&#8217;ll create it, we start by creating our base XML object and retrieving a string with xml identation, then we open a File reference to it and finally<br />
we write the file using writeUTFBytes that will automatically save our string to the XML file with UTF-8.</p>
<pre>private function dataComplete(e:Event):void
			{
				myXml = new XML(myLoader.data.toString());
				myContactsCol = new XMLListCollection(myXml..contact);

			}

			private function updateList():void
			{
				myContactsCol = new XMLListCollection(myXml..contact.(name.toLowerCase().indexOf(search_txt.text.toLowerCase()) != -1 ))
			}</pre>
<p>Here you&#8217;ll also notice that we no longer need to invalidate the list or reset the datagrid&#8217;s dataprovider, this is because we now use Binding.</p>
<pre>private function saveData(e:Event):void
			{
				var xmlToSave:XML = new XML('&lt;flashEnabled&gt;&lt;contacts&gt;'+myContactsCol.toXMLString()+'&lt;/contacts&gt;&lt;/flashEnabled&gt;')
				var saveStr:String = xmlToSave.toXMLString();

				var file:File = new File('app-storage:/data.xml');
				var fs:FileStream = new FileStream();
				fs.open(file, FileMode.WRITE);

				fs.writeUTFBytes(saveStr);
				fs.close();
			}</pre>
<p>This is where we&#8217;ll save the file, it&#8217;s essentially the same as create file, but with the contents of our current contact xml collection as nodes.</p>
<pre>private function addItem():void
			{
				myContactsCol.addItem(new XML('&lt;contact&gt;' +
						'&lt;name&gt;Contact1&lt;/name&gt;' +
						'&lt;telephone&gt;&lt;/telephone&gt;' +
						'&lt;email&gt;&lt;/email&gt;' +
						'&lt;/contact&gt;'))
			}

			private function removeItem():void
			{
				myContactsCol.removeItemAt(myDg.selectedIndex);
			}

		]]&gt;
	&lt;/mx:Script&gt;</pre>
<p>Here we have the add and remove item from our collection. So first lets go over adding an item, we essentially need to create an empty node and add it to the xml collection.<br />
This can be achieved in a myriad of ways, but I reckon this might be the easiest.<br />
Then for the removing part it&#8217;s as easy as removing the item that corresponds to the current index of the data grid.</p>
<p>Design</p>
<pre>&lt;mx:DataGrid bottom="60" top="10" left="10" id="myDg" dataProvider="{myContactsCol}" editable="true" right="10"&gt;
		&lt;mx:columns&gt;
			&lt;mx:DataGridColumn headerText="Name" dataField="name"/&gt;
			&lt;mx:DataGridColumn headerText="Telphone" dataField="telephone"/&gt;
			&lt;mx:DataGridColumn headerText="Email" dataField="email"/&gt;
		&lt;/mx:columns&gt;
	&lt;/mx:DataGrid&gt;
	&lt;mx:Label x="10" text="Search:" bottom="10"/&gt;
	&lt;mx:TextInput x="65" id="search_txt" bottom="10"/&gt;
	&lt;!-- Code block 9 --&gt;
	&lt;mx:Button x="233" label="Go!" id="go_btn" click="{updateList()}" bottom="10"/&gt;
	&lt;mx:Button label="Save" click="{saveData(event)}" id="saveButton" bottom="10" right="10"/&gt;
	&lt;mx:Button x="286" label="Add" click="{addItem()}" bottom="10"/&gt;
	&lt;mx:Button x="343" label="Remove" click="{removeItem()}" bottom="10"/&gt;

&lt;/mx:WindowedApplication&gt;</pre>
<p>With all the UI elements in place we can now set the two properties we need for the datagrid to work properly, the editable=&#8221;true&#8221; so that we can edit the contents of each item,<br />
and the to set the dataprovider to myContactsCol so that it will bind to it.</p>
<p>Afterward we just have the normal buttons and layout to call the appropriate methods on click and you&#8217;re all set.</p>
<p>I hope that this will help you get some insight on how to use Bindings and how to modify and write files using AIR so that you can apply it to your own projects,<br />
in the previous part of this article we mentioned the application self update, but since then Adobe has release the air update framework and therefore there&#8217;s no longer<br />
need to use our own library to achieve a self updating application.</p>
<p>If you have any questions feel free to expose them here and I&#8217;ll address them as soon as I can.</p>
<p class="addtoany_share_save_container">
    <a class="a2a_dd addtoany_share_save" onmouseover="a2a_show_dropdown(this)" onmouseout="a2a_onMouseOut_delay()" href="http://www.addtoany.com/share_save?sitename=The%20Tech%20Labs&amp;siteurl=http%3A%2F%2Fwww.thetechlabs.com%2F&amp;linkname=How%20to%20build%20a%20contact%20manager%20in%20AIR%20using%20XML%20-%20Part%202&amp;linkurl=http%3A%2F%2Fwww.thetechlabs.com%2Fxml%2Fhow-to-build-a-contact-manager-in-air-using-xml-part-2%2F" onclick="javascript:pageTracker._trackPageview('oclicks/http://www.addtoany.com/share_save?sitename=The%20Tech%20Labs&amp;siteurl=http%3A%2F%2Fwww.thetechlabs.com%2F&amp;linkname=How%20to%20build%20a%20contact%20manager%20in%20AIR%20using%20XML%20-%20Part%202&amp;linkurl=http%3A%2F%2Fwww.thetechlabs.com%2Fxml%2Fhow-to-build-a-contact-manager-in-air-using-xml-part-2%2F');"><img src="http://www.thetechlabs.com/wp-content/plugins/add-to-any/share_save_171_16.gif" width="171" height="16" alt="Share/Save/Bookmark"/></a>

	</p><img src="http://feedproxy.google.com/~r/TheTechLabs_air/~4/t1GsW73rOeI" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.thetechlabs.com/xml/how-to-build-a-contact-manager-in-air-using-xml-part-2/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.thetechlabs.com/xml/how-to-build-a-contact-manager-in-air-using-xml-part-2/</feedburner:origLink></item>
		<item>
		<title>Creating A Downloader For YouTube with Flex/Air</title>
		<link>http://feedproxy.google.com/~r/TheTechLabs_air/~3/ESHudCfZYhQ/</link>
		<comments>http://www.thetechlabs.com/video/creating-a-downloader-for-youtube-with-flexair-2/#comments</comments>
		<pubDate>Thu, 12 Jun 2008 10:58:17 +0000</pubDate>
		<dc:creator>Jack Herrington</dc:creator>
		
		<category><![CDATA[AIR]]></category>

		<category><![CDATA[Flex]]></category>

		<category><![CDATA[Interfaces]]></category>

		<category><![CDATA[Video]]></category>

		<category><![CDATA[adobe air]]></category>

		<category><![CDATA[adobe flex]]></category>

		<category><![CDATA[how-to]]></category>

		<category><![CDATA[tutorial]]></category>

		<category><![CDATA[YouTube]]></category>

		<guid isPermaLink="false">http://www.thetechlabs.com/?p=61</guid>
		<description><![CDATA[<p>YouTube’s mixed easy movie access with community uploads to create a startling new service. The online problem is that you can only access YouTube when you are online. How can you access those movies when you are offline? Let’s solve that problem by building a downloader with Flex and AIR.<br />
In this article we will build a cross platform application that searches for YouTube videos and then provides a mechanism to download those videos and view them locally. You will be able to take your favorite YouTube videos with you wherever you go.</p>
<small><em>posted in <a href="http://www.thetechlabs.com/category/air/">AIR</a> by Jack Herrington <a href="http://www.thetechlabs.com/video/creating-a-downloader-for-youtube-with-flexair-2/#comments">Leave A Comment</a><br />&copy;2008 <a href="http://www.thetechlabs.com">The Tech Labs</a>. All Rights Reserved.</em></small>]]></description>
			<content:encoded><![CDATA[<p>YouTube’s mixed easy movie access with community uploads to create a startling new service. The online problem is that you can only access YouTube when you are online. How can you access those movies when you are offline? Let’s solve that problem by building a downloader with Flex and AIR.<br />
In this article we will build a cross platform application that searches for YouTube videos and then provides a mechanism to download those videos and view them locally. You will be able to take your favorite YouTube videos with you wherever you go.</p>
<h3>Requirements</h3>
<p><strong>Flex 3</strong> <a title="Try/Buy" href="http://www.adobe.com/products/flex/?promoid=BPDEQ" onclick="javascript:pageTracker._trackPageview('oclicks/http://www.adobe.com/products/flex/?promoid=BPDEQ');" target="_blank"></a></p>
<p><a title="Try/Buy" href="http://www.adobe.com/products/flex/?promoid=BPDEQ" onclick="javascript:pageTracker._trackPageview('oclicks/http://www.adobe.com/products/flex/?promoid=BPDEQ');" target="_blank">Try/Buy</a></p>
<p><strong>Sample files:</strong></p>
<p><a title="Download Sample Files" href="http://thetechlabs.com/tutorials/files/youtube/YouTube.zip" onclick="javascript:pageTracker._trackPageview('downloads/tutorials/files/youtube/YouTube.zip');">YouTube.zip</a><br />
Let’s start by building the search user interface.</p>
<h3>Searching YouTube</h3>
<p>YouTube provides a set of RSS feeds that keep you up to date with the latest videos. These feeds take lots of parameters to refine what you are looking for, one of those is an arbitrary keyword search.<br />
The Flex code below uses the YouTube search feed to request a set of videos based on the user’s search criteria. The code then uses the e4x language extensions in ActionScript 3 to parse the feed and present the video thumbnails in a TileList.</p>
<pre>&lt;?xml version="1.0" encoding="utf-8"?&gt; 

  &lt;mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"  title="YouTube  Search"&gt; 

  &lt;mx:Script&gt; 

  &lt;![CDATA[ 

  <strong>import</strong>
 mx.rpc.events.ResultEvent; 

  <strong>import</strong>
 mx.rpc.http.HTTPService; 

<strong>namespace</strong>
 atom = <strong>"http://www.w3.org/2005/Atom"</strong>
; 

    <strong>namespace</strong>
 media = <strong>"http://search.yahoo.com/mrss/"</strong>
; 

<strong>private</strong>
 <strong>function</strong>
 onSearch() : <strong>void</strong>
 { 

    <strong>var</strong>
 search:HTTPService = <strong>new</strong>
 HTTPService(); 

    search.url = <strong>"http://gdata.youtube.com/feeds/api/videos/?vq="</strong>
+escape(txtSearch.text)+<strong>"&amp;orderby=updated"</strong>
; 

    search.resultFormat = <strong>'e4x'</strong>
; 

     search.addEventListener(ResultEvent.RESULT,onSearchResult); 

    search.send(); 

  } 

  <strong>private</strong>
 <strong>function</strong>
 onSearchResult( event:ResultEvent ) : <strong>void</strong>
 { 

    <strong>use</strong>
 <strong>namespace</strong>
 atom; 

    <strong>use</strong>
 <strong>namespace</strong>
 media; 

    <strong>var</strong>
 movies:Array = []; 

    <strong>for</strong>
 <strong>each</strong>
( <strong>var</strong>
 entry:XML <strong>in</strong>
 event.result..entry ) { 

      <strong>var</strong>
 group:XML = entry.group[0]; 

      movies.push( { 

         id:group.player.@url.toString(), 

         description:entry.content.toString(), 

         thumbnail:group.thumbnail[0].@url.toString() } ); 

    } 

    thumbList.dataProvider =  movies; 

  } 

  ]]&gt; 

  &lt;/mx:Script&gt; 

  &lt;mx:HBox  width=&#8221;100%&#8221;&gt; 

  &lt;mx:TextInput  width=&#8221;100%&#8221; id=&#8221;txtSearch&#8221; /&gt; 

  &lt;mx:Button  label=&#8221;Search&#8221; click=&#8221;onSearch()&#8221; /&gt; 

  &lt;/mx:HBox&gt; 

  &lt;mx:TileList  id=&#8221;thumbList&#8221; width=&#8221;100%&#8221; height=&#8221;100%&#8221;&gt; 

  &lt;mx:itemRenderer&gt; 

  &lt;mx:Component&gt; 

  &lt;mx:HBox  paddingBottom=&#8221;5&#8243; paddingLeft=&#8221;5&#8243; paddingRight=&#8221;5&#8243;  paddingTop=&#8221;5&#8243;&gt; 

  &lt;mx:Image  source =&#8221;{data.thumbnail}&#8221; height=&#8221;100&#8243; width=&#8221;150&#8243; horizontalAlign=&#8221;center&#8221; verticalAlign=&#8221;middle&#8221; toolTip=&#8221;{data.description}&#8221;&gt; 

  &lt;/mx:Image&gt; 

  &lt;/mx:HBox&gt; 

  &lt;/mx:Component&gt; 

  &lt;/mx:itemRenderer&gt; 

  &lt;/mx:TileList&gt; 

  &lt;/mx:WindowedApplication&gt;</pre>
<p>The onSearch method is called from the search button. It creates a new HTTPService on the fly with the URL of the YouTube feed for the search. It then registers onSearchResult as an event handler for the result.<br />
The onSearchResult method uses e4x to parse through each ‘entry’ tag in the RSS feed. For each movie it builds an object with three fields. The ‘id’ field holds the URL of the HTML page for the video. The ‘description’ field holds the textual description of the video. And the ‘thumbnail’ field holds the URL of the thumbnail.<br />
When I launch this MXML in an AIR project within Flex Builder 3 I see something like Figure 1.</p>
<p align="center"><img src="http://thetechlabs.com/wp-content/uploads/2008/06/fig1.png" border="0" alt="Figure 1-1. Just the YouTube search" width="540" height="380" /></p>
<p align="center"><strong>Figure 1-1. Just the YouTube search</strong></p>
<p>In this case I’ve typed in ‘super mario’ and pressed ‘search’ to get a list of the movies that matched that criteria.<br />
From here we need to add the ability to download the flash video, as well as play it back.</p>
<h3>Downloading From YouTube</h3>
<p>The user interface for the download version of the project is going to be a lot more complex than the search interface. We need to show the search results, allow for playback, and add a Save button to save the movie locally. The finished product is shown in Figure 2.</p>
<p align="center"><img src="http://thetechlabs.com/wp-content/uploads/2008/06/fig2.png" border="0" alt="" width="545" height="603" /></p>
<p align="center"><strong>Figure 1-2. The search and the downloader</strong></p>
<p>The interface is separated into two pieces, defined by panels. One panel is for searching and the other panel is shows the downloaded movies. The vertical divider allows you to scale the size of each of these panels to your preferred size.<br />
The search section is largely the same though we will add a progress indicator (invisible in the figure) next to the search button. That progress indicator will be used during the downloads of the movies since those tend to be fairly big files.<br />
The downloaded movies panel has the list of downloaded movies on the left, and the movie player on the right. As well as a Save button that is only visible if a movie is selected. The Save button allows the user to copy the downloaded movie out of the temporary directory onto the desktop (or wherever they choose).<br />
The user interface portion of the MXML code for this example is shown below:</p>
<pre>&lt;?xml version="1.0" encoding="utf-8"?&gt; 

  &lt;mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"  title="YouTube  Downloader" 

    height="700" width="600" paddingBottom="5" paddingLeft="5"  paddingRight="5" paddingTop="5" 

     creationComplete="updateLocalVideos()"&gt; 

  &lt;mx:VDividedBox width="100%" height="100%"&gt; 

&lt;mx:Panel  title="Search" paddingBottom="5" paddingLeft="5"  paddingRight="5" paddingTop="5" width="100%"  height="50%"&gt; 

  &lt;mx:HBox  width="100%"&gt; 

  &lt;mx:TextInput  width="100%" id="txtSearch" /&gt; 

  &lt;mx:Button  label="Search" click="onSearch()" /&gt; 

  &lt;mx:ProgressBar width="100" id="downloadProgress" visible="false" /&gt; 

  &lt;/mx:HBox&gt; 

  &lt;mx:TileList  id="thumbList" width="100%" height="100%" doubleClickEnabled="true" doubleClick="onThumbClick()"&gt; 

   ... 

  &lt;/mx:TileList&gt; 

  &lt;/mx:Panel&gt; 

&lt;mx:Panel  title="Downloaded  Movies"  paddingBottom="5" paddingLeft="5" paddingRight="5"  paddingTop="5" width="100%" height="50%"&gt; 

  &lt;mx:HBox  height="100%"&gt; 

  &lt;mx:List  id="localList" width="170" height="100%" doubleClickEnabled="true" doubleClick="onMovieClick()"  &gt; 

   &lt;mx:itemRenderer&gt; 

   &lt;mx:Component&gt; 

   &lt;mx:HBox paddingBottom="2" paddingLeft="2"  paddingRight="2" paddingTop="2"&gt; 

   &lt;mx:Image source="{data.thumbnail.url}" height="100"  width="140" horizontalAlign="center" verticalAlign="middle"&gt; 

   &lt;/mx:Image&gt; 

   &lt;/mx:HBox&gt; 

   &lt;/mx:Component&gt; 

   &lt;/mx:itemRenderer&gt; 

  &lt;/mx:List&gt; 

  &lt;mx:VBox  verticalAlign="middle" horizontalAlign="center" width="100%" height="100%"&gt; 

  &lt;mx:VideoDisplay id="player" width="300" height="200" backgroundColor="black" /&gt; 

  &lt;mx:Button  id="btnSave" label="Save" visible="false" click="onSave()" /&gt; 

  &lt;/mx:VBox&gt; 

  &lt;/mx:HBox&gt; 

  &lt;/mx:Panel&gt; 

  &lt;/mx:VDividedBox&gt;</pre>
<p>To keep the code sample short I’ve omit a bit of the original code from the display of the movies found during the search.<br />
Don’t be put off by the quantity of tags. The MXML is quite straightforward. Most of the tags define the ‘itemRenderer’s used by the list control to show the video thumbnails. The other elements, the panels, the video display, the save button and so on, are just single elements with a few attributes to refine them.<br />
With the interface all laid out it’s time to dig into the code. The search code remains exactly the same, but we’ve now added a thumbClick event handler which is called when the user double clicks on a thumbnail in the search panel. The thumbClick handler starts the request for the HTML page associated with the YouTube video. The onHTMLComplete method receives the HTML code for the page as text. It then calls getFLVURL to get the URL for the FLV data for the movie.<br />
The code for this is shown below:</p>
<pre>&lt;mx:Script&gt; 

  &lt;![CDATA[ 

  <strong>import</strong>
 mx.rpc.events.ResultEvent; 

  <strong>import</strong>
 mx.rpc.http.HTTPService; 

  <strong>import</strong>
 com.adobe.serialization.json.JSONDecoder; 

<strong>namespace</strong>
 atom = <strong>"http://www.w3.org/2005/Atom"</strong>
; 

    <strong>namespace</strong>
 media = <strong>"http://search.yahoo.com/mrss/"</strong>
; 

<strong>private</strong>
 <strong>function</strong>
 onSearch() : <strong>void</strong>
 { ...} 

    <strong>private</strong>
 <strong>function</strong>
 onSearchResult( event:ResultEvent ) : <strong>void</strong>
 { ... } 

<strong>public</strong>
 <strong>function</strong>
 getFLVURL( sHTML:String ) : String { 

    <strong>var</strong>
 swfArgsFound:Array = sHTML.match( <strong>/var swfArgs =(.*?);/</strong>
 ); 

    <strong>var</strong>
 swfArgsJS:JSONDecoder = <strong>new</strong>
 JSONDecoder( swfArgsFound[1] ); 

    <strong>var</strong>
 swfArgs:Object = swfArgsJS.getValue(); 

    <strong>var</strong>
 url:String = <strong>&#8216;http://youtube.com/get_video.php&#8217;</strong>
; 

    <strong>var</strong>
 first:Boolean = <strong>true</strong>
; 

    <strong>for</strong>
( <strong>var</strong>
 k:String <strong>in</strong>
 swfArgs ) { 

      <strong>if</strong>
 ( swfArgs[k] != <strong>null</strong>
 &amp;&amp; swfArgs[k].toString().length &gt;  0 ) { 

        url += first ? <strong>&#8216;?&#8217;</strong>
 : <strong>&#8216;&amp;&#8217;</strong>
; 

        first = <strong>false</strong>
; 

        url += k+<strong>&#8216;=&#8217;</strong>
+escape(swfArgs[k]);    

      } 

     } 

    <strong>return</strong>
 url; 

  } 

<strong>private</strong>
 <strong>function</strong>
 onHTMLComplete( movie:Object, event:Event )  : <strong>void</strong>
 { 

    <strong>var</strong>
 loader:URLLoader = event.target <strong>as</strong>
 URLLoader; 

    <strong>var</strong>
 movieID:String = movie.id.split( /=/ )[1]; 

    <strong>var</strong>
 flvStream:URLStream = startRequest( movieID+<strong>&#8216;.flv&#8217;</strong>
,  getFLVURL( loader.data ) ); 

    startRequest( movieID+<strong>&#8216;.jpg&#8217;</strong>
,  movie.thumbnail ); 

   flvStream.addEventListener(Event.COMPLETE,<strong>function</strong>
(event:Event):<strong>void</strong>
 { 

      downloadProgress.visible = <strong>false</strong>
; 

      updateLocalVideos(); 

    } ); 

    downloadProgress.source =  flvStream; 

    downloadProgress.visible = <strong>true</strong>
; 

  } 

<strong>private</strong>
 <strong>function</strong>
 onThumbClick() : <strong>void</strong>
 { 

    <strong>var</strong>
 htmlLoader:URLLoader = <strong>new</strong>
 URLLoader(); 

     htmlLoader.addEventListener(Event.COMPLETE, 

      <strong>function</strong>
( event:Event ) : <strong>void</strong>
 { onHTMLComplete( thumbList.selectedItem,  event ); } ); 

    htmlLoader.load(<strong>new</strong>
 URLRequest(thumbList.selectedItem.id)); 

  }</pre>
<p>Let me step back for a second and talk briefly about Flash Video. YouTube, like any other site that uses a Flash player to show videos, uses FLV as the movie format. What YouTube does is provide their Flash video application with enough data for it to construct a ‘source’ URL for it’s video display object. That ‘source’ URL is get_video.php along with a set of parameters. Those parameters are stored in a Javascript variable called ‘swfVars’ which is embedded in the page.<br />
The GetFLVURL takes the Javascript from the page and extracts the ‘swfVars’. It then uses the JSONDecode class provided by the as3corelib (http://code.google.com/p/as3corelib/) to decode the Javascript into an ActionScript object. From there it construct the FLV URL from the parameters in the ActionScript object.<br />
The weakness in this example application is that it uses this ‘screen scraping’ technique to get to the FLV URL. Unfortunately there is no easier way to do it. If the format of the YouTube HTML changes, then this code might need to be rewritten to compensate for the changes.<br />
Once the FLV URL is constructed the onHTMLComplete method calls startRequest on both the thumbnail and the FLV to download the data and store it locally. The code for this is shown below:</p>
<pre><strong>private</strong>
 <strong>function</strong>
 onReqComplete( fileName:String, event:Event  ) : <strong>void</strong>
 { 

    <strong>var</strong>
 stream:URLStream = event.target <strong>as</strong>
 URLStream; 

    <strong>var</strong>
 byteLength:int = stream.bytesAvailable; 

    <strong>var</strong>
 bytes:ByteArray = <strong>new</strong>
 ByteArray(); 

    stream.readBytes( bytes, 0, byteLength  ); 

    stream.close(); 

  <strong>if</strong>
 ( File.applicationStorageDirectory.exists == <strong>false</strong>
 ) 

        File.applicationStorageDirectory.createDirectory(); 

    <strong>var</strong>
 f:File = <strong>new</strong>
 File(  File.applicationStorageDirectory.nativePath + File.separator + fileName ); 

    <strong>var</strong>
 fs:FileStream = <strong>new</strong>
 FileStream(); 

    fs.open( f, FileMode.WRITE ); 

    fs.writeBytes( bytes, 0,  byteLength ); 

    fs.close(); 

  } 

  <strong>private</strong>
 <strong>function</strong>
 startRequest( fileName:String, url:String )  : URLStream { 

    <strong>var</strong>
 req:URLStream = <strong>new</strong>
 URLStream(); 

    req.addEventListener(Event.COMPLETE, <strong>function</strong>
 ( event:Event ) : <strong>void</strong>
 { onReqComplete( fileName, event ); } ); 

    req.load( <strong>new</strong>
 URLRequest( url ) ); 

    <strong>return</strong>
 req; 

  }</pre>
<p>The startRequest builds a URLStream object to get the data for the given URL. It then sets up onReqComplete as an event handler for when the download is complete. The onReqComplete uses the AIR File API to store the data, which is read directly into an AIR ByteArray class, into a file stored in the application storage directory. The application storage directory is maintained by AIR automatically for you.<br />
Once the files are downloaded the updateLocalVideos method is called. This method, shown below, updates the list of local videos in the downloaded videos panel.</p>
<pre><strong>private</strong>
 <strong>function</strong>
 updateLocalVideos() : <strong>void</strong>
 { 

    <strong>var</strong>
 fileNames:Object = <strong>new</strong>
 Object(); 

    <strong>for</strong>
 <strong>each</strong>
 ( <strong>var</strong>
 file:File <strong>in</strong>
 File.applicationStorageDirectory.getDirectoryListing() ) { 

      <strong>var</strong>
 fName:String = file.name.split( <strong>/[.]/</strong>
 )[0]; 

      fileNames[ fName ] = <strong>true</strong>
; 

    } 

    <strong>var</strong>
 movieList:Array = []; 

    <strong>for</strong>
( <strong>var</strong>
 fileKey:String <strong>in</strong>
 fileNames ) { 

      <strong>var</strong>
 thumb:File = <strong>new</strong>
 File(  File.applicationStorageDirectory.nativePath + File.separator + fileKey + <strong>&#8216;.jpg&#8217;</strong>
 ); 

      <strong>var</strong>
 movie:File = <strong>new</strong>
 File(  File.applicationStorageDirectory.nativePath + File.separator + fileKey + <strong>&#8216;.flv&#8217;</strong>
 ); 

      <strong>if</strong>
 ( thumb.exists &amp;&amp; movie.exists ) 

        movieList.push( {  thumbnail: thumb, movie: movie } ); 

    } 

    localList.dataProvider =  movieList; 

  }</pre>
<p>To do that the method uses the getDirectoryListing method, provided by the AIR File API, to get all of the files in the application storage directory. It then chops off the extensions and creates a list of just the file names. From there builds the list of local movies by iterating through the file names and checking to make sure that both the ‘.flv’ file for the video, and the ‘.jpg’ file for the thumbnail, are available.<br />
With the list of local videos in hand the method sets the dataProvider of the local list to the movie list that it generated.<br />
From there the final few functions handle the playback and the saving of the FLV to the desktop. These methods are shown below:</p>
<pre><strong>private</strong>
 <strong>function</strong>
 onMovieClick() : <strong>void</strong>
 { 

    player.source =  localList.selectedItem.movie.url; 

    btnSave.visible = <strong>true</strong>
; 

  } 

  <strong>private</strong>
 <strong>function</strong>
 onSave() : <strong>void</strong>
 { 

    <strong>var</strong>
 f:File = File.desktopDirectory; 

    f.addEventListener(Event.SELECT,onSaveSelect); 

    f.browseForSave(<strong>&#8220;Save FLV&#8221;</strong>
); 

  } 

  <strong>private</strong>
 <strong>function</strong>
 onSaveSelect( event:Event ) : <strong>void</strong>
 { 

    <strong>var</strong>
 f:File = event.target <strong>as</strong>
 File; 

    <strong>var</strong>
 lf:File = localList.selectedItem.movie <strong>as</strong>
 File; 

    lf.copyTo( f, <strong>true</strong>
 ); 

  } 

  ]]&gt; 

  &lt;/mx:Script&gt; 

&lt;/mx:WindowedApplication&gt;</pre>
<p>The onMovieClick method is called when the user double clicks on a movie in the local list. It just sets the source of the VideoDisplay to the url of the local ‘.flv’ file.<br />
The onSave method is called when the user clicks the Save button. It pops up a Save dialog using the AIR File API. If the user hits Save then the onSaveSelect method is called which copies the ‘.flv’ file from the local storage directory to the location they specify. You can see the interface for this in action in Figure 3.</p>
<p align="center"><img src="http://thetechlabs.com/wp-content/uploads/2008/06/fig3.png" border="0" alt="" width="491" height="253" /></p>
<p align="center"><strong>Figure 1-3. The save window</strong></p>
<p>On it’s face it seems like a lot of code, but it’s really not all that daunting when you break it down into it’s component pieces.</p>
<h3>Your Next Steps</h3>
<p>I hope you can leverage the code that I have presented in this article in your own work. There are some good reusable fragments including the file request and storage code in on ReqComplete. The JSON parsing in GetFLVURL can also come in handy when dealing with websites that lack a web services interface. Or in this case, have a web service interface that lacks the information you require.<br />
If you do make use of this code, be sure to let me know. You can contact me directly through my website; http://jackherrington.com.</p>
<p class="addtoany_share_save_container">
    <a class="a2a_dd addtoany_share_save" onmouseover="a2a_show_dropdown(this)" onmouseout="a2a_onMouseOut_delay()" href="http://www.addtoany.com/share_save?sitename=The%20Tech%20Labs&amp;siteurl=http%3A%2F%2Fwww.thetechlabs.com%2F&amp;linkname=Creating%20A%20Downloader%20For%20YouTube%20with%20Flex%2FAir&amp;linkurl=http%3A%2F%2Fwww.thetechlabs.com%2Fvideo%2Fcreating-a-downloader-for-youtube-with-flexair-2%2F" onclick="javascript:pageTracker._trackPageview('oclicks/http://www.addtoany.com/share_save?sitename=The%20Tech%20Labs&amp;siteurl=http%3A%2F%2Fwww.thetechlabs.com%2F&amp;linkname=Creating%20A%20Downloader%20For%20YouTube%20with%20Flex%2FAir&amp;linkurl=http%3A%2F%2Fwww.thetechlabs.com%2Fvideo%2Fcreating-a-downloader-for-youtube-with-flexair-2%2F');"><img src="http://www.thetechlabs.com/wp-content/plugins/add-to-any/share_save_171_16.gif" width="171" height="16" alt="Share/Save/Bookmark"/></a>

	</p><img src="http://feedproxy.google.com/~r/TheTechLabs_air/~4/ESHudCfZYhQ" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.thetechlabs.com/video/creating-a-downloader-for-youtube-with-flexair-2/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.thetechlabs.com/video/creating-a-downloader-for-youtube-with-flexair-2/</feedburner:origLink></item>
		<item>
		<title>How to build a contact manager in AIR using XML</title>
		<link>http://feedproxy.google.com/~r/TheTechLabs_air/~3/I6XsYC60ipc/</link>
		<comments>http://www.thetechlabs.com/xml/how-to-build-a-contact-manager-in-air-using-xml/#comments</comments>
		<pubDate>Sat, 24 May 2008 22:39:17 +0000</pubDate>
		<dc:creator>Pedro Furtado</dc:creator>
		
		<category><![CDATA[AIR]]></category>

		<category><![CDATA[Flex]]></category>

		<category><![CDATA[XML]]></category>

		<category><![CDATA[adobe]]></category>

		<category><![CDATA[contact manager]]></category>

		<category><![CDATA[how-to]]></category>

		<category><![CDATA[ria]]></category>

		<guid isPermaLink="false">http://thetechlabs.com/?p=3</guid>
		<description><![CDATA[<p>This will be the first part of our first tutorial: How to build a contact manager in AIR using XML. For the first part of this tutorial we’ll be reading, parsing and searching an XML into an AIR application and going through the whole getting it to compile and exporting a release version.</p>
<small><em>posted in <a href="http://www.thetechlabs.com/category/air/">AIR</a> by Pedro Furtado <a href="http://www.thetechlabs.com/xml/how-to-build-a-contact-manager-in-air-using-xml/#comments">Leave A Comment</a><br />&copy;2008 <a href="http://www.thetechlabs.com">The Tech Labs</a>. All Rights Reserved.</em></small>]]></description>
			<content:encoded><![CDATA[<p>Hello from <a href="http://www.dreaminginflash.com/" onclick="javascript:pageTracker._trackPageview('oclicks/http://www.dreaminginflash.com/');" target="_blank">Dreaming in flash’s team</a></p>
<p>We’ve been invited by Carlos to make some tutorials on what we know best, Flash and Flex.</p>
<p>This will be the first part of our first tutorial: How to build a contact manager in AIR using XML.</p>
<p>For the first part of this tutorial we’ll be reading, parsing and searching an XML into an AIR application and going through the whole getting it to compile and exporting a release version. So first things first, you’ll need Flex 3 SDK, or Flex 3 IDE, that you can get for free over at Adobe, or the 30 day trial if you go for the builder.</p>
<p>After you’ve installed it we’re good to go. Open Flex, and create a new Flex Project. Select the Desktop Application option and we’re all set to start coding.</p>
<p style="text-align: center;"><img class="alignnone size-thumbnail wp-image-1349" style="border: 0pt none; vertical-align: top; margin-top: 10px; margin-bottom: 10px;" src="http://flashenabled.files.wordpress.com/2008/05/new-air-project.jpg?w=225&amp;h=196" alt="" width="225" height="196" /></p>
<p style="text-align: left;">
<p>First we must start by adding an initialize handler to our application so that we know when we can start working.</p>
<pre>&lt;mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" initialize="{init()}" layout="absolute" viewSourceURL="srcview/index.html" height="451"&gt;
&lt;!-- // --&gt;
&lt;mx:Script&gt;
&lt;![CDATA[
<strong>import</strong> mx.collections.XMLListCollection;</pre>
<p>Then we create our variables and initiate them, set an Event handler to handle the loading of the XML File, and start the loading process.</p>
<pre><strong>private</strong> <strong>var</strong> myXml:XML;
<strong>private</strong> <strong>var</strong> myLoader:URLLoader;
<strong>private</strong> <strong>var</strong> myContactsCol:XMLListCollection;</pre>
<p>The handler is simple, it casts the received data to an XML Object and initializes the XMLListCollection to our full data. (More on why we use a XmlListCollection on part 2)</p>
<pre><strong>public</strong> <strong>function</strong> init():<strong>void</strong>
{
<strong>var</strong> myReq:URLRequest = <strong>new</strong> URLRequest(<strong>'data.xml'</strong>);
myLoader = <strong>new</strong> URLLoader()
myLoader.addEventListener(Event.COMPLETE, dataComplete);
myLoader.load(myReq);
}</pre>
<p>Now here's where the 'magic' happens the search and updating of our datagrid.</p>
<pre><strong>private</strong> <strong>function</strong> dataComplete(e:Event):<strong>void</strong>
{
myXml = <strong>new</strong> XML(myLoader.data.toString());
myContactsCol = <strong>new</strong> XMLListCollection(myXml..contact);
myDg.dataProvider = myContactsCol;
myDg.invalidateList()
}</pre>
<p>I know this line might look scary but it really isn't, it's simple E4X at work, what we do is to get an XMLList out of our xml where the name node of each contact node matches our expression, in this case any name that contains our search string. And then we refresh the visual part of the datagrid.</p>
<pre><strong>private</strong> <strong>function</strong> updateList():<strong>void</strong>
{
myContactsCol = <strong>new</strong> XMLListCollection(myXml..contact.(name.toLowerCase().indexOf(search_txt.text.toLowerCase()) != -1 ))
myDg.dataProvider = myContactsCol;
myDg.invalidateList()
}
]]&gt;
&lt;/mx:Script&gt;</pre>
<p>Now for the MXML part of our application, it’s simple enough we drag our datagrid to our workspace, name it and set the names for it’s columns, note the dataField property this related directly to the name of the XML Nodes.</p>
<pre>&lt;mx:DataGrid bottom="60" top="10" left="10" id="myDg"&gt;
&lt;mx:columns&gt;
&lt;mx:DataGridColumn headerText="Name" dataField="name"/&gt;
&lt;mx:DataGridColumn headerText="Telphone" dataField="telephone"/&gt;
&lt;mx:DataGridColumn headerText="Email" dataField="email"/&gt;
&lt;/mx:columns&gt;
&lt;/mx:DataGrid&gt;
&lt;mx:Label x="10" text="Search:" bottom="10"/&gt;
&lt;mx:TextInput x="65" id="search_txt" bottom="10"/&gt;</pre>
<p>And finally we just set the click handler for the GO! button to update our search results.</p>
<pre>&lt;mx:Button x="233" label="Go!" id="go_btn" click="{updateList()}" bottom="10"/&gt;
&lt;/mx:WindowedApplication&gt;</pre>
<p>Now in order to publish the application we need to create a certificate (or use an existing one), this can be easily done by using create new certificate option, which will create an unsigned certificate, or by getting one from Twathe or Verisign.</p>
<p style="text-align: center;"><img class="alignnone size-thumbnail wp-image-1350 aligncenter" src="http://flashenabled.files.wordpress.com/2008/05/export-release-build.jpg?w=224&amp;h=204" alt="" width="224" height="204" /></p>
<p style="text-align: center;"><img class="alignnone size-thumbnail wp-image-1347 aligncenter" src="http://flashenabled.files.wordpress.com/2008/05/create-new-certificate.jpg?w=227&amp;h=112" alt="" width="227" height="112" /></p>
<p style="text-align: left;">
<p>After that you just save your certificate, select it and hit finish!<span class="apple-converted-space"> </span></p>
<p>We now have a fully working Air application.</p>
<p>In part 2 we’ll go through Editing and saving the file, how to set up our bindings so that both the Xml, and the results are updated automatically, go through the data types that we use and how to use an auto-updating library for your application.</p>
<p><a href="http://thetechlabs.com/flashenabledblog/tutorials/FEXMLContacts.zip" onclick="javascript:pageTracker._trackPageview('downloads/flashenabledblog/tutorials/FEXMLContacts.zip');" target="_blank">Download the support files</a></p>
<p class="addtoany_share_save_container">
    <a class="a2a_dd addtoany_share_save" onmouseover="a2a_show_dropdown(this)" onmouseout="a2a_onMouseOut_delay()" href="http://www.addtoany.com/share_save?sitename=The%20Tech%20Labs&amp;siteurl=http%3A%2F%2Fwww.thetechlabs.com%2F&amp;linkname=How%20to%20build%20a%20contact%20manager%20in%20AIR%20using%20XML&amp;linkurl=http%3A%2F%2Fwww.thetechlabs.com%2Fxml%2Fhow-to-build-a-contact-manager-in-air-using-xml%2F" onclick="javascript:pageTracker._trackPageview('oclicks/http://www.addtoany.com/share_save?sitename=The%20Tech%20Labs&amp;siteurl=http%3A%2F%2Fwww.thetechlabs.com%2F&amp;linkname=How%20to%20build%20a%20contact%20manager%20in%20AIR%20using%20XML&amp;linkurl=http%3A%2F%2Fwww.thetechlabs.com%2Fxml%2Fhow-to-build-a-contact-manager-in-air-using-xml%2F');"><img src="http://www.thetechlabs.com/wp-content/plugins/add-to-any/share_save_171_16.gif" width="171" height="16" alt="Share/Save/Bookmark"/></a>

	</p><img src="http://feedproxy.google.com/~r/TheTechLabs_air/~4/I6XsYC60ipc" height="1" width="1"/>]]></content:encoded>
			<wfw:commentRss>http://www.thetechlabs.com/xml/how-to-build-a-contact-manager-in-air-using-xml/feed/</wfw:commentRss>
		<feedburner:origLink>http://www.thetechlabs.com/xml/how-to-build-a-contact-manager-in-air-using-xml/</feedburner:origLink></item>
	</channel>
</rss>
