Postcards From My Life

Lint I find in my mind's belly-button.
  • EPK
  • Consulting
  • Resume
  • Nerd Herding
  • Talks
  • Flex
  • Zend Framework

Posts Tagged ‘adobe’

Putting all the pieces together

Monday, August 9th, 2010

Dear Reader,

I have posted several posts on this blog that all derived from a sample app that I wrote to generate QR Codes using the Google API. So far I’ve only give you the individual pieces, this post is about pulling them all together and creating the application.

I have been working on this particular application since February. (I used it to generate the QR Code I sent to the lovely and talented Kathy for Valentine’s Day. (Yeah, I’m that much of a geek) If you are a regular reader then you will recognize some of the code and concepts that we have discussed previously.

The MXML can be found below this post, and a link to the FXP can be found just above it. If you have Flash Builder 4, grab the FXP and you can import it and see all the code.

Converting Flex Builder 3 to Flash Builder 4

This project actually started life as a Flex Builder 3 project. I did the normal Export/Import to get the project into Flash Builder 4 and this worked just fine. The code compiled and ran immediately. (Kudos to the Flash Builder 4 team for their work in backwards compatibility)

All was not perfect however. One of the cool new things about Flash Builder 4 is the Themes option. If you use the Spark components then themes will allow you to quickly change the look and feel of your application. If you right click on the project itself and then select Properties, you will notice a “Flex Theme” option.

From this dialog box you can choose from 13 different pre-defined themes and you can design your own.

Unfortunately, since I imported this app from Flex Builder 3, none of my visual controls were spark controls. The results of applying a theme was comical. Thanks to the code completion and some trial and error though, I was able to convert most of the controls over to spark. In most cases, the code completion actually told me “Don’t use X, use Y” which is a VERY NICE feature. The remaining MX controls did not have an obvious spark counterpart or the suggested counterparts didn’t work the way I wanted them to.

Setup and cleanup

A good portion of the Actionscript code in the MXML falls into 2 categories, either setup code or cleanup code.

Setup

[Bindable] protected var qrCode:QRCode = new QRCode();
 
var imageWindow:ImageWindow;
 
 protected function init():void
 {
         /*
         * add our event listeners
         */
         qrCode.addEventListener('imageCreated',this.displayImage);
         qrCode.addEventListener('imageFailed',this.cannotDisplayImage);
 } // protected function init():void

Here we create the QRCode class described in “Actionscript class to create QR Codes“. In the init() we add our event listeners for a successful image fetch and a fail.

Cleanup

		protected function destroy():void
		{
			try {
				imageWindow.close();
				imageWindow=null;
			} catch (e:Error) {
 
			}
		}

Here in destroy() we only do one thing, we close any open windows. Since this app only has one window, we only have to hard-code a close here. However, since the window may not be open, I’ve wrapped it in a try/catch so that if the window is not yet open, we handle the error. (Actually, we ignore the error)

The heart of the app

createImage() is the heart of the app. The MXML below it is just display code setting things up so that users can turn all the knobs and push all the buttons. Once they have everything the way they want, we call createImage().

the majority of the code is a big switch statement that puts together the right string to encode. The last one is the most difficult, the VCARD. [Side Note: Since I couldn't find an Actionscript VCARD implementation, I may write one in a future post.]

       /*
        * Setup for the call
        */
       qrCode.width                = parseInt(imageWidth.text);
       qrCode.height               = parseInt(imageHeight.text);
       qrCode.margin               = margin.selectedIndex;
       qrCode.errorCorrectionLevel = errorCorrectionLevel.selectedIndex;
       qrCode.content              = contentToEncode; 			
       qrCode.imageType            = grpImageType.selectedValue.toString();
 
 
       /*
        * make the call
        */
       qrCode.createImage();

Once we know what we are encoding, the code above actually makes the call. We set the properties for the qrCode and the call qrCode.createImage().

In the init() we set event listeners for both a successful image retrieval and a failure. Since every developer knows their code doesn’t fail, let’s talk about the success.

protected function displayImage(event:Event):void
{
       var thisImage:Image = event.target.getImage();
       var winX:int = -1; 
       var winY:int = -1;
 
       try {
               winX=imageWindow.nativeWindow.x;
               winY=imageWindow.nativeWindow.y;
               imageWindow.close();
               imageWindow = null;
       } catch (e:Error) {
 
       }
       imageWindow = new ImageWindow(thisImage,grpImageType.selectedValue.toString());
       if (winX>-1) {
               imageWindow.open();
               imageWindow.nativeWindow.x=winX;
               imageWindow.nativeWindow.y=winY;
       } else {
               imageWindow.open();
       }
       return;
} // protected function displayImage(event:Event):void

Again, as with in destroy() we try to close the window. However, before we do so, we save off the X and Y of the window in case the user has moved it.

Then we create a new ImageWindow, passing in the image and the type of image to save. Finally, we position it. We have to position it after the call to open because before open is called, the nativeWindow property doesn’t exist.

The package store

Now that everything is working, we need to build it, package it and get it ready to ship. Up till now, when we have been compiling the application, we have been using the F11 key, which fired the debug version. That is why we can only have one instance running at a time. If you use CTRL-F11 to run your app, it won’t include the debug code and won’t try to attach itself to the debugger. However, to package it and ship it you have a few more steps.

Code signing

The last thing most AIR developers seem to think about is that they need a certificate to sign their code. It is a topic that serious application developers should tackle. I looked around at the web and found this page as the best reference for purchasing code signing certificates. The only one I could find that is not listed on that chart is GoDaddy. Unlike their SSL certificates, GoDaddy’s code signing certificates aren’t cheap. They start at $199/year.

No matter which service you go with, if you plan on distributing your application, you will need a certificate. For the purposes of this demonstration I will use a self signed certificate to package my application. This is not a recommended practice. (I’m just too cheap to pop for a cert for this tutorial)

To begin the export journey, click on the Project menu and select “Export Release Build”. This will bring up the first screen in the export wizard.

In this first screen we usually accept the defaults for everything. The one option of interest is the “Enable View Source” check box. Even though we are building an AIR application, we can still make our source code available for inspection. Checking the box enables the “Choose Source Files” button and you can decide to allow the user to browse the entire application or just certain files. When the user runs your application, a “View Source” option is added to the right click menu and if they choose it, they will see a code browser that looks like this:

As we discussed in the Code Signing section above, all AIR apps must be signed with a code signing certificate. Clicking “Next” on the wizard will bring up the code signing screen of the Wizard.

This is the screen where you select your certificate. If you are just testing, or distributing your application internally, you can choose to Create a certificate. While this will allow you to self-sign your application, when you install your users will see an ugly, scarry screen telling them that AIR via your operating system does not recognize the certificate of the publisher and thus cannot vouch for them.

This is generally not what you want when it comes to your customers.

You do have the option of creating an AIRI file. This is an intermediate file that you can hand off for signing. This is the option you would want if your company has a central authority that controls the signing certificates. They will sign the AIRI file using the command line utility adt and hand you back an AIR file ready to distribute.

Clicking “Next” will actually begin the export process. If you have warnings in your application (as the QRCodeMaker does) it will bring up a dialog box alerting you to those warnings and allowing you to cancel the process at that point and deal with them or continue on. Since ours is benign, I have chosen to ignore it.

The final step in the process is to choose which files get included the AIR or AIRI file. I assume this is to allow you to exclude common libraries and simply link to them. Our application is simple enough so that we just select the default which is the XML meta file and the generated SWF.

Clicking “Finish” will complete the export of your AIR or AIRI and you are ready to distribute or sign accordingly.

Conclusion

Developing desktop applications (and I assume mobile apps as wel) is never easy. Even trivial apps like QRCodeMaker require pre-planning to complete without several refacortings. The Flex framework, while verbose (as pointed out by my friends Nate Abele and Ed Finkler in the comments of the post “Actionscript class to create QR Codes“) is no more verbose than most modern frameworks. This is a problem I have with most modern frameworks for the languages with which I have worked. (Java, PHP and ActionScript) It is not necessarily a bad thing to be overly verbose but it does increase the learning curve on these frameworks.

The point of these posts has not been to show you best practices or even recommended practices. I myself am still discovering those. My point was to show you the process I went through to get this application running, my thought process behind some of the decisions I made, and to start a discussion.

I hope you will take the code I’ve shared with you, tinker with it, make it better and then share it with others.

Until next time,
I <3 |<
=C=

Disclosure: I work with Blue Parabola, Adobe is a customer of Blue Parabola.

Right Click to download the entire project
Completed (but self-signed) application.

QRCodeMaker.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009" 
						xmlns:s="library://ns.adobe.com/flex/spark" 
						xmlns:mx="library://ns.adobe.com/flex/mx" 
						xmlns:net="flash.net.*" 
	                    creationComplete="init();" 
	                    width="250" 
	                    height="400"  
						showStatusBar="false"
						backgroundColor="#F0F0F0"
						closing="destroy()">
	<s:layout>
		<s:BasicLayout/>
	</s:layout>
 
	<fx:Declarations>
		<s:RadioButtonGroup id="grpImageType" />
	</fx:Declarations>
 
	<fx:Script>
	<![CDATA[
 
		import mx.controls.Alert;
		import mx.controls.Image;
		import mx.rpc.events.FaultEvent;
		import mx.rpc.events.ResultEvent;
		[Bindable] protected var qrCode:QRCode = new QRCode();
 
		var imageWindow:ImageWindow;
 
		 protected function init():void
		 {
			 /*
			 * add our event listeners
			 */
			 qrCode.addEventListener('imageCreated',this.displayImage);
			 qrCode.addEventListener('imageFailed',this.cannotDisplayImage);
		 } // protected function init():void
 
		protected function destroy():void
		{
			try {
				imageWindow.close();
				imageWindow=null;
			} catch (e:Error) {
 
			}
		}
		 protected function createImage():void
		 {
 
			var contentToEncode:String = ""; 
 
			/*
			 * Figure out which type of content we are encoding
			 */
			switch(tabBar.selectedIndex) {
		 		case 0:
		 			// TEXT
					contentToEncode  = this.textToEncode.text;
					break;
 
		 		case 1:
		 			// URL
					contentToEncode  = this.emailToEncode.text;
					break;
 
		 		case 2:
		 			// Email address
					contentToEncode  = 'MAILTO:'+this.urlToEncode.text;
					break;
 
		 		case 3:
		 			// vCard
		 		/*
				 * http://en.wikipedia.org/wiki/VCard
				 */
					contentToEncode = "BEGIN:VCARD\n" +
					                  "VERSION:3.0\n" +
					                  "N:"+txtLName.text+';'+txtFName.text+"\n" +
					                  "FN:"+txtFName.text+' '+txtLName.text+"\n" +
					                  "ADR:"+txtAddress.text+';'+txtCity.text+';'+txtState.text+';'+txtPostalCode.text+"\n" +
					                  "TEL:"+txtPhone.text+"\n" +
					                  "EMAIL:"+txtEmail.text+"\n" +
					                  "URL:"+txtUrl.text+"\n" +
					                  "END:VCARD\n";
					break;		 		
		 	}
 
			/*
			 * Setup for the call
			 */
			qrCode.width                = parseInt(imageWidth.text);
			qrCode.height               = parseInt(imageHeight.text);
			qrCode.margin               = margin.selectedIndex;
			qrCode.errorCorrectionLevel = errorCorrectionLevel.selectedIndex;
			qrCode.content              = contentToEncode; 			
			qrCode.imageType            = grpImageType.selectedValue.toString();
 
 
			/*
			 * make the call
			 */
			qrCode.createImage();
			return;			
		 } // protected function createImage():void
 
 
		 protected function displayImage(event:Event):void
		 {
			var thisImage:Image = event.target.getImage();
			var winX:int = -1; 
			var winY:int = -1;
 
			try {
				winX=imageWindow.nativeWindow.x;
				winY=imageWindow.nativeWindow.y;
				imageWindow.close();
				imageWindow = null;
			} catch (e:Error) {
 
			}
			imageWindow = new ImageWindow(thisImage,grpImageType.selectedValue.toString());
			if (winX>-1) {
				imageWindow.open();
				imageWindow.nativeWindow.x=winX;
				imageWindow.nativeWindow.y=winY;
			} else {
				imageWindow.open();
			}
		 	return;
		 } // protected function displayImage(event:Event):void
 
 
		 protected function cannotDisplayImage(event:FaultEvent):void
		 {
		 	Alert.show(event.toString());
		 } // protected function cannotDisplayImage(event:FaultEvent):void
 
	]]>
</fx:Script>
 
	<s:HGroup width="100%" height="100%">
		<mx:Spacer />
 
		<s:VGroup width="100%" height="100%">
			<mx:Spacer />
 
 
			<s:HGroup width="100%">
				<mx:Label text="Height"/>
				<mx:TextInput id="imageHeight" width="60" text="150" />
				<mx:Label text="Width"/>
				<mx:TextInput id="imageWidth" width="60" text="150" />
			</s:HGroup>
 
			<s:HGroup width="100%">
				<s:Label text="Quality" />
				<s:ComboBox width="50" 
							id="errorCorrectionLevel" 
							selectedIndex="1" 
							dataProvider="{qrCode.qualityArray}" />
 
 
				<s:Label text="Margin"/>
				<s:ComboBox  
					width="50"
					selectedIndex="4" 
					id="margin" 
					dataProvider="{qrCode.marginArray}"  />
			</s:HGroup>
 
			<s:VGroup gap="0" width="100%">
 
				<s:TabBar id="tabBar"  width="100%" dataProvider="{viewstack1}" />
 
				<mx:ViewStack 
					id="viewstack1" 
					width="100%" 
					height="225" >
					<s:NavigatorContent label="Text" width="100%" enabled="true" >
						<s:BorderContainer width="100%" height="100%" backgroundColor="#F0F0F0">
 
							<s:VGroup width="100%">
								<mx:Spacer  />
								<s:Label text="Enter Your Text Here" />
								<s:TextArea height="100%" width="95%" id="textToEncode" text=""/>
							</s:VGroup>
						</s:BorderContainer>
					</s:NavigatorContent>
 
					<s:NavigatorContent label="Email" width="100%" height="100%">
						<s:BorderContainer width="100%" height="100%" backgroundColor="#F0F0F0">
							<s:VGroup width="100%">
								<mx:Spacer />
								<s:Label text="Enter Your Email Here" />
								<s:TextInput width="95%" id="emailToEncode" text="mailto:" height="18"/>
							</s:VGroup>
						</s:BorderContainer>						
					</s:NavigatorContent>
 
					<s:NavigatorContent label="URL" width="100%" height="100%">
						<s:BorderContainer width="100%" height="100%" backgroundColor="#F0F0F0">
							<s:VGroup>
								<mx:Spacer />
								<s:Label text="Enter Your URL Here" />
								<s:TextInput width="95%" id="urlToEncode" text="" height="18"/>							
							</s:VGroup>
						</s:BorderContainer>						
					</s:NavigatorContent>
 
					<s:NavigatorContent label="VCard" width="100%" height="100%">
						<s:BorderContainer width="100%" height="100%" backgroundColor="#F0F0F0">
							<s:VGroup width="100%" height="100%"  gap="1" >
								<s:HGroup width="100%">
									<mx:Label text="First Name" width="75"/>
									<s:TextInput id="txtFName"/>
								</s:HGroup>
								<s:HGroup width="100%">
									<s:Label text="Last Name" width="75"/>
									<s:TextInput id="txtLName"/>
								</s:HGroup>
								<s:HGroup width="100%">
									<s:Label text="Address" width="75"/>
									<s:TextInput id="txtAddress"/>
								</s:HGroup>
								<s:HGroup width="100%">
									<s:Label text="City" width="75"/>
									<s:TextInput id="txtCity"/>
								</s:HGroup>
								<s:HGroup width="100%">
									<s:Label text="State" width="75"/>
									<s:TextInput id="txtState"/>
								</s:HGroup>
								<s:HGroup width="100%">
									<s:Label text="Postal Code" width="75"/>
									<s:TextInput id="txtPostalCode"/>
								</s:HGroup>
								<s:HGroup width="100%">
									<s:Label text="Phone" width="75"/>
									<s:TextInput id="txtPhone"/>
								</s:HGroup>
								<s:HGroup width="100%">
									<s:Label text="Email" width="75"/>
									<s:TextInput id="txtEmail"/>
								</s:HGroup>
								<s:HGroup width="100%">
									<s:Label text="URL"  width="75"/>
									<s:TextInput id="txtUrl"/>
								</s:HGroup>
							</s:VGroup>
						</s:BorderContainer>						
					</s:NavigatorContent>					
				</mx:ViewStack>
			</s:VGroup>
 
 
 
			<s:HGroup width="100%" horizontalAlign="center">				
				<s:RadioButton label="PNG" groupName="grpImageType" selected="true"/>
				<s:RadioButton label="JPG" groupName="grpImageType" />
			</s:HGroup>
			<s:Button label="Create QRCode" width="100%" id="drawImage" click="createImage()"/>
		</s:VGroup>
		<mx:Spacer />
 
	</s:HGroup>
 
</s:WindowedApplication>

Tags: actionscript, adobe, code signing, flash, flex, packaging, qr code
Posted in Flex, Programming | Comments Off

 

Working with windows in Flex

Monday, August 2nd, 2010

Dear Reader,

I am putting the finishing touches on my reference application to demonstrate my wrapper for Google’s QR Code API. While doing so I decided to see what it would take to have the image pop up in a new window. Originally I had it displaying on the right side of the main window but aesthetically, that just didn’t do it for me.

“A journey of a thousand steps begins with a subclass…”

I created an ActionScript subclass of the spark Window class thinking that I would then subclass it as MXML and do the visual parts. This technique called “Code Behind” is considered a best practice when building Flex components. However, once I started working on it, I realized that I only needed 2 graphical elements. It just seemed easier to code it all in AS.

The Constructor

The main body of code in this window is in the constructor. We hand in the two pieces of information that it needs to set things up, the actual image to display and the type of image to save. In the reference application, the user is presented with 2 options for image type, PNG and JPG. It should be noted that I don’t actually validate that here although down below, if it is not a PNG then the user is getting a JPG, regardless of what they specified.

public function ImageWindow(thisImage:Image,thisImageType:String)
{
	super();
	this.showStatusBar=false;

I always turn of the status bar on windows that aren’t the main application window. Unless I know I am going to use it, I turn it off on the main application window as well. For the most part, it is just wasted screen real estate.

In this next section, we actually create the 2 graphical elements of the window, the image itself and the Save button. After we create them, we create a VGroup to position them for us. Once we have added both the image and the button to the VGroup, we add the vGroup to the Window so it and all of it’s elements will display.

/* 
 * Image
 */
imageType           = thisImageType;
qrCodeImage         = thisImage;
qrCodeImage.x       = 0;
qrCodeImage.y       = 0;
this.width          = qrCodeImage.width+10;
this.height         = qrCodeImage.height+30;
qrCodeImage.visible = true;
 
 
/* 
 * Button
 */
btnSave.label   = "Save";
btnSave.y       = thisImage.height+10;
btnSave.x       = 0;
btnSave.visible = true;
btnSave.addEventListener(MouseEvent.CLICK,imageSave);
 
 
/* 
 * Vertical Group
 */
var vg:VGroup = new VGroup();
vg.horizontalAlign="center";
vg.addElement(qrCodeImage);
vg.addElement(btnSave);
this.addElement(vg);
return;
} // public function ImageWindow(thisImage:Image,thisImageType:String)

Saving the image

This code was originally in the main application. It didn’t really belong to the QRCode.as but it never felt right in the main application either. When I created ImageWindow.as, I realized I had found it it’s home. imageSave() is called when the Save button is clicked.

The first thing it does is create a BitmapData and load the image into it. Next it determines the type of image to create. As I said earlier, even though PNG and JPG are valid options, the way the code reads, it is either PNG or you are getting a JPG. Once the image is encoded into a ByteArray and a default name is set, we use the FileReference class to save it. This will open the operating system’s native Save As dialog and allow you to change the name and place it in the proper folder.

The final thing we do here is close the window. If you have saved it, that is really all you can do in this window. So you might as well get rid of it.

protected function imageSave(event:Event):void
{
        var bmpd:BitmapData = new BitmapData(qrCodeImage.width,qrCodeImage.height);
        bmpd.draw(qrCodeImage);
 
        var imgByteArray:ByteArray = null;
        var fileName:String  = "";
 
        if (imageType=="PNG") {
                var pngenc:PNGEncoder = new PNGEncoder();
                imgByteArray = pngenc.encode(bmpd);				
                fileName = "qrCode.png";
        } else {
                var jpgenc:JPEGEncoder = new JPEGEncoder(100);
                imgByteArray = jpgenc.encode(bmpd);				
                fileName = "qrCode.jpg";
        } // if (grpImageType.selectedValue=="PNG")
 
        var file:FileReference = new FileReference();
        file.save(imgByteArray, fileName);
        this.close();			
} // protected function imageSave(event:Event):void

Using it

Ok, enough with the subclasses, lets look at how this works in an actual application.

The first thing we need is some place to store our Window. I usually do one of two things. If I know I will need more than one window, I usually create an array in the application global space. Then I can just add my windows to the array. If I know I am only going to use one, as in this case, I simply create a single variable to hold it, again in the global application space. I do not pretend that this is a best practice – the practice of storing variables in the global application space – however, for small projects such as this, it works. Regardless of how you do it, you need to make sure you have a handle to the window so that you can close it later. Otherwise, your application may not properly close and exit.

var imageWindow:ImageWindow;

When I am ready to show the graphic, it is a simple matter of instantiating the IageWindow class, passing in the image and the image type as parameters to the constructor.

Well, it would be simple if it weren’t for the fact that I am re-using the same window and to be nice, I want to make sure that if the user has moved the window off to one side, I honor their wishes and keep it there whenever I refresh it. What I end up with looks like this.

protected function displayImage(event:Event):void
{
       var thisImage:Image = event.target.getImage();
       var winX:int = -1; 
       var winY:int = -1;

Here is where we store off the current window’s X and Y coordinates. If the window doesn’t exist, this will throw and error. Since we aren’t really interested in the error, there is nothing in the catch. The final thing we do is close the existing window and null it out.

It was interesting (and by interesting I be frustrating to the point of drinking) that the window’s X and Y properties were always 0. You hae to use the X and Y coordinates of the nativeWindow property to actually find/affect the position of the window.

       try {
               winX=imageWindow.nativeWindow.x;
               winY=imageWindow.nativeWindow.y;
               imageWindow.close();
               imageWindow = null;
       } catch (e:Error) {               
       }

Now we create the new window with the new image. For those wondering, yes, I could have simply re-used the existing window each time and rebuilt the graphic. If I were doing this for anything other than demonstration purposes, I would have. However, I wanted to show positioning windows in this post so I did it this way.

       imageWindow = new ImageWindow(thisImage,grpImageType.selectedValue.toString());
       if (winX>-1) {
               imageWindow.open();
               imageWindow.nativeWindow.x=winX;
               imageWindow.nativeWindow.y=winY;
       } else {
               imageWindow.open();
       }
       return;
} // protected function displayImage(event:Event):void

Conclusion

That’s it. The more I work on my reference application for my QR Code creator class, the smaller the actual application becomes. Moving things like the imageSave() into the control that actually deals with the image makes more sense to me. If you are curious, I did not put the imageSave method in QRCode.as for a couple of reasons. The main one was that imageSave uses the FileReference class; that is an AIR specific class. This would have limited QRCode to being an AIR only class. As it is, if you write a proxy for the Google API on your web site, you can use QRCode.as in a Flash app that serves up QR Codes on web pages. (I am not entirely sure why you would want to use but I didn’t want to limite your future creativity.)

Hopefully, next week, I will get around to cleaning up the reference app, publish it and be able to move onto another project. :)

Until next time,
I <3 |<
=C=

Disclosure: I work with Blue Parabola, Adobe is a customer of Blue Parabola.

ImageWIndow.as

package
{
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.net.FileReference;
	import flash.utils.ByteArray;
 
	import mx.controls.Image;
	import mx.graphics.codec.JPEGEncoder;
	import mx.graphics.codec.PNGEncoder;
 
	import spark.components.Button;
	import spark.components.VGroup;
	import spark.components.Window;
 
	public class ImageWindow extends Window
	{
		public var imageType:String = 'PNG';
		public var btnSave:Button = new Button() ;
		public var qrCodeImage:Image = new Image();
 
		public function ImageWindow(thisImage:Image,thisImageType:String)
		{
			super();
			this.showStatusBar=false;
 
			/* 
			 * Image
			 */
			imageType           = thisImageType;
			qrCodeImage         = thisImage;
			qrCodeImage.x       = 0;
			qrCodeImage.y       = 0;
			this.width          = qrCodeImage.width+10;
			this.height         = qrCodeImage.height+30;
			qrCodeImage.visible = true;
 
 
			/* 
			 * Button
			 */
			btnSave.label   = "Save";
			btnSave.y       = thisImage.height+10;
			btnSave.x       = 0;
			btnSave.visible = true;
			btnSave.addEventListener(MouseEvent.CLICK,imageSave);
 
 
			/* 
		 	 * Vertical Group
			 */
			var vg:VGroup = new VGroup();
			vg.horizontalAlign="center";
			vg.addElement(qrCodeImage);
			vg.addElement(btnSave);
			this.addElement(vg);
			return;
		} // public function ImageWindow(thisImage:Image,thisImageType:String)
 
 
		protected function imageSave(event:Event):void
		{
			var bmpd:BitmapData = new BitmapData(qrCodeImage.width,qrCodeImage.height);
			bmpd.draw(qrCodeImage);
 
			var imgByteArray:ByteArray = null;
			var fileName:String  = "";
 
			if (imageType=="PNG") {
				var pngenc:PNGEncoder = new PNGEncoder();
				imgByteArray = pngenc.encode(bmpd);				
				fileName = "qrCode.png";
			} else {
				var jpgenc:JPEGEncoder = new JPEGEncoder(100);
				imgByteArray = jpgenc.encode(bmpd);				
				fileName = "qrCode.jpg";
			} // if (grpImageType.selectedValue=="PNG")
 
			var file:FileReference = new FileReference();
			file.save(imgByteArray, fileName);
			this.close();			
		} // protected function imageSave(event:Event):void
	} // public class ImageWindow extends Window
} // package

Tags: adobe, flex, qr code, windows
Posted in Flex, Programming | Comments Off

 

Actionscript class to create QR Codes

Monday, July 26th, 2010

Dear Reader,

Anyone that knows me knows that I love new toys. Sometimes it’s a new piece of hardware or a new game. Sometimes however, it is just a idea to play with. This post is all about an idea. I’ve been intrigued by QR Codes for a while. Actually, my love of bar codes goes way back to when I used to run computer systems at my parent’s company. Even back then I saw how they could change things. These days, Bar Codes are ubiquitous in retail but haven’t moved out of the business niche. QR Codes stands ready to change that. Given that most smart phones can translate them they make a great way to link the real world to the net.

There are great resources on the web to generate QR Codes and save them to your computer for later use. However, being a developers, I wanted something I could play with and incorporate into my own applications. The problem is that generating a QR Code is not a trivial thing. Lucky for us however, Google has done the hard part already and all I really need to do is wrap their API in a class and I can use this in my own applications.

What you need to get started

If you want to create QR Codes, you have to be able to do two things, create them and read them. Since this post is all about creating them, I will leave the reading them part to you. If you have a smart phone, there is probably a good application for you to use to verify your work. For the Android OS, I use BarCode Scanner.

That’s it. All you need is a way to check your work. Google does the heavy lifting of creating the graphic and the code below gives you a nice easy wrapper to allow you to include it in your Flash Builder 4 applications.

The fun part

Ok, let’s take apart the code. This is one of my first Flex components that I am confident enough to release. That having been said, I do welcome any comments or criticisms about the class.

If you haven’t yet, go review Google’s Chart API page on QR Codes. Google has boiled it down to the essentials, 3 required parameters and two optional. The heart of the class puts those parameters together and makes the call to the API. It takes the image it gets back and puts it in a form that Flex applications can re-purpose.

Planning for all EVENTualities

[Event(name="imageCreated", type="ImageCreatedEvent")]
[Event(name="imageFailed",  type="ImageFailedEvent")]

First we have to prepare for the events that can happen. In our case, either it works or it doesn’t. Either way, we need to provide the developer using this class with a way to handle both eventualities in their code so we don’t have to in our code.

The code for the events is located below, after the main class. The ImageFailedEvent is just a subclass of FaultEvent and serves only as a marker that can be trapped separately.

ImageCreatedEvent however, has an additional property of thisImage. When the even is fired, it passes a copy of the QRCode object to all listeners allowing them to easily fetch and incorporate the resulting graphic into the application.

The BINDABLEs

[Bindable] public var qualityArray:ArrayCollection = new ArrayCollection(["L","M","Q","H"]);
[Bindable] public var marginArray:ArrayCollection  =  new ArrayCollection([1,2,3,4,10,40]);

Of all the properties we have, only two are bindable, the two data arrays that specify the acceptable parameters for Error Correction and Margin.

All other parameters have reasonable defaults but in using the class, no reason to actually make them bindable presented itself.

The heart of the matter

var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageComplete);
loader.load(request);

