FlashAndTheCity (http://flashandthecity.com) is going to be a 3-day “out of the box” Flash conference focusing on RIA technologies. The purpose of the event is to learn, share your knowledge and network with your peers from the Big Apple and other great cities.
The idea of this conference is to bring Flash professionals together and talk about Adobe’s latest technologies such as Flash, Flex, AIR, LCDS and ColdFusion. Starting with the setting, the event will take place in a different and dynamic environment called “3LD Art Technology Center” located in downtown Manhattan. Come evening, the event will then continue with interesting networking opportunities at venues throughout the City! This exciting event will not only be educational, but fun and offering an invaluable opportunity to connect with new and existing people, some of whom you may have only heard their names from working in the field.
If you are a developer, designer, employer, entrepreneur or just interested in learning more about Flash and especially the New York City Flash community, you must check out FlashAndTheCity! Don’t forget to book your ticket early in order to get the early bird pricing. The conference will include breakfast and lunch and we will have discounted prices at a local hotel for those who need accommodations. If you have any questions, comments or any other ideas, please feel free to contact us! We are very open minded and would like to hear from you! Thanks very much, and looking forward to seeing you all at FlashAndTheCity!
Back in January ‘09 I posted an API that makes working with an AIR application that works with an SQLite database and has many SQL commands and multiple tables simple. I have received feedback from developers that are using the API and have found it helpful. I kept getting requests to update the API to support multiple tables.
You need this API because working with an application that has many SQL commands and multiple tables can become challenging.
These commands may be initialized from different classes, and
we may want to keep the database connection open and avoid duplicating code.
SQLiteManager does just that and allows you to set the database settings and than access the manager from anywhere in your application. It makes the process simpler and integrates very well with micro architecture frameworks.
I finally got around to updating the API, and it now support the following:
Password encryption
Multiple tables
Common SQL commands
Transactions and rollback option
Better handling of results
Improved and optimized code
Let’s look at the implementation code. The application will hold two tables, one for users and one for orders, and we will be able to insert and read information as well as keep track of transactions and rollback in case there are errors or for any other reasons.
AIR SQLite Manager
The first step is to set constants with all the names of all the user gesture SQL we will be running. This way we can track requests and use the same result handler for all of our SQL requests.
The creation CompleteHandler method will be called once the application has completed initialization. We will be setting the database information and starting the connection. Notice that you need to set the array Vector with all of the tables you will have in your application. The way it is working is in case the application doesn’t have the tables created already. They will be generated automatically for the user so you need to specifiy the SQL command to create these tables and the name of each table. The names can be anything; just use unique names.
// start database
protected function creationCompleteHandler():void
{
var password:String = null; // leave as null to have the database unsecure or set a password for secure connection. Example: "Pa55word";
var sqliteTables:Vector.<SqliteTableVO> = new Vector.<SqliteTableVO>;
sqliteTables[0] = new SqliteTableVO( "Users", "CREATE TABLE Users(UserId INTEGER PRIMARY KEY, UserName VARCHAR(150)); " );
sqliteTables[1] = new SqliteTableVO( "Orders", "CREATE TABLE Orders(OrderId INTEGER PRIMARY KEY, UserId VARCHAR(150), OrderTotal DOUBLE);" );
addListeners();
database.start( "Users.sql3", sqliteTables, password, sqliteTables[0].tableName );
}
Notice that in the previous method we had a call to set the listeners we will be using. Take a look at the events we will be listening to:
// Set all the listeners
private function addListeners():void
{
database.addEventListener(DatabaseSuccessEvent.DATABASE_CONNECTED_SUCCESSFULLY, function(event:DatabaseSuccessEvent):void
{
event.currentTarget.removeEventListener(event.type, arguments.callee);
database.executeSelectAllCommand( database.sqliteTables[0].tableName, READ_ALL_USERS_INFO );
});
database.addEventListener(DatabaseSuccessEvent.COMMAND_EXEC_SUCCESSFULLY, onSelectResult);
database.addEventListener(DatabaseSuccessEvent.DATABASE_READY, function(event:DatabaseSuccessEvent):void {
event.currentTarget.removeEventListener(event.type, arguments.callee);
trace("database ready!");
} );
database.addEventListener(DatabaseFailEvent.COMMAND_EXEC_FAILED, function(event:DatabaseFailEvent):void {
trace("SQL execution fail: "+event.errorMessage);
});
database.addEventListener(DatabaseFailEvent.DATABASE_FAIL, function(event:DatabaseFailEvent):void {
var message:String = "Database fail: "+event.errorMessage;
if (event.isRolledBack)
{
message += "\nTransaction was rolled back";
}
Alert.show(message);
});
database.addEventListener(DatabaseSuccessEvent.CREATING_DATABASE, function(event:DatabaseSuccessEvent):void {
event.currentTarget.removeEventListener(event.type, arguments.callee);
trace(event.message);
});
}
We need two methods to generate the insert SQL command for the two tables we have and make the request.
protected function insertDataClickHandler(event:MouseEvent):void
{
var SQLStatementText:String = "INSERT INTO Users VALUES('" + userId.text + "','" + userName.text + "');'";
database.executeCustomCommand(SQLStatementText, INSERT_USER_INFO);
}
protected function insertOrderClickHandler(event:MouseEvent):void
{
var SQLStatementText:String = "INSERT INTO Orders VALUES('" + ordersDataGrid.dataProvider.length+1 + "','" + IdComboBox.selectedItem.label + "','" + orderTotal.text + "');'";
database.executeCustomCommand(SQLStatementText, INSERT_ORDER_INFO);
}
Once SQL commands are requested all the results are processed in this implementation with the same handler called onSelectResult. Notice that each request had a unique name so we are able to match the request to the result and update the view as needed.
// handles results
private function onSelectResult(event:StatementCompleteEvent):void
{
var result:Array = event.results.data;
var rowsAffected:int = event.results.rowsAffected;
switch (event.userGestureName)
{
case null:
break;
case READ_ALL_USERS_INFO:
if (result == null)
break;
var len:int = result.length;
var dp:ArrayCollection = new ArrayCollection();
for (var i:int; i<len; i++)
{
dp.addItem( { label: result[i].UserId, UserName: result[i].UserName } );
}
IdComboBox.dataProvider = usersDataGrid.dataProvider = dp;
database.executeSelectAllCommand( this.database.sqliteTables[1].tableName, READ_ALL_ORDERS_INFO );
break;
case INSERT_USER_INFO:
database.executeSelectAllCommand( this.database.sqliteTables[0].tableName, READ_ALL_USERS_INFO );
break;
case INSERT_ORDER_INFO:
database.executeSelectAllCommand( this.database.sqliteTables[1].tableName, READ_ALL_ORDERS_INFO );
break;
case READ_ALL_ORDERS_INFO:
if (result == null)
break;
len = result.length;
dp = new ArrayCollection();
for (i = 0; i<len; i++)
{
dp.addItem( { OrderId: result[i].OrderId, OrderTotal: result[i].OrderTotal, UserId: result[i].UserId } );
}
ordersDataGrid.dataProvider = dp;
break;
}
}
The last part is the view. We have two forms to submit data and data grids to show the results.
To handle the transactions we have a check box and a button to roll back in case we need to roll back. There are a few reasons to roll back. For instance, a commit of a few SQL commands that depend on each other failed. For example, in this demo app we have a user and an order, and in case the user couldn’t be created we may want to roll back and remove the user’s order.
Flash Player 10 and AIR allow loading files from your local system as well as saving files. Once the files are loaded you have access to the ByteArray.
Convert ByteArray into an image
You can easily convert the ByteArray into an image:
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(event:Event):void
{
event.currentTarget.removeEventListener(event.type, arguments.callee);
image.source=loader.content;
});
loader.loadBytes(event.file.fileContent);
Play ByteArray
Unfortunately, you cannot set the bytes of a video stream through the NetStream Object. What you can do is write the URLStream’s bytes to a local file, and then pass that file to the NetStream object. You can do a workaround such as writing out the URLStream’s bytes as they come in with the help of the “append” method.
What it means is that you cannot play ByteArray in AIR or Flex Application. I know that that feature is on the Adobe’s team list but until they adjust the code you will have to find a workaround.
Mixing Halo and Spark components is still not working perfectly and causing all sort of problems, but in case you need to do inline rendering using Halo component, it’s possible. This is how you would do it for a Spark list component: