Category Archives: MainImporter

My PlayBook Experience

As I posted before, I attended the PlayBook introduction in Toronto.

Free PlayBook? Yes, please!

The main draw of course is the offer of a free PlayBook for an approved app in the BlackBerry app store for the PlayBook.

As it so happens, I also have been pursuing a personal project for the past year, Moviesinhaiku. This seemed like a good fit for a tablet app.

There are a lot of great resources on how to get started with developing for the PlayBook, so I won’t touch on that too much. What I’d like is to go over some features of my specific app and some decision-making involved in making those features work.

Without further ado.
(note: all images resized from original screen size)

What should a Movieinhaiku look like?

The first problem I wanted solved was how I wanted to display my haikus. I didn’t want anything too elaborate because;
A) I couldn’t guarantee performance
B) They’re haikus about movies. How elaborate do they need to be?

I have been keeping an updated XML file handy on the moviesinhaiku test site. Each haiku looks like this;

<haiku answer='True Grit'>
<imdb year='2010'>tt1403865</imdb>
<haiku_text><![CDATA[A man with true grit.<br/>Girl hires. Catch father's killer.<br/>Vengeance costs in time.]]></haiku_text>
</haiku>

The Moviesinhaiku logo is essentially two rectangles, black stacked on white. Made sense to simply continue that for the haiku “cards”

I don’t want to discount the ease or difficulty in using XML and Flash. I’ve been using the exact same class for a long time now MainImporter for importing pretty much everything. But I’m not really going to get into parsing XML.

One thing I decided to try that was new to me was use the Dictionary class. It seemed like a great tool to store all my haikus. I’ll be completely honest in admitting that I don’t get it entirely, but it’s primarily a set of keys. So I created my Dictionary and pushed in all the haikus created so I could reference them whenever I wanted.


haikuDict = new Dictionary;
for each(var haikuXMLnode:XML in haikuXML..haiku){
   var haiku:Haiku = new Haiku(haikuXMLnode);
   haikuHolder.addHaiku(haiku);
   haikuDict[haikuCount] = haiku;
   haikuCount++;
}

Haikus on screen

Once I had my haikus in the dictionary, I wanted to scatter them around. The easiest way I could think to do that was to use the Orbit class I’d written for the Particle book for GalaxyGoo. Throw a little random into the mix and you get a nice, little random orbital display.

Third party services

I wanted to try and give more information than just the title of the movie so, as can be seen in the XML node above, I copied the IMDB identifier for each movie. A friend of mine did some quick searching and located The MovieDB. I applied for an API key and got to work. I set up two classes to get the information from teh interwebz to my app.

The first class I called APIcontrol and in essence just holds my API information; key, listing url and some static constants. The API results can be returned in XML and JSON. I’d never used JSON before and once again decided I’d be better off learning something new than just staying in my comfort zone. The first thing I needed was the as3corelib from Mike Chambers housed at github.
Specifically, I needed one class for one function call

import com.adobe.serialization.json.JSON;
var jsonObj:Object = JSON.decode(event.target.data);

Once again, how to work with JSON is all over the lazy web so I’m not going to get into it. It’s pretty easy and I think I’ll use it more.

The second class I wrote was called ImdbWindow. Technically, I should have called it TmdbWindow, I suppose. However, I didn’t and don’t like living in regret. ImdbWindow grabbed all the information from APIcontrol and created a window using; the movie poster, the synopsis, the array of genres and the release date. Example below. One thing that was very important I’d taken from a post by Christian Cantrell about scaling your images for mobile. Please read it as it’s important.

Filtering

Something I thought really mattered when I started was filtering. I liked the idea of the person using the device being able to view the haikus based on release or by title. In addition, I wanted to be able to give that same person the ability to search by text. There are a few things to consider when doing this so let’s see if I can break it down.

1. When alphabetizing, remember that a lot of movies start with “The”, “A” and “An”. When I store my haikus, each one stores an alphabetical version of their title. Writing a getter/setter for an alphabetic version of the title is as simple as;

public function set alphabetic($str:String):void{
   var tmpStr:String;
   if
($str.toLowerCase().indexOf("the ")==0){
      tmpStr = $str.substr(4,$str.length);
   }else if($str.toLowerCase().indexOf("a ")==0){
      tmpStr = $str.substr(2,$str.length);
   }else if($str.toLowerCase().indexOf("an ")==0){
      tmpStr = $str.substr(3,$str.length);
   }else{
      tmpStr = $str;
   }
   haikuAlphaTitle = tmpStr;
}

As you can see, I do a substring check at the beginning of the title for my little culprits and create a string that has those removed. This way, when I’m alphabetizing, they’ll make sense. The getter, of course, is just a getter, returning the alphabetic title.

Now how does one alphabetically sort? Using arrays and the sortOn method, of course!

I have two separate sortOn functions; Year and Title.

private function sortOnYear(firstHaiku:Haiku, secondHaiku:Haiku):Number {
   var aYear:Number = firstHaiku.year;
   var bYear:Number = secondHaiku.year;
   if(aYear > bYear) {
      return 1;
   } else if(aYear < bYear) {       return -1;
   } else {
      return 0;
   }
}
private function sortOnTitle(firstHaiku:Haiku, secondHaiku:Haiku):Number {
   var aAlphabet:String = firstHaiku.alphabetic;
   var bAlphabet:String = secondHaiku.alphabetic;
   if(aAlphabet > bAlphabet) {
      return 1;
   } else if(aAlphabet < bAlphabet) {       return -1;
   } else {
      return 0;
   }
}


(sorting in action)

As can be seen from the above image, the search by title is also active. This ended up being pretty easy. Since all the haikus are in the Dictionary, I was able to just set up an onChanged event with the textField. Then I compare the searched term with the haiku’s title. If there’s a match, I add the haiku to the displayList

private function onSearchHandler(evt:Event):void{
   while (mainHolder.numChildren) {mainHolder.removeChildAt(0); }
   haikuHolder = new HaikuLayout;
   haikuHolder.addEventListener(HaikuEvents.ADD_SCROLLBAR,addScroll);
   haikuHolder.startXposition = stage.stageWidth*.5;
   haikuHolder.startYposition = stage.stageHeight*.5;
   for (var key:Object in haikuDict) {
      // iterates through each object key
      var haiku:Haiku = haikuDict[key] as Haiku;
      var titleStr:String = haiku.title.toLowerCase();
      if(titleStr.indexOf(evt.target.searchStr) != -1){
         haikuHolder.addHaiku(haiku);
      }
   }
   haikuHolder.initLayout();
   mainHolder.addChild(haikuHolder);
}

Of course, no matter how close I thought I got to finishing, something would intercede and remind me that the worst thing you can be is your own boss. Especially when it’s a personal project. More especially when you’re not too busy with client work. Most especially when you like a challenge.

Just as I figured I was nearly done, Matt Fabb tweeted;

@wheniwas19 Looks like a v1 app to me! That said feature request: hide the title. ‘Cause it’s fun guessing the movie just from the haiku.

Which should surprise no-one that knows me I glommed on to like a barnacle to a ship (weird metaphor). So i felt like Matt was right, people should be able to treat this like a quiz! Well, that doesn’t bring me entirely back to the drawing board. So I decided to give it a shot.

Building a quiz

First things first, let people type in their answer or give them multiple choice? According to my friend, Drew, “Don’t give anyone too much to input. Your margin for error increases. Which is true. Unless I wanted to create a massive dictionary full of spelling options, I’d be shooting myself in the foot. So multiple choice it is. Simple enough, I suppose.

My solution is about as smart as I get. I have a list of titles provided by the xml. A huge list of titles. Why not pick four titles at random from the list and use 3 of them as alternates in the multiple choice. How would one do that, you ask? Lucky for you, I know how one does that.

Random numbers that don’t repeat

For some reason, for as long as I can recall, I have been building little functions that make non-repeating random number generators. I have my little RandomRange class so I add this function to it.

public static function nonRepeat(max:Number, num:Number):Array{
   var tempArray:Array = [];
   for (var i:int = 0; i < max && num > 0; ++i) {
      if (Math.floor(Math.random() * max - i) < num) {          tempArray.push(i + 1);
         num--;
      }
   }
   return tempArray;
}

This is easily called using var randomArr:Array = RandomRange.nonRepeat(titles.length,4);

So here I have an array with four random titles. Now I want to make sure none of the four titles are the answer. Don’t want to double up, right?

var answerCount:Number = 0;
var answersArrArray = [];
for(var i:int=0;i < randomArr.length;i++){    if(titles[randomArr[i]] != answerTitleStr){
       answersArr[answerCount] = new QuizAnswer(titles[randomArr[i]],answerWidthNum);
      answerCount++;
   }
}

Now I definitely have an array of four answers that are randomly pulled from all the titles in my xml and are not the answer. But I do, absolutely want the correct answer in the four. So I pop it into a random position in the answersArr array.

var realAnswer:QuizAnswer = new QuizAnswer(answerTitleStr);
answersArr[RandomRange.retrieveRandom(0,3)] = realAnswer;

Then all I need to do is build my quiz. As you noticed, I have a class called QuizAnswer that’s basically a textField with a black background that dispatches an event when clicked. If it’s the wrong answer, the textField is dimmed and the MouseEvent is removed. Process of elimination.

Once the quiz is answered, the process of pinging the Movie DB occurs again. I added in the option of seeing the haikus with or without titles and that’s how the whole app loads.

It ain’t over ’til it’s over

Just as I thought I was out, Matt Fabb pulled me back in with his innocuous question:

@wheniwas19 I don’t want to add more work for you, but I assume MoviesInHaiku app links to your Etsy shop?

Long story short, yes. I did. After Matt asked, but whatever.

So that’s it. Maybe not the most informative PlayBook post, but there is some thought to what I do. I have submitted the app at a cost of $1.99 to perhaps recoup some of the cost of building one of these things entirely on my own and on my own dime. I hope it’s approved, I hope people buy it and I hope they like it. I’m sure if they don’t I’ll hear about it.

The Social Network Moviesinhaiku

A little while ago, I’d done a regular-sized print of the Trigger Street production 21. The producer, Dana Brunetti, wanted to know if I’d be into making a larger version. While I’m all for copying myself, I thought maybe he might like one done for the release of their latest film, The Social Network. Fortunately for me, Dana’s a really great guy and his response was “Go for it.”

After watching the movie (worth seeing, btw), I sat down to think about what I really wanted to make.I’ve never done a large format print. The more I looked into large format printmaking, the more I realized my skills weren’t up to the task. But I do know a thing or two about digital work.

Once I decided I’d pursue a digital route I started thinking about what, visually, I wanted. I looked at some production shots of the movie on the site and thought, “That would make a good background.” or “That’s an interesting composition.” Then I listened intently to the soundtrack by Trent Reznor and Atticus Finch. Then looking at both a picture of Jesse Eisenberg and listening to one track (On We March), then glancing over at the IMDB cast list, a light bulb went off.

Immediately I started considering HYPE, the framework developed by two good friends of mine, Joshua Davis and Branden Hall. I made a list what I wanted to do and checked off the list what HYPE could do easily versus what I could easily.

ME. Create some XML files with main cast list, secondary cast list and main production
ME. Load XML, parse it into an array
HYPE. Attach a bitmap as a source for pixel data using PixelColorist
HYPE. Create a BitmapCanvas 4 times the size of my original.
HYPE. Load and start an MP3
HYPE. Start a loop/rhythm to do something
ME. Pick a random position on the x-axis
ME. Pick a position on the y-axis based on the song’s position
HYPE. Use SoundAnalyzer.getOctave to tell me how big to make my text in that x-position
HYPE. Use PixelColorist to tell me what colour to make that text in the bitmap.
HYPE. Copy that text into the huge BitmapCanvas
ME. When the song is finished, stop everything.
HYPE. Encode the BitmapCanvas into a targa format.
HYPE. Save the encoded BitmapCanvas targa file.

Look at that list. A 2:1 ratio of what I didn’t need to figure out because HYPE was there. When we get into discussion about patterns and frameworks, we get caught up on deviation. If you use MVC, that’s all you can use, for instance. I argue that you can use what you want, I’ve always believed that. HYPE, in case it escaped your notice, is built for exactly that kind of mentality. All I wanted was a few classes. But without those classes, I would have been stuck trying to build this from scratch.

Suffice it to say, I finished the prints. Thanks to some added help from the inimitable Branden Hall the images saved out HUGE just fine.

Title: Zuckerberg
Line 1: From geek to elite.
Audio: On We March
Text Content: Primary cast
Source Image: Jesse Eisenberg head

Title: Robe Walk
Line 2: Friends counted, friends discounted.
Audio: In Motion
Text Content: Full cast
Source Image: Eisenberg walking in robe

Title: Fingers Drum
Line 3: When percent matters.
Audio: Carbon Prevails
Text Content: Production credits
Source Image: Hands under table

Due to the fact that this is a commission only four copies were printed. Thank you so much, Dana, for the opportunity. I hope you love your prints!

MainImporter Class

This is the second custom class used in the Bullet Class, MainImporter.

During the same project that prompted TextFormatter it dawned on me I had no real useful Load this class. There are a lot of nice phat loaders around, BulkLoader is a great example. However, it’s kind of overkill for what I wanted.

So I put together MainImporter. It handles Bitmaps, SWFs and XML nicely and I’ve used it extensively for pretty much every project since.

package com.utils{
import com.events.LoaderEvent;
import flash.display.Loader;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IOErrorEvent;
import flash.events.ProgressEvent;
import flash.net.URLLoader;
import flash.net.URLRequest;
public class MainImporter extends EventDispatcher{
public static const ITEMLOADED:String = “item loaded”;
private var itemURL:String;
public var loader:Loader;
public var loaderXML:URLLoader;
private var fileEnder:String;
public var loadedCurrent:Number;
public var loadedTotal:Number;
public function MainImporter(_url:String){
itemURL = _url;
var tmpArray:Array = itemURL.split(“.”);
fileEnder = tmpArray[tmpArray.length-1];
loadItem();
}
private function loadItem():void{
var urlRequest: URLRequest = new URLRequest(itemURL);
if(fileEnder == “xml”){
loaderXML = new URLLoader();
loaderXML.addEventListener(IOErrorEvent.IO_ERROR,loadError);
loaderXML.addEventListener(ProgressEvent.PROGRESS,progressListener);
loaderXML.addEventListener(Event.COMPLETE,completeLoad);
loaderXML.addEventListener(Event.INIT,initListener);
loaderXML.load(urlRequest);
}else{
loader = new Loader();
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR,loadError);
loader.contentLoaderInfo.addEventListener(ProgressEvent.PROGRESS,progressListener);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE,completeLoad);
loader.contentLoaderInfo.addEventListener(Event.INIT,initListener);
loader.load(urlRequest);
}
}
private function initListener(evt:Event):void{
//trace(“init”);
}
private function progressListener(evt:ProgressEvent):void{
loadedCurrent = Math.floor(evt.bytesLoaded/1024);
loadedTotal = Math.floor(evt.bytesTotal/1024);
dispatchEvent(new LoaderEvent(LoaderEvent.UPDATELOADER,loadedCurrent,loadedTotal,true));
}
private function loadError(evt:IOErrorEvent):void{
trace(“IOErrorEvent : “ + evt);
}
private function completeLoad(evt:Event):void{
dispatchEvent(new Event(MainImporter.ITEMLOADED));
}
}
}

This is a work in progress, so do what you want with it.
MainImporter.as