The heart of the class is the createImage() method. To use the class, you instantiate the class, set the required properties, and then call createImage(). Within that method, the first thing we do is a parameter check. Make sure we have given the image a valid height and width and that we actually have something to encode. Once we can make a reasonable call to the API, we setyp for the call, make it and deal with the results.

[Side Note: QR Codes can store a variety of information. The one above is simple text. Anything that can be represented as text can be encoded. The zxing library can interpret several different types of data and the Android app will allow you to act on it appropriately. If the content starts with mailto: then the app asks me f I want to send an email to this person. If it starts with http: then the app asks me if I want to open it in a browser. Similarly, if it starts with BEGIN VCARD: then it will let me store the resulting VCARD in my contacts. Your application will most likely support similar features. In the reference application I created to test QRCode.as, I allow for encoding text, emails, URLs and VCARDS. However, this is ancillary to the QR Code specification. The specification does not dictate formats, only maximums for the different types of data (numeric, alphanumeric, binary, etc.) being stored.]

Back in February I wrote a post about Dynamically loading images from the web. In it, I talked about how calling an API that does not return XML or JSON is not as straightforward as it might be in Flex. I used the techniques (and probably the code) that I displayed in that post to make the call to Google’s chart API. I won’t rehash the previous post, you can review it as your leisure. At it’s core is a call to import flash.display.Loader to load the contents from the web.

createImage() does have one new feature, debug. If you set debug = true you will get an alert showing you the parameters being passed to Google’s API. I am a big believer in giving developers a way to see what is going on without having to hack the code. If you have Charles installed and running, you can see the call actually being made, however, I find it easier just to incorporate debugging into the code and allow developers the option. The dark side of this is that developers have the option of shooting themselves in the food this way as well. More than one developer has left debugging turned on when releasing their code.

Dealing with the aftermath

Since Flex is asynchronous, we have to have a method to deal with the image once the API call is done. imageComplete() is the handler for the COMPLETE event. In it, we set the height and width of the actual image as well as the data from the image itself. Then we dispatch the imageComplete event so that the main program can do something with the image.

Wrapping it up

That is it for the class. To use it, just instantiate, set the parameters and fire createImage() Of couse because we are talking to an API at Google, this only works with AIR applications. If you need a Flash application that runs from your own domain to be able to use QRCode.as, you will have to setup a project on your server to proxy the call to Google. (Trivial but beyond the scope of what we are discussing here.)

Until next time,
I <3 |<
=C=

Disclosure: I work with Blue Parabola, Adobe is a customer of Blue Parabola.

The Code

/*
 * Instantiate the object
 */
var qrCode:QRCode = new QRCode();
 
/*
 * Add our event listeners
 */
