Stanford Lectures for iPhone Application Programming

Stanford university offers the Spring 2009 quarter lectures for free via iTunes U.

I think that’s my way into iPhone application programming.

Check out the standford website for details: http://www.stanford.edu/class/cs193p/cgi-bin/index.php

UPDATE:

Online resources for auditors and iTunes U viewers:
http://groups.google.com/group/iphone-appdev-auditors
http://cs193p.com/

No flash events after PrintJob.start()

While developing the print functionality within a framework I got stuck on the behaviour of the Flash Player print implementation.

I couldn’t solve the following steps:

  1. Start the PrintJob and display the print dialog
  2. Procceed the following tasks for every print page:
    • Load dynamic data into view templates (images, text, video…)
    • Wait for the onLoadComplete event and add the displayed contents using PrintJob.addPage()
    • Repeat for next page…
  3. Call PrintJob.send()

I really thought this would be an easy task but now I think it’s not possible. Several attempts later I figured out that after calling PrintJob.start() there is no way to use the flash event model! This means loading contents after calling PrintJob.start() is impossible!

WOW!

So I used a workaround: I create all the print pages like I mentioned before, but without calls on the PrintJob-API. I simply store every print page as BitmapData and afterwards I create the PrintJob within a loop.

Now I have multiple other problems:

  • Hundreds of pages use a huge amount of RAM and often crash the application.
  • The bitmap resolution sucks so printout text quality is unsatisfying and due to point 1 I have no chance to work with higher quality.
  • I must avoid skript timeouts because after preparing all the bitmaps it takes much more that 15 seconds to add all these pages to a PrintJob.

Did I slip something?

Finally I created a small example to show you the disfunctionality (PrintJobProblem.as):

package  
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Rectangle;
	import flash.printing.PrintJob;
	import flash.text.TextField;
	import flash.text.TextFieldAutoSize;
	import flash.utils.getTimer;	
 
	/**
	 * @author Markus Raab - derRaab.com / superclass.de
	 */
	public class PrintJobProblem extends Sprite 
	{
		private var _numPrintOuts : int;
		private var _printOutIndex : int;
		private var _printJob : PrintJob;
 
		/**
		 * Starts a event based printout test after a loop based
		 * test has successfully finished.
		 */
		public function PrintJobProblem()
		{
			_numPrintOuts = 2;
 
			runLoopBasedTest( );
			runEventBasedTest( );
		}
 
		/**
		 * Demonstrates and ensures the functionality.
		 */
		private function runLoopBasedTest() : void
		{
			_printJob = new PrintJob();
 
			if ( _printJob.start() )
			{	
				for ( var i : int = 0; i < _numPrintOuts; i++ )
				{
					addPage( i );
				}
 
				_printJob.send();
			}
		}
 
		/**
		 * Demonstrates that after calling PrintJob.start() the internal
		 * eventmodel doesn't dispatch any events at all. Everything is blocked
		 * until the script times out.
		 */
		private function runEventBasedTest() : void
		{
			_printJob = new PrintJob();
 
			if ( _printJob.start() )
			{
				trace( getTimer(), "PrintJob.start() called" );
				_printOutIndex = 0;
 
				startAddPagesOnEnterFrame();
			}
		}
 
		private function startAddPagesOnEnterFrame() : void
		{
			addEventListener( Event.ENTER_FRAME, onAddPagesOnEnterFrameEvent );
		}
 
		private function onAddPagesOnEnterFrameEvent(event : Event) : void
		{
			if ( _printOutIndex == _numPrintOuts )
			{
				stopAddPagesOnEnterFrame();
 
				_printJob.send();
				trace( getTimer(), "PrintJob.send()" );
			}
			else
			{
				addPage( _printOutIndex++ );
			}
		}
 
		private function stopAddPagesOnEnterFrame() : void
		{
			removeEventListener( Event.ENTER_FRAME, onAddPagesOnEnterFrameEvent );
		}
 
		private function addPage( pageIndex : int ) : void
		{
			var page : Sprite = createPrintPage( pageIndex );
 
			stage.addChild( page );
 
			var viewWidth : Number = page.width;
			var viewHeight : Number = page.height;
 
			var scaleX : Number = _printJob.pageWidth / viewWidth;
			var scaleY : Number = _printJob.pageHeight / viewHeight;
			var scale : Number = Math.min( scaleX, scaleY );
 
			page.scaleX = page.scaleY = scale;
 
			var printRect : Rectangle = new Rectangle( 0, 0, viewWidth, viewHeight );
 
			try
			{
				_printJob.addPage( page, printRect );
			}
			catch( e: Error )
			{
				trace( getTimer(), "PrintJob.addPage() failed" );
			}
 
			stage.removeChild( page );
		}
 
		private function createPrintPage( pageIndex : int) : Sprite
		{
			var page : Sprite = new Sprite();
 
			var textField : TextField = new TextField();
				textField.autoSize = TextFieldAutoSize.LEFT;
				textField.text = "Page " + pageIndex;
 
			page.addChild( textField );	
 
			return page;
		}
	}
}

Packaging an AIR installation file

I found this adobe help page but I didn’t want to use command line for packaging an AIR installation file. Simply because I’m not used to it. So I was looking for a different way.

Well, actually Flex Builder provides the possibility to do that, but you must ensure to activate the “Copy non-embedded files to output folder” compiler option in the project properties. Otherwise Flex Builder won’t let you select your additional sources.

And for that reason you have to put your additional sources to the src-folder.

Export multiple .fla files using .jsfl

Another very basic information. I had to export multiple .fla files so I wrote my first .jsfl file:

// Binary Directory path (file:// syntax)
var binDir = "file:///Users/.../swf/";
// Source Directory path (file:// syntax)
var srcDir = "file:///Users/.../fla/";
// .fla file names
var fileNames = new Array( "file1", "file2", "file3" );
 
var fileName;
var fileSrc;
var fileBin;
var fla;
 
for ( var i = 0; i < fileNames.length; i++ )
{
	fileName = fileNames[ i ];
	fileSrc = srcDir + fileName + ".fla";
	fileBin = binDir + fileName + ".swf";
 
	fla = fl.openDocument( fileSrc );
	fla.exportSWF( fileBin, true ); 
 
	fl.closeDocument( fla, false );
}

Using the Loader-Class with Adobe AIR

Just a quick note because it took me a little while to find out that it’s necessary to use “file://” syntax to access the local file system.

// Basic example:
var url: String = "file:///Users/markusraab/Desktop/yourfile.swf";
 
// or use a file reference:
var file: File = new File();
var url: String = file.url;
 
var loader: Loader = new Loader();
loader.load( new URLRequest( url ) );

Adobe AIR flash.filesystem.File methods throw Error 2037 when no nativePath is set

While developing an Flex based AIR Application I figured out an issue with the flash.filesystem.File class. I tried to access properties like exists, nativePath etc. before a nativePath was set using a browse method. So the while debugging the following error was triggered:

Error #2037: Functions called in incorrect sequence, or earlier

Well, I would expect to get at least some default values. For example File.exists should be false or something. But you will always get an exception. So after research I found a note about it here.

Anyway, the only chance you have is to catch the error.

But there is one thing more: If you publish the AIR application, install it and try it then, you won’t get an exception. Everything works fine than.