qrCode.addEventListener('imageCreated',this.displayImage);
qrCode.addEventListener('imageFailed',this.cannotDisplayImage);
 
 
/*
 * Setup for the call
 */
qrCode.width                = parseInt(imageWidth.text);
qrCode.height               = parseInt(imageHeight.text);
qrCode.margin               = margin.selectedIndex;
qrCode.errorCorrectionLevel = errorCorrectionLevel.selectedIndex;
qrCode.content              = contentToEncode; 			
qrCode.imageType            = grpImageType.selectedValue.toString();
 
/*
 * make the call
 */
qrCode.createImage();
/**
 * 
 * http://code.google.com/apis/chart/docs/gallery/qr_codes.html
 */
package
{
	[Event(name="imageCreated", type="ImageCreatedEvent")]
	[Event(name="imageFailed",  type="ImageFailedEvent")]
 
	public class QRCode
	{
		import flash.display.BitmapData;
		import flash.display.Loader;
		import flash.display.LoaderInfo;
		import flash.net.FileReference;
		import flash.net.URLRequest;
		import flash.net.URLVariables;
		import flash.utils.ByteArray;
 
		import mx.collections.ArrayCollection;
		import mx.controls.Alert;
		import mx.controls.Image;
		import mx.graphics.codec.JPEGEncoder;
		import mx.graphics.codec.PNGEncoder;
		import mx.rpc.events.FaultEvent;
		import mx.rpc.events.ResultEvent;
 
 
		[Bindable] public var qualityArray:ArrayCollection = new ArrayCollection(["L","M","Q","H"]);
		[Bindable] public var marginArray:ArrayCollection  =  new ArrayCollection([1,2,3,4,10,40]);		
 
		public var height:int = 150;
		public var width:int  = 150;
 
		public var content:String           = "";		
		public var debug:Boolean            = false;		
		public var errorCorrectionLevel:int = 0;
		public var imageType:String         = 'PNG';
 
		public var margin:int               = 0;
 
		protected var _image:Image = new Image();
		protected var apiUrl:String         = 'http://chart.apis.google.com/chart';
 
		public function QRCode()
		{
			/*
			* cht=qr
			* chs=<width>x<height>
			* chl=<data>
			* chd
			* choe=<output_encoding>
			* chld=<error_correction_level>|<margin>
			* 
			* Notes:
			* changing the quality needs to make sure the max number of chs hasn't been exceeded.
			* Add Types
			*/
		}
 
 
 
		public function createImage():void
		{
			// do a property check and throw appropriate error for missing properties like content to encode
			if (this.width<1) {
				dispatchEvent(new ImageFailedEvent(ImageFailedEvent.IMAGE_FAILED,false,false));
				return;
			}
			if (this.height<1) {
				dispatchEvent(new ImageFailedEvent(ImageFailedEvent.IMAGE_FAILED,false,false));
				return;
			}
			if (this.content.length<1) {
				dispatchEvent(new ImageFailedEvent(ImageFailedEvent.IMAGE_FAILED,false,false));
				return;
			}
 
			var params:URLVariables = new URLVariables();
			params.cht  = 'qr';
			params.chs  = this.height +'x'+ this.width;
			params.chl  = this.content;
			params.chld = qualityArray[errorCorrectionLevel]+"|"+marginArray[margin];
 
			var request:URLRequest = new URLRequest(apiUrl);
			request.method="POST";
			request.data = params;
 
			// DEBUGGING
			if (this.debug) {
				var a:String = apiUrl + 
					'?cht='+params.cht
					+"&chs="+params.chs
					+"&chld="+params.chld
					+"&chl="+params.chl
				Alert.show(a);			                                                       
			} // if (this.debug)
 
			var loader:Loader = new Loader();
			loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageComplete);		 	
			loader.load(request);
		} // protected function createImage():void
 
 
		protected function imageComplete(event:Event):void
		{
			var loader:Loader = (event.target as LoaderInfo).loader;
			_image.data   = loader.content;
			_image.width  = loader.width;
			_image.height = loader.height;
			dispatchEvent(new ImageCreatedEvent(ImageCreatedEvent.IMAGE_CREATED,false,false,_image)); 
			return;
		} // protected function displayImage(event:Event):void
 
		protected function cannotDisplayImage(event:FaultEvent):void
		{
			dispatchEvent(new ImageFailedEvent(ImageFailedEvent.IMAGE_FAILED,false,false)); 
		} // protected function cannotDisplayImage(event:FaultEvent):void
 
		public function getImage():Image{
			return this._image;
		}
 
	}
}

ImageCreatedEvent.as

package 
{
	import flash.events.Event;
	import mx.controls.Image;
 
	public class ImageCreatedEvent extends Event
	{
		public static const IMAGE_CREATED:String = 'imageCreated';
		protected var _image:Image;
 
		public function ImageCreatedEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false, thisImage:Image=null)
		{
			super(type, bubbles, cancelable);
			_image = thisImage;
			return;
		}
 
		override public function clone():Event 
		{
			return new ImageCreatedEvent(type, bubbles, cancelable, _image);
		}
 
	}
}

ImageFailedEvent.as

package
{
	import mx.rpc.events.FaultEvent;
 
	public class ImageFailedEvent extends FaultEvent
	{
		public static const IMAGE_FAILED:String = 'imageFailed';
 
		public function ImageFailedEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super(type, bubbles, cancelable);
		}
 
	}
}
Disclosure: I work with Blue Parabola. Adobe is a customer of Blue Parabola.

Tags: adobe, flash, flex, qr code
Posted in Flex, Programming | 9 Comments »

 

Migrating Flex components to Flex Libraries

Wednesday, July 21st, 2010

Dear Reader,

Recently I decided to consolidate all of the custom and reusable controls I had created in Flex into a single library. Even as good as Eclipse is at refactoring (one of the things it actually does well) it still wasn’t was clear cut of a procedure as I had hoped for.
(more…)

Tags: adobe, flex, libraries
Posted in Flex, Programming | Comments Off

 

DPC Wrap-up

Monday, June 14th, 2010

Dear Reader,

I’m sitting in Schipol airport with Rob Allen waiting for my flight. (Rob is reviewing his photos) Thinking back over the past few days, the word that comes to mind is fantastic. DPC10 was fantastic.

For those that don’t know me, I have a personal interest in DPC as last year I worked at Ibuildings and hosted it. It’s always difficult to hand off something that means so much to you but I am happy to report that Lorna Jane took it and made it her own. It was everything I had hoped it would be.

(more…)

Tags: adobe, Amsterdam, chris shiflett, dutch php conference, elizabeth naramore, glee, lorna jane mitchell, matthew, rob allen
Posted in PHP | Comments Off

 

Flash Builder 4 Data Wizard for HTTP Services – A love story in 1 act

Monday, June 7th, 2010

Dear Reader,

Disclosure: I work with Blue Parabola, Adobe is a customer of Blue Parabola.

If you’ve been paying attention, you will have noticed that I recently published a series of articles over on phparch.com discussing the new Data Connection Wizard for PHP that Adobe has built into Flash Builder 4. While I am not a great fan of Wizards in general, I recognize that there is a segment of the developer population that does like them so I respect that and try not to run screaming from the room every time I see someone using one.
(more…)

Tags: actionscript, adobe, flash builder 4, HTTP
Posted in Flex, Programming | 1 Comment »

 

Flash Builder 4 and packaging

Wednesday, June 2nd, 2010

Dear Reader,

I am currently teaching a class for php|architect and Adobe, “Rich Internet Applications with Flex and PHP“. Today, one of the topics we covered was packaging classes. I was demonstrating how easy it was to create packages in Flash Builder 4 and going along nicely using my slides when a student asked a question.

“Why doesn’t it actually create the sub packages for me? Do I need to do that myself?”

(more…)

Tags: adobe, classes, eclipse, flash builder 4, packaging
Posted in Programming | Comments Off

 

A Flex 3 custom events primer

Monday, February 15th, 2010

Dear Reader,

I am a web developer. It’s been over 10 years since I’ve done serious desktop application development. So when I fire up Flex Builder to write my first AIR application, it takes a while for things to start coming back to me. One of the things that is only now starting to come back is Event Driven Programming.

This past week I decided to start working on a small project to scratch a personal itch. The project itself fizzled but I learned something none-the-less. I learned how to create custom events in Flex 3.

The project required selecting a file for processing. Flex 3 has a control for actually opening the dialog box and selecting the file but no control that shows the button and the selected file name.

(more…)

Tags: adobe, air, events, flex, flex builder, marco tabini
Posted in Flex, Programming | 2 Comments »

 
  • Event Registration Online for Day Camp 4 Developers : Soft Skills

  • Team Based PHP Training

  • Tags

    adobe API article Cal Evans codeworks community conference cw09 Derick Rethans developers devzone elizabeth naramore Exim flex fun IBuildings Kathy Evans linkedin Management Marketing microsoft MySQL Nashville open source phar PHP phparchitect php developers podcampnashville podcast Programming Quickies respect Sebastian Bergmann Silly-Con Valley sixty second tech software development terry chay twitter upgrade video windows wordpress zend zend framework

  • RSS PHP Podcasts

    • DPCRadio: Web services for consumer devices
    • Creating RSS & Atom Feeds with Zend_Feed
    • SitePoint Podcast #77: Paper or Blu-ray?
    • JSClasses, JSMag, PHP Alpha 1, PHP strict typing, IndieConf – Lately in PHP podcast episode 4
    • SitePoint Podcast #76: Wicked WordPress Themes with Allan Cole and Jeffrey Way
    • DPCRadio: Designing for Reusability
    • SitePoint Podcast #75: Awesome Overkill
    • DPCRadio: Technical Debt
    • SitePoint Podcast #74: WordPress Themes with Nathan Rice and Cory Miller
    • SitePoint Podcast #73: Cease and Desoup

  • Me

    • Best web design company
    • Cal Evans Dot Com
    • Cheap Wine Diary
    • Cyrano’s Apprentice
    • Evans Internet Construction Company
    • My Life as a Child
    • PHP Podcasts
    • Sixty Second Tech

  • Categories

    • Apache
    • BlogBling
    • Blogging
    • codeworks
    • Entertainment
    • Entrepreneurship
    • Flex
    • Humor
    • JavaScript
    • Long Form
    • Management
    • Marketing
    • Me
    • PHP
    • podcasting
    • Programming
    • SQL
    • Technology
    • Web 2.0
    • wordpress
    • WordPress Plugins
    • writing
    • zend framework


Postcards From My Life is proudly powered by WordPress
Entries (RSS) and Comments (RSS).