Archive for the 'Flash Mobile' Category

15
Mar

Passive Multi-View design pattern - Create Flex dynamic graphical GUIs for Flash 10 using Catalyst

Currently there is a growing need, in my opinion, to create dynamic GUIs for Rich Flash applications.
Why would you need different GUIs for the same application? There are few reasons:

  • AIR/Flex application - developing an application that will be used as Flex and AIR application, sharing the same code
  • Brands - Developing different brands for the same products
  • Multi-devices application - developing an application that will be deployed on different devices such as mobile, web and desktop

Let’s talk about multi-device applications. Currently you can develop application for different OS which can be deployed on your desktop, web or mobile device (up until today UMPC and MIDs).

Adobe announced at the GSMA Mobile World Congress that Flash Player 10 will be available on Smartphones running Windows Mobile, Google’s Android, Nokia S60 / Symbian, and Palm. Combine Flash 10 availability on many devices with the release of Flash Catalyst and you can start building applications that will be served on multiple devices. Additionally, Intel announced the optimization and enabling of Adobe Flash Player 10 and Adobe AIR on ARM which creates new possibilities for creating applications using AIR for devices such as mobile phones, set-top boxes, MIDs, TVs, automotive platforms, MP3 players and many others.

Using Flash Catalyst designer can create GUI for the developer which can be served on one device, but what if you need to create the same application for two devices with different GUIs?

One is a mobile device with smaller screen, let’s say the iPhone (which Adobe already has a version of Flash for the iPhone running on emulation software) and another one will be deployed on your home computer browser with a larger screen. You can create a custom Flex component that extends UIComponent and change the sub classes once there is a need for it, however, you may want to build a completely different GUI for each device based on the device capability. Some GUIs may not have the same sub component since you want the GUI to be lightweight to accommodate mobile development.

You can use Flash Catalyst using the State design pattern or use the presentation model (see some of my previous blogs entries). Flash Catalyst allows you to work with the new design/developer separation paradigm and support states but doesn’t allow you to create multi passive views on the same project.

Take a look at the demo project deployed using the Passive Multi-View on a Nokia MID device, UMPC device as an Adobe AIR project, as well as a laptop and desktop deployment:

Adobe Flex/AIR Cross Platform Experience

The solution: I created a new design pattern that is a mix of the Passive View design pattern as well as the Factory design pattern.
With the new design pattern you can create different views. All views are using the same base high level classes, and you can combine them easily with other tools such as: Degrafa, Flex FXG, Flex 3, AS3.0 with CSS.

I used a music service called MP3 Tunes to demonstrate the new design pattern and you can view the complete application here. After you login to your account, you can select the device size and it will show you two different GUIs based on the size you selected. This is just a POC, but in real life application you can use a context class to determine the GUI to be deployed based on user’s screen size or other factors. For instance, you can create a GUI for touch screen applications, or a GUI for Playstation.

To understand the design pattern let’s take a look at the Passive View design pattern and Factory design pattern.


Passive View Design Pattern

The Passive View pattern is a derivative of the Model View Presenter (MVP), which is considered a derivative of the Model-view-controller. The Passive View pattern has some similarities to the “Code Behind” implementation, since both achieve a complete separation of ActionScript logic and MXML or AS component tags. The passive presentation model allows us to easy test our application since we can create test cases just for the logic and if needed test cases for the view
In passive view pattern we split our laundry to two piles, view and presenter:

View
* State is in the view
* View is passive and is not aware of the Presenter
Presenter
* Logic is in the Presenter.
* Presenter observes view events.
* Presenter updates the view data.
* Presenter ‘knows’ about the components in the view.
* Presenter holds the data or point to a data class.

By moving all the logic out of the view, the Passive View pattern can achieve the separation of designer and developer work and create a paradigm where it’s easy to change the view. The view class contains only the components and their states, no events, logic, changes or model. This pattern works great with Flash Catalyst since the designer responsibility is to create the view (pixel) and behavior (state) so you can copy/paste the FXG code into the application and just set the id’s of each component.

Passive View Flex UML

Factory Design Pattern
Now throw in the mix the factory design pattern and you can actually create few views for each presenter.
The factory pattern is one of the most basic creational patterns that deals with the problem of creating different products without specifying the exact concrete class that will be created. This is done by by creating a separate method for creating the product abstract class, whose subclasses can override to specify the derived type of product that will be created. The best way to describe that is to think of a pizza restaurant that holds different types of Pizza such as Mushroom pizza, Bacon pizza and other types of pizza, however, they are all pizzas that include the same ingredients such as dough, cheese and others.

Factory Design Pattern UML

Now if we mix these two design patterns together we get the following UML diagram. The Creator uses the factory pattern to find out which view (product) to use and then takes that and pushes it into the presenter. The Sub-presenter and subView can be used by the main view.

UML-multi-view-passive-view-factory

Now let’s put it to work and look at the working example with some screen shots. The application creator let you decide which view to use:

mp3tunes flex application

Once a view is selected, the presenter is provided with the view and creates the GUI:
mp3tunes flex application smaller

mp3tunes flex application larger

The screen shot is of an application I created in Illustrator, converted it to Flash Catalyst and then imported it to Flex.
Let’s take a look at the Factory Class. The class holds two constants for each product and the “AbstractMusicPlayerMain” allows us to be able to select different products that extends that class.


package com.elad.mp3tunes.view
{
	import com.elad.mp3tunes.view.mobile.MusicPlayerMain320x480;
	import com.elad.mp3tunes.view.web.MusicPlayerMain530x520;

	import flash.errors.IllegalOperationError;

	public final class MusicPlayerFactory
	{
		/**
		 * Music player types enums
		 */
		public static const WEB:int = 0;
		public static const MOBILE:int = 1;

		public static function createView(musicPlayerType:Number):AbstractMusicPlayerMain
		{
			var retVal:AbstractMusicPlayerMain;

			switch (musicPlayerType)
			{
				case WEB:
					retVal = new MusicPlayerMain530x520();
				break;
				case MOBILE:
					retVal = new MusicPlayerMain320x480();
				break;
				throw new IllegalOperationError("The view type " + musicPlayerType + " is not recognized.");
			}

			return retVal;
		}
	}
}

The abstract class holds all the components that will be used by presenter, take a look:


package com.elad.musicplayer.view
{
	import com.elad.framework.musicplayer.Player;
	import mx.components.baseClasses.FxComponent;
	import mx.components.baseClasses.FxScrollBar;
	import mx.containers.Canvas;
	import mx.controls.ProgressBar;
	import mx.graphics.graphicsClasses.TextGraphicElement;

	public class AbstractMusicPlayer extends Canvas
	{
		private var player:Player = new Player();

		// text
		public var songInfoText:TextGraphicElement;
		public var currentTimeText:TextGraphicElement;
		public var totalTimeText:TextGraphicElement;

		// Buttons
		public var playButton:FxComponent;
		public var pauseButton:FxComponent;
		public var forwardButton:FxComponent;
		public var rewindButton:FxComponent;
		public var randomButton:FxComponent;
		public var replyButton:FxComponent;
		public var artistsButton:FxComponent;
		public var albumsButton:FxComponent;

		// sliders
		public var songSlider:FxScrollBar;
		public var trackProgressBar:ProgressBar;
		public var downloadProgressBar:ProgressBar;
		public var volumeProgressBar:ProgressBar;
		public var volumeSlider:FxScrollBar;

		public function AbstractMusicPlayer()
		{
			super();
		}
	}
}

The product is each main view mxml class. Keep in mind that the MXML classes allow us to implements interfaces but not to extends a class, the closest way to extend a class is to use the abstract class in the main tag instead of let’s say the Canvas tag. Using that will use the class constructor, which typically is against how abstract is created. Once we repeat the same component they will be overriding the abstract class members;


<view:AbstractMusicPlayer xmlns="http://ns.adobe.com/mxml/2009"
	xmlns:lib="MusicPlayerSmall_library.*"
	xmlns:d="http://ns.adobe.com/fxg/2008/dt"
	xmlns:th="http://ns.adobe.com/thermo/2009"
	xmlns:ai="http://ns.adobe.com/ai/2008"
	xmlns:view="com.elad.musicplayer.view.*"
	backgroundColor="0xe6e6e6"
	width="320" height="480"
	horizontalScrollPolicy="off"
	verticalScrollPolicy="off"
	borderStyle="solid" borderThickness="3">

		<!-- Track Slider -->
		<Group>
			<ProgressBar id="downloadProgressBar"
				left="37" top="84"
				barSkin="com.elad.musicplayer.view.mobile.components.DownloadProgressBarSkin"
				trackSkin="com.elad.musicplayer.view.mobile.components.DownloadProgressTrackSkin"
				minimum="0" maximum="100"
				labelWidth="0"
				direction="right" mode="manual" />
			<ProgressBar id="trackProgressBar" alpha="0.5"
				left="37" top="84"
				barSkin="com.elad.musicplayer.view.mobile.components.TrackProgressBarSkin"
				trackSkin="com.elad.musicplayer.view.mobile.components.TrackProgressTrackSkin"
				minimum="0" maximum="100"
				labelWidth="0"
				direction="right" mode="manual" />
			<FxHScrollBar id="songSlider" left="32" top="72"
				skinClass="com.elad.musicplayer.view.mobile.components.HorizontalScrollbar1"/>
		</Group>

</view:AbstractMusicPlayer>

The presenter handles the logic of the main application, as well as using sub presenter and sub view if needed.


package com.elad.musicplayer.view.presenter
{
	import com.elad.mp3tunes.Music;
	import com.elad.mp3tunes.enum.SortType;
	import com.elad.mp3tunes.events.AlbumDataEvent;
	import com.elad.mp3tunes.events.ArtistsResultEvent;
	import com.elad.mp3tunes.events.TrackDataEvent;
	import com.elad.mp3tunes.vo.AlbumItemVO;
	import com.elad.mp3tunes.vo.AlbumListVO;
	import com.elad.mp3tunes.vo.ArtistItemVO;
	import com.elad.mp3tunes.vo.ArtistListVO;
	import com.elad.mp3tunes.vo.TrackItemVO;
	import com.elad.mp3tunes.vo.TrackListVO;
	import com.elad.musicplayer.view.AbstractMusicPlayerMain;
	import flash.events.Event;
	import mx.collections.ArrayCollection;
	import mx.controls.Alert;
	import mx.events.ListEvent;

	/**
	 * Presentation Pattern - Passive View
	 *
	 * @author Elad
	 *
	 */
	 [Bindable]
	public class MusicPlayerMainPresenter
	{

	    //--------------------------------------------------------------------------
	    //
	    //  Variables
	    //
	    //--------------------------------------------------------------------------
		private var music:Music = Music.getInstance();
		private var artistList:ArtistListVO = null;
		private var albumList:AlbumListVO = null;
		private var trackList:TrackListVO = null;
		private var tileResultType:String;

		// Corresponding view
		private var musicPlayerMain:AbstractMusicPlayerMain;

        // Sub-presenters
        private var musicPlayerPresenter:MusicPlayerPresenter;

	    //--------------------------------------------------------------------------
	    //
	    //  Constructor
	    //
	    //--------------------------------------------------------------------------
		public function MusicPlayerMainPresenter(musicPlayerMain:AbstractMusicPlayerMain)
		{
			this.musicPlayerMain = musicPlayerMain;
			musicPlayerPresenter = new MusicPlayerPresenter(musicPlayerMain.musicPlayer);
			musicPlayerMain.dg.addEventListener(Event.CHANGE, dgChangeHandler);
			musicPlayerMain.tileList.addEventListener(Event.CHANGE, selectTileListItem);
			musicPlayerMain.musicPlayer.addEventListener(MusicPlayerPresenter.NEXT_TRACK_EVENT, nextTrack);
			musicPlayerMain.musicPlayer.addEventListener(MusicPlayerPresenter.PREVIOUS_TRACK_EVENT, previousTrack);
			musicPlayerMain.musicPlayer.addEventListener(MusicPlayerPresenter.ARTISTS_CLICK_EVENT, getAllArtists);
			musicPlayerMain.musicPlayer.addEventListener(MusicPlayerPresenter.ALBUMS_CLICK_EVENT, getAllAlbums);
			musicPlayerMain.musicPlayer.addEventListener(MusicPlayerPresenter.PLAYING_COMPLETED, function():void { nextTrack(null); } );
			getAllArtists();
		}

	    //--------------------------------------------------------------------------
	    //
	    //  Class methods
	    //
	    //--------------------------------------------------------------------------

		private function nextTrack(event:Event):void
		{
			var trackItem:TrackItemVO = trackList.getItem(++musicPlayerMain.dg.selectedIndex);
			musicPlayerPresenter.playSong(trackItem);
		}

		private function previousTrack(event:Event):void
		{
			var trackItem:TrackItemVO = trackList.getItem(--musicPlayerMain.dg.selectedIndex);
			musicPlayerPresenter.playSong(trackItem);
		}

		private function getAllAlbums(event:Event=null):void
		{
			music.addEventListener(AlbumDataEvent.ALL_ALBUMS_DATA_COMPLETED, onAllAlbumCompleted);
			music.getAllAlbums(artistList);
		}

		private function getAllArtists(event:Event=null):void
		{
			music.addEventListener(ArtistsResultEvent.ARTIST_RESULT_COMPLETED, onArtistsResult);
			music.addEventListener(ArtistsResultEvent.ARTIST_RESULT_ERROR, function(event:ArtistsResultEvent):void { Alert.show(String(event.message)); });
			music.getMusicByArtists();
		}

	    //--------------------------------------------------------------------------
	    //
	    //  Event handlers
	    //
	    //--------------------------------------------------------------------------
		protected function selectTileListItem(event:ListEvent):void
		{
			var index:Number = event.columnIndex;
			getTrackList(index);
		}

		protected function getTrackList(index:Number):void
		{
			var id:String;
			music.addEventListener(TrackDataEvent.TRACK_DATA_COMPLETED, onTrackDataComplete);
			music.addEventListener(TrackDataEvent.TRACK_DATA_ERROR, function():void { Alert.show("Error getting track data"); });
			// based on the type in the tile list
			switch (tileResultType)
			{
				case SortType.ALBUMS:
					var albumItem:AlbumItemVO = albumList.getItem(index);
					id = albumItem.albumId;
				break
				case SortType.ARTISTS:
					var artistItem:ArtistItemVO = artistList.getItem(index);
					id = artistItem.artistId;
				break
			}
			music.getTrackData(id, tileResultType);
		}

		protected function dgChangeHandler(event:ListEvent):void
		{
			var index:Number = event.rowIndex;
			var trackItem:TrackItemVO = trackList.getItem(index);
			musicPlayerPresenter.playSong(trackItem);
		}

		private function onArtistsResult(event:ArtistsResultEvent):void
		{
			music.removeEventListener(ArtistsResultEvent.ARTIST_RESULT_COMPLETED, onArtistsResult);
			tileResultType = SortType.ARTISTS;
			artistList = new ArtistListVO(event.artistList.list.source);
			var item:ArtistItemVO = artistList.getItem(0);
			var dp:ArrayCollection = new ArrayCollection();
			for (var i:Number = 0; i<artistList.list.length; i++)
			{
				item = artistList.getItem(i);
				dp.addItem({name: item.artistName, count: item.trackCount});
			}
			musicPlayerMain.tileList.dataProvider = dp;
			musicPlayerMain.tileList.selectedIndex = 0;
			getTrackList(0);
		}

		private function onAllAlbumCompleted(event:AlbumDataEvent):void
		{
			music.removeEventListener(AlbumDataEvent.ALL_ALBUMS_DATA_COMPLETED, onAllAlbumCompleted);
			tileResultType = SortType.ALBUMS;
			albumList = new AlbumListVO(event.collection.list.source);
			var item:AlbumItemVO;
			var dp:ArrayCollection = new ArrayCollection();
			for (var i:Number = 0; i<albumList.list.length; i++)
			{
				item = albumList.getItem(i);
				dp.addItem({name: item.albumTitle, count: item.trackCount});
			}
			musicPlayerMain.tileList.dataProvider = dp;
			musicPlayerMain.tileList.selectedIndex = 0;
			getTrackList(0);
		}

		private function onAlbumDataComplete(event:AlbumDataEvent):void
		{
			music.removeEventListener(AlbumDataEvent.ALBUM_DATA_COMPLETED, onAlbumDataComplete);
			music.removeEventListener(AlbumDataEvent.ALBUM_DATA_ERROR, onAlbumDataComplete);
			var albumList:AlbumListVO = new AlbumListVO(event.collection.list.source);
			var albumId:String = albumList.getItem(0).albumId;
			music.addEventListener(TrackDataEvent.TRACK_DATA_COMPLETED, onTrackDataComplete);
			music.addEventListener(TrackDataEvent.TRACK_DATA_ERROR, function():void { Alert.show("Error getting track data"); });
			music.getTrackData(albumId);
		}

		private function onTrackDataComplete(event:TrackDataEvent):void
		{
			music.removeEventListener(AlbumDataEvent.ALBUM_DATA_COMPLETED, onAlbumDataComplete);
			music.removeEventListener(TrackDataEvent.TRACK_DATA_ERROR, function():void { Alert.show("Error getting track data"); });
			trackList = new TrackListVO(event.collection.list.source);
			// show track list
			musicPlayerMain.dg.dataProvider = trackList.list.source;
			// play first song
			var trackItem:TrackItemVO = trackList.getItem(0);
			musicPlayerPresenter.playSong(trackItem);
			// set selected song
			musicPlayerMain.dg.selectedIndex = 0;
		}
	}
}

The creator then takes both the product and the presenter and create a composition:


var musicPlayerView:AbstractMusicPlayerMain = MusicPlayerFactory.createView(type);
this.addChild(musicPlayerView);
musicPlayerMainPresenter = new MusicPlayerMainPresenter(musicPlayerView);

see complete of the creator:


<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
	backgroundColor="0xe6e6e6"
	x="0" y="0"
	verticalAlign="middle"
	creationComplete="creationCompleteHandler(event)">
	<mx:Style source="assets/css/main.css" />
	<mx:Script>

		<![CDATA[
			import com.elad.musicplayer.view.presenter.MusicPlayerMainPresenter;
			import com.elad.musicplayer.view.AbstractMusicPlayerMain;
			import com.elad.musicplayer.view.LoginForm;
			import mx.managers.PopUpManager;
			import mx.containers.TitleWindow;
			import mx.events.FlexEvent;
			import com.elad.musicplayer.view.MusicPlayerFactory;
			import com.elad.musicplayer.view.AbstractMusicPlayerMain;
            private var loginForm:LoginForm;
			private var musicPlayerMainPresenter:MusicPlayerMainPresenter;
			// handler after creation complete
			protected function creationCompleteHandler(event:FlexEvent):void
			{
				loginForm = LoginForm(PopUpManager.createPopUp(this, LoginForm, true));
				loginForm.addEventListener(LoginForm.LOGIN_SUCCESSFULL, onLogin);
			}

			// method to load the view
			protected function loadView(type:Number):void
			{
				hBox.visible = false;
				hBox = null;
				var musicPlayerView:AbstractMusicPlayerMain = MusicPlayerFactory.createView(type);
				this.addChild(musicPlayerView);
				musicPlayerMainPresenter = new MusicPlayerMainPresenter(musicPlayerView);
			}

			private function onLogin(event:Event):void
			{
				hBox.visible = true;
			}
		]]>
	</mx:Script>

	<mx:HBox id="hBox" visible="false">
		<mx:Button label="320x480" click="loadView(MusicPlayerFactory.MOBILE)" />
		<mx:Button label="530x520" click="loadView(MusicPlayerFactory.WEB)" />
	</mx:HBox>

</mx:Application>

To view the complete application Click here.
Please note that you have to have an account with MP3tunes Music Locker in order to login into your account and use the application.

21
Feb

mp3tunes-as3-api open source API to use MP3Tunes in Flex/AIR/AS3 projects

mp3tunes is a service to load your audio files and share them between different devices such as: desktop, mobile, game consoles and the Web. I just completed creating a foundation API for AS3.0 so you can start using the API in your Flex/AIR/AS3.0 projects.
You can see the project in google code: http://code.google.com/p/mp3tunes-as3-api/
The SWC and ASDOC can be downloaded from here: http://mp3tunes-as3-api.googlecode.com/files/Archive.zip
And here’s a simple implementation of the API in Flex 4 (Gumbo). The code login into the MP3tunes Music API, retrieve album result, artist data, track data and plays the first audio file in your account.


<FxApplication xmlns="http://ns.adobe.com/mxml/2009"
	creationComplete="creationCompleteHandler(event)">
	<Script>
		<![CDATA[
			import flash.events.Event;
			import flash.media.Sound;
			import flash.net.URLRequest;
			import com.elad.MP3tunes.vo.TrackItemVO;
			import com.elad.MP3tunes.vo.TrackListVO;
			import com.elad.MP3tunes.events.TrackDataEvent;
			import com.elad.MP3tunes.vo.AlbumListVO;
			import com.elad.MP3tunes.events.AlbumDataEvent;
			import com.elad.MP3tunes.vo.ArtistItemVO;
			import com.elad.MP3tunes.vo.ArtistListVO;
			import mx.controls.Alert;
			import com.elad.MP3tunes.events.ArtistsResultEvent;
			import com.elad.MP3tunes.events.MusicEvent;
			import mx.events.FlexEvent;
			import com.elad.MP3tunes.Music;
			private var music:Music;
			protected function creationCompleteHandler(event:FlexEvent):void
			{
				music = new Music("YOUR_DEVELOPER_KEY");
				music.addEventListener(MusicEvent.LOGIN_SUCCESSFULL, onLogin);
				music.addEventListener(MusicEvent.LOGIN_ERROR, function():void { Alert.show("Error login"); } );
				music.login("YOUR_USERNAME", "YOUR_PASSWORD");
			}
			private function onLogin(event:MusicEvent):void
			{
				music.removeEventListener(MusicEvent.LOGIN_SUCCESSFULL, onLogin);
				music.addEventListener(ArtistsResultEvent.ARTIST_RESULT_COMPLETED, onArtistsResult);
				music.addEventListener(ArtistsResultEvent.ARTIST_RESULT_ERROR, function():void { Alert.show("Error getting artist list"); });
				music.getMusicByArtists();
			}
			private function onArtistsResult(event:ArtistsResultEvent):void
			{
				music.removeEventListener(ArtistsResultEvent.ARTIST_RESULT_COMPLETED, onArtistsResult);
				var artistList:ArtistListVO = new ArtistListVO(event.artistList.list.source);
				var item:ArtistItemVO = artistList.getItem(0);
				music.addEventListener(AlbumDataEvent.ALBUM_DATA_COMPLETED, onAlbumDataComplete);
				music.addEventListener(AlbumDataEvent.ALBUM_DATA_ERROR, function():void { Alert.show("Error getting album list"); });
				music.getAlbumData(item.artistId);
			}
			private function onAlbumDataComplete(event:AlbumDataEvent):void
			{
				music.removeEventListener(AlbumDataEvent.ALBUM_DATA_COMPLETED, onAlbumDataComplete);
				music.removeEventListener(AlbumDataEvent.ALBUM_DATA_ERROR, onAlbumDataComplete);
				var albumList:AlbumListVO = new AlbumListVO(event.collection.list.source);
				var albumId:String = albumList.getItem(0).albumId;
				music.addEventListener(TrackDataEvent.TRACK_DATA_COMPLETED, onTrackDataComplete);
				music.addEventListener(TrackDataEvent.TRACK_DATA_ERROR, function():void { Alert.show("Error getting track data"); });
				music.getTrackData(albumId);
			}
			private function onTrackDataComplete(event:TrackDataEvent):void
			{
				music.removeEventListener(AlbumDataEvent.ALBUM_DATA_COMPLETED, onAlbumDataComplete);
				music.removeEventListener(TrackDataEvent.TRACK_DATA_ERROR, function():void { Alert.show("Error getting track data"); });
				var trackList:TrackListVO = new TrackListVO(event.collection.list.source);
				var trackItem:TrackItemVO = trackList.getItem(0);
				playSong(trackItem.downloadURL);
			}
			private function playSong(url:String):void
			{
				var sound:Sound = new Sound();
				var req:URLRequest = new URLRequest(url);
				sound.load(req);
				sound.play();
			}
		]]>
	<Script>
<FxApplication>
</p>
18
Feb

Flash 10 on Mobile devices in early 2010

Adobe announced at the GSMA Mobile World Congress this month, that Flash Player 10, will be available on Smartphones running Windows Mobile, Google’s Android, Nokia S60 / Symbian, and Palm. These mobile devices with Flash Player 10 are expected to hit the market starting early 2010.

What about the iPhone?

In recent interview to Bloomberg Television, Adobe announced that onus is on them to create a well working version of Flash that runs on iPhone. Shantanu Narayan, CEO of Adobe, said:

“It’s a hard technical challenge, and that’s part of the reason Apple and Adobe are collaborating [...] The ball is in our court. The onus is on us to deliver.”

Adobe already has a version of Flash for the iPhone running on emulation software. So far Apple seems to be interested of people developing applications using their Objective C as well as promoting Sproutcore, which is a JavaScript framework that compete with Flash.

I believe that the challenge in bringing Flash 10 to mobile is taking in consideration mobile constrains such as limited resources, platform awareness and adaptation. Taking desktop application and deploying them on mobile with usage of 3D graphics, sound and video can drain the battery pretty quickly and show poor performance. Adobe will most likely to create a version of Flash 10 that has reduced functionality, however has some additional APIs to be able to tap into the touch screen as well as the GPS or other hardware devices.

23
Jan

Adobe AIR Mobile Touch and Multi-Touch screen applications

Today, there are already few systems that are using Adobe AIR to build a multi-touch GUI. There is an open project called Touchlib that allows listening to user gestures and building your own touch-screen. Intuilab has presented in Adobe MAX in Milan an application that takes full advantage of surface computers. The system integrates many contents such as: image, video, web content, Adobe pdf, Flash, Illustrator, Photoshop, Microsoft Office etc.

The idea behind Touch and Multi-touch computer screens is to follow the instructions of finger/s or hand/s and be able to track gestures. The idea is simple:

1. Register to receive gestures events.
2. Handle gesture events.
3. Interpret the gesture events.

Creating a touch screen application on UMPC can be done today, and we can rely on the UMPC device to handle the touch screen; however you may find that relying entirely on the device may be buggy.

For instance, the device may register a user gesture when your fingertip is on the device and didn’t even move your finger since a slight move also get registered, however you may not need your application to be that sensitive.

I recommend implementing your own instructions to register user gestures. Microsoft released the beta version of Windows 7 which track user gestures via WM_GESTURECOMMAND, WM_GESTURE and WM_TOUCH so I think it’s possible to communicate with these method using a proxy such as Merapi or you can create your Java proxy.

I have created a simple POC that I havn’t test much but it gives you an idea. The API is for touch screen application for mobile devices such as UMPC that register user gestures and can be expended to register multi-touch screen user gestures. The way it works is that the API translate the mouse events into user gestures based on time and movement.

Here’s a screen shot of the application on a UMPC:
AIR Touch Screen

And you can download it from here:
http://elromdesign.com/blog/Flex/TouchScreen/TouchScreen/Touchscreen.zip

Take a look at the class below that gives its own instructions on when to register user gestures.


package com.elad.framework.touchscreen
{
	import com.elad.framework.touchscreen.events.TouchEvent;
	import com.elad.framework.touchscreen.vo.TouchVO;

	import flash.events.EventDispatcher;
	import flash.events.MouseEvent;
	import flash.utils.Timer;

	import mx.core.UIComponent;

	public class TouchManager extends EventDispatcher
	{
		private var moveTimer:Timer;
		private var previousX:int = 0;
		private var previousY:int = 0;
		private var component:UIComponent;

		public function TouchManager(component:UIComponent)
		{
			this.component = component;
		}

		public function start():void
		{
			initialize();
			this
		}

		public function stop():void
		{
			component.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDownHandler);
			component.removeEventListener(MouseEvent.MOUSE_UP, onMouseUpHandler);

			moveTimer.stop();
			moveTimer = null;
		}		

		protected function initialize():void
		{
			component.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDownHandler);
			component.addEventListener(MouseEvent.MOUSE_UP, onMouseUpHandler);
		}

		private function startTimer():void
		{
			moveTimer = new Timer(100,1000);
			moveTimer.start();
		}

		private function onMouseDownHandler(event:MouseEvent):void
		{
			startTimer();
			component.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMoveHandler);

			var touch:TouchVO = new TouchVO(this.previousX, this.previousY, event.localX, event.localY, this.moveTimer.currentCount);
			this.dispatchEvent( new TouchEvent(	TouchEvent.TOUCH_DOWN, touch ) );
		}

		private function onMouseUpHandler(event:MouseEvent):void
		{
			moveTimer.stop();
			component.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMoveHandler);

			var touch:TouchVO = new TouchVO(this.previousX, this.previousY, event.localX, event.localY, this.moveTimer.currentCount);
			this.dispatchEvent( new TouchEvent(	TouchEvent.TOUCH_UP, touch ) );
		}

		private function onMouseMoveHandler(event:MouseEvent):void
		{
			var isMove:Boolean = isTouchMove(event.localX, event.localY);
			var touch:TouchVO = new TouchVO(this.previousX, this.previousY, event.localX, event.localY, this.moveTimer.currentCount);

			if (isMove)
			{
				this.dispatchEvent( new TouchEvent(TouchEvent.TOUCH_DRAG, touch) );
			}
		}

		private function isTouchMove(x:int, y:int):Boolean
		{
			var retVal:Boolean = false;
			var ignore:int = 3;
			var isXmoved:Boolean;
			var isYmoved:Boolean;

			if (previousX != 0 && previousY != 0)
			{
				isXmoved = (x > previousX+ignore || x < previousX-ignore) ? true : false;
				isYmoved = (y > previousY+ignore || y < previousY-ignore) ? true : false;

				if ( isXmoved || isYmoved )
				{
					retVal=true;
				}
			}

			previousX = x;
			previousY = y;

			return retVal;
		}		

	}
}

And than we can implement the class with a simple interface:


<WindowedApplication xmlns="http://ns.adobe.com/mxml/2009" layout="absolute"
	width="800" height="600"
	initialize="initializeHandler()">

	<Script>
		<![CDATA[
			import com.elad.framework.touchscreen.vo.TouchVO;
			import com.elad.framework.touchscreen.events.TouchEvent;
			import com.elad.framework.touchscreen.TouchManager;
			import mx.collections.ArrayCollection;

			[Bindable]
			private var arrayCollection:ArrayCollection = new ArrayCollection;

			private var touch:TouchManager;

			protected function initializeHandler():void
			{
				touch = new TouchManager(this);
				touch.addEventListener(TouchEvent.TOUCH_DOWN, onMouseDownHandler);
				touch.addEventListener(TouchEvent.TOUCH_UP, onMouseUpHandler);
				touch.addEventListener(TouchEvent.TOUCH_DRAG, onMouseDragHandler);

				touch.start();
			}						

			private function onMouseDownHandler(event:TouchEvent):void
			{
				ellipse.visible = true;
				moveEllipse(event.touchVO.currentX, event.touchVO.currentY);
				registerLocation(ellipse.x, ellipse.y, "MouseDown", 0);
			}	

			private function onMouseUpHandler(event:TouchEvent):void
			{
				ellipse.visible = false;
			}					

			private function onMouseDragHandler(event:TouchEvent):void
			{
					moveEllipse(event.touchVO.currentX, event.touchVO.currentY);
					registerLocation(ellipse.x, ellipse.y, "MouseMove", event.touchVO.moveTimer);
			}

			private function registerLocation(x:int, y:int, type:String, time:int):void
			{
				arrayCollection.addItem({locationX: ellipse.x, locationY: ellipse.y, type: type, time: time});
				dg.dataProvider = arrayCollection;
			}

			private function moveEllipse(x:int, y:int):void
			{
				ellipse.x = x - ellipse.width/2;
				ellipse.y = y - ellipse.height/2;
			}			

		]]>
	</Script>

	<Group>
		<Ellipse height="80" width="80" id="ellipse" visible="false">
			<stroke>
				<SolidColorStroke color="0x000000" weight="2"/>
			</stroke>
		</Ellipse>
	</Group>

	<DataGrid x="341" y="4" id="dg" dataProvider="{arrayCollection}" height="410">
		<columns>
			<DataGridColumn headerText="X" dataField="locationX"/>
			<DataGridColumn headerText="Y" dataField="locationY"/>
			<DataGridColumn headerText="Type" dataField="type"/>
			<DataGridColumn headerText="Time" dataField="time"/>
		</columns>
	</DataGrid>

I am looking for a .NET developer to spare time to create a simple class that gets Windows 7 events and compile them into a DLL, so we can pick them up in a Java proxy, which will make a request every few millisecond and look for user gesture changes, so if you are interested, please reply in this blog post.

17
Jan

Just released - Adobe AIR YouTube Video Widget works Online and Offline. Watch a presentation video

Alpha version of YouTube Video Widget, allows you to search videos from YouTube and download them into your local drive. Once you go offline the application automatically aware of the change and switches to offline mode, which allow you to view the downloaded videos.

Application can be deployed on desktops or on a touch screen UMPC as well as future ARM devices that will support AIR 1.5.

Download the application from Adobe AIR Marketplace or from here.

Developed with Flex 4 (Gumbo), Flash Catalyst and AIR 1.5. The framework is Caringorm with the Presentation Model, allows us to easily create a template so the application changes based on platform.

YouTube AIR Widget Preview

Once you select a video you can download the video. Which will be saved on your local drive and information captured in SQLite database. In fact the application recognize automatically that you went offline.

YouTube AIR Widget Preview, download video.

I made a quick presentation that shows the application capabilities. Take a look at the video below:

The full tutorials and explanation on how to build applications like this one will be available in our new book which will be out in May by APress and can be purchased now for a limited discount price from Amazon.

05
Jan

Cross Platform AIR Application on Flex Gumbo - Context Awareness Manager to gather, detect and translate system info

The cure in adopting AIR as a cross platform application as well as creating mobile applications is to be able to know the user’s system capabilities as well as track changes in its environment.

The AIR Context Awareness Manager can help you develop a cross-platform application which is operation system (OS) independent.

AIR Context Awareness Manager API can be utilized to gather and track information available in our application. We can split the information gathered into three categories:

1. System capability and support.
2. User activity.
3. Track changes in system.

As new capabilities will be available we can add them to the manager.

Download SWC, example and source code from Google code:
http://code.google.com/p/contextawarenessmanager/

Adobe AIR is available on PC, MAC and Linux, and as Adobe Open Screen Project materializes into a reality with the announcement of Flash 10 and AIR available on mobile devices, the challenge will increase.

Platforms are usually capable of adjusting to many changes in the device on their own. For instance, system track network errors and displaying an error message, however, our application cannot rely entirely on the platform to adjust correctly and we may want to make changes in the application once changes in the device occur. For instance, our AIR application can go into offline mode once we are not connected to the internet.

In essence our AIR application should have the ability to react and adjust during the entire lifetime of the application, additionally our application is responsible for hiding issues and constraints of the mobile device from the user. For instance, user doesn’t care that our system has low network download speed and will blame our application on choppy video.

In order to achieve that kind of awareness in our application, we should be able to be aware of the changes as well as translate the context. That’s where Context Awareness comes into place. Context Awareness means that once we get information regarding the application platform and as changes are made in the platform environment we need to translate the information and understand what it means as well as react accordingly.

Most of the system capability information is available through the flash.system.Capabilities API. The Capabilities that API provides are properties that describe the system and flash player that are hosting the application. ContextVO will hold the capability properties as well as other properties.

Detecting System Capabilities


          public function ContextVO()
          {
               avHardwareDisable = Capabilities.avHardwareDisable;
               hasAccessibility = Capabilities.hasAccessibility;
               hasAudio = Capabilities.hasAudio;
               hasAudioEncoder  = Capabilities.hasAudioEncoder;
               hasEmbeddedVideo = Capabilities.hasEmbeddedVideo;
               hasMP3 = Capabilities.hasMP3;
               hasPrinting = Capabilities.hasPrinting;
               hasScreenBroadcast = Capabilities.hasScreenBroadcast;
               hasScreenPlayback = Capabilities.hasScreenPlayback;
               hasStreamingAudio = Capabilities.hasStreamingAudio;
               hasVideoEncoder = Capabilities.hasVideoEncoder;
               isDebugger = Capabilities.isDebugger;
               language = Capabilities.language;
               localFileReadDisable = Capabilities.localFileReadDisable;
               manufacturer = Capabilities.manufacturer;
               os = Capabilities.os;
               osName = Capabilities.os.substr(0, 3).toLowerCase();
               pixelAspectRatio = Capabilities.pixelAspectRatio;
               playerType = Capabilities.playerType;
               screenColor = Capabilities.screenColor;
               screenDPI = Capabilities.screenDPI;
               screenResolutionX = Capabilities.screenResolutionX;
               screenResolutionY = Capabilities.screenResolutionY;
               serverString = Capabilities.serverString;
               version = Capabilities.version;
          }

Detecting System Support


private function setSystemSupportCapability():void
{
     contextVO.supportsDockIcon = NativeApplication.supportsDockIcon;
     contextVO.supportsMenu = NativeApplication.supportsMenu;
     contextVO.supportsSystemTrayIcon = NativeApplication.supportsSystemTrayIcon;
     contextVO.supportsMenu = NativeWindow.supportsMenu;
     contextVO.supportsNotification = NativeWindow.supportsNotification;
     contextVO.supportsTransparency = NativeWindow.supportsTransparency;
     contextVO.systemMaxSize = NativeWindow.systemMaxSize;
     contextVO.systemMinSize = NativeWindow.systemMinSize;
}

Detecting User Presence


private function detectUserPresence():void
{
     nativeApp.idleThreshold = idleThresholdTime;
     nativeApp.addEventListener(Event.USER_IDLE, onUserIdleHandler);
     nativeApp.addEventListener(Event.USER_PRESENT, onUserPresentHandler);
}
private function onUserIdleHandler(evt:Event):void
{
     var lastUserInput:Number = NativeApplication.nativeApplication.timeSinceLastUserInput;
     var event:Event = new Event(USER_IDLE, true);
     contextVO.isUserPresent = false;
     contextVO.lastUserInput = lastUserInput;
     this.dispatchEvent(event);
}
private function onUserPresentHandler(evt:Event):void
{
     var event:Event = new Event(USER_PRESENT, true);
     contextVO.isUserPresent = true;
     this.dispatchEvent(event);
}

Detecting network connectivity changes


private function detectNetworkChanges():void
{
     nativeApp.addEventListener(Event.NETWORK_CHANGE, onNetworkStatusChange);
}
private function onNetworkStatusChange(evt:Event):void
{
     var event:Event = new Event(NETWORK_CHANGE, true);
     contextVO.isNetworkChanged = true;
     this.dispatchEvent(event);
}

Detecting HTTP connectivity


private function detectHTTPConnectivity():void
{
     monitor = new URLMonitor(new URLRequest(siteToTrack));
     monitor.addEventListener(StatusEvent.STATUS, onHTTPConnectivityChange);
     monitor.start();
}
private function onHTTPConnectivityChange(evt:StatusEvent):void
{
    var event:Event;
    contextVO.isHTTPAvaliable = monitor.available;
    event = (monitor.available) ? new Event(HTTP_CONNECTIVITY_TRUE, true) :
         new Event(HTTP_CONNECTIVITY_FALSE, true);</p>
<p>     this.dispatchEvent(event);
}

Detecting socket connectivity


private function detectSocketConnectivity():void
{
     socketMonitor = new SocketMonitor(siteSocketMonitor,portToCheck);
     socketMonitor.addEventListener(StatusEvent.STATUS, onSocketStatusChange);
     socketMonitor.start();
}
private function onSocketStatusChange(evt:StatusEvent):void
{
     var event:Event;
     contextVO.isSocketMonitorAvailable = socketMonitor.available;
     event = (socketMonitor.available) ? new Event(SOCKET_CONNECTIVITY_TRUE, true) :
         new Event(SOCKET_CONNECTIVITY_FALSE, true);
     this.dispatchEvent(event);
}

Detecting Local Drives


private function detectLocalDrivers():void
{
     contextVO.currentAvaliableDrives = (contextVO.osName=="mac") ?
          new File('/Volumes/').getDirectoryListing() : File.getRootDirectories() ;
}
public function refreshLocalDrives():void
{
     detectLocalDrivers();
}

Detecting WindowedApplication movement


private function detectWindowedApplicationMovment():void
{
     Application.application.addEventListener(NativeWindowBoundsEvent.MOVING, onWindowedApplicationMovment);
}
private function onWindowedApplicationMovment(evt:NativeWindowBoundsEvent):void
{
     var event:Event = new Event(NATIVE_WINDOW_MOVED, true);
     contextVO.windowPositionAfterBounds = evt.afterBounds;
     contextVO.windowPositionBeforeBounds = evt.beforeBounds;
     this.dispatchEvent(event);
}

Getting the runtime version and patch level


private function setRuntimeInformation():void
{
     contextVO.getRuntimeVersion = nativeApp.runtimeVersion;
     contextVO.getRuntimePatchLevel = nativeApp.runtimePatchLevel;
}

I have created an example of a monitor application that will display the changes across the application. Take a look at the code below:

Context Awareness Manager


<WindowedApplication xmlns="http://ns.adobe.com/mxml/2009"
	layout="absolute"
	creationComplete="init()">

	<Script>
		<![CDATA[
			import com.elad.framework.utils.ContextAwarenessManager;

			private var contextAwareness:ContextAwarenessManager = ContextAwarenessManager.getInstance();

			private function init():void
			{
				contextAwareness.addEventListener(ContextAwarenessManager.USER_IDLE, onUserHandler);
				contextAwareness.addEventListener(ContextAwarenessManager.USER_PRESENT, onUserHandler);
				contextAwareness.addEventListener(ContextAwarenessManager.HTTP_CONNECTIVITY_TRUE, onHTTPConnectivityChange);
				contextAwareness.addEventListener(ContextAwarenessManager.HTTP_CONNECTIVITY_FALSE, onHTTPConnectivityChange);
				contextAwareness.addEventListener(ContextAwarenessManager.NATIVE_WINDOW_MOVED, onWindowedApplicationMovment);
				contextAwareness.addEventListener(ContextAwarenessManager.NETWORK_CHANGE, onNetworkStatusChange);
				contextAwareness.addEventListener(ContextAwarenessManager.SOCKET_CONNECTIVITY_FALSE, onSocketStatusChange);
				contextAwareness.addEventListener(ContextAwarenessManager.SOCKET_CONNECTIVITY_TRUE, onSocketStatusChange);

				contextAwareness.start();
				textArea.text = "Start Tracking";
			}

			private function onUserHandler(evt:Event):void
			{
				textArea.text += "\nisUserPresent: "+contextAwareness.contextVO.isUserPresent.toString();
				textArea.text += "\nlastUserInput: "+contextAwareness.contextVO.lastUserInput.toString();
			}

			private function onNetworkStatusChange(evt:Event):void
			{
				textArea.text += "\nisNetworkChanged: "+contextAwareness.contextVO.isNetworkChanged.toString();
			}

			private function onHTTPConnectivityChange(evt:Event):void
			{
			    textArea.text += "\nisHTTPAvaliable: "+contextAwareness.contextVO.isHTTPAvaliable.toString();
			}

			private function onSocketStatusChange(evt:Event):void
			{
				textArea.text += "\nisSocketMonitorAvailable: "+contextAwareness.contextVO.isSocketMonitorAvailable.toString();
			}

			private function onWindowedApplicationMovment(evt:Event):void
			{
				textArea.text += "\nwindowPositionAfterBounds: "+contextAwareness.contextVO.windowPositionAfterBounds.toString();
				textArea.text += "\nwindowPositionBeforeBounds: "+contextAwareness.contextVO.windowPositionBeforeBounds.toString();
			}

			private function showCapabilitiesClickHandler(event:MouseEvent):void
			{
				textArea.text += "\navHardwareDisable: "+contextAwareness.contextVO.avHardwareDisable.toString();
				textArea.text += "\nhasAccessibility: "+contextAwareness.contextVO.hasAccessibility.toString();
				textArea.text += "\nhasAudio: "+contextAwareness.contextVO.hasAudio.toString();
				textArea.text += "\nhasAudioEncoder : "+contextAwareness.contextVO.hasAudioEncoder.toString();
				textArea.text += "\nhasEmbeddedVideo: "+contextAwareness.contextVO.hasEmbeddedVideo.toString();
				textArea.text += "\nhasMP3: "+contextAwareness.contextVO.hasMP3.toString();
				textArea.text += "\nhasPrinting: "+contextAwareness.contextVO.hasPrinting.toString();
				textArea.text += "\nhasScreenBroadcast: "+contextAwareness.contextVO.hasScreenBroadcast.toString();
				textArea.text += "\nhasScreenPlayback: "+contextAwareness.contextVO.hasScreenPlayback.toString();
				textArea.text += "\nhasStreamingAudio: "+contextAwareness.contextVO.hasStreamingAudio.toString();
				textArea.text += "\nhasVideoEncoder: "+contextAwareness.contextVO.hasVideoEncoder.toString();
				textArea.text += "\nisDebugger: "+contextAwareness.contextVO.isDebugger.toString();
				textArea.text += "\nlanguage: "+contextAwareness.contextVO.language.toString();
				textArea.text += "\nlocalFileReadDisable: "+contextAwareness.contextVO.localFileReadDisable.toString();
				textArea.text += "\nmanufacturer: "+contextAwareness.contextVO.manufacturer.toString();
				textArea.text += "\nos: "+contextAwareness.contextVO.os.toString();
				textArea.text += "\nosName: "+contextAwareness.contextVO.osName.toString();
				textArea.text += "\npixelAspectRatio: "+contextAwareness.contextVO.pixelAspectRatio.toString();
				textArea.text += "\nplayerType: "+contextAwareness.contextVO.playerType.toString();
				textArea.text += "\nscreenColor: "+contextAwareness.contextVO.screenColor.toString();
				textArea.text += "\nscreenDPI: "+contextAwareness.contextVO.screenDPI.toString();
				textArea.text += "\nscreenResolutionX: "+contextAwareness.contextVO.screenResolutionX.toString();
				textArea.text += "\nscreenResolutionY: "+contextAwareness.contextVO.screenResolutionY.toString();
				textArea.text += "\nserverString: "+contextAwareness.contextVO.serverString.toString();
				textArea.text += "\nversion: "+contextAwareness.contextVO.version.toString();
			}

			private function showSystemSupportClickHandler(event:MouseEvent):void
			{
				textArea.text += "\nsupportsDockIcon: "+contextAwareness.contextVO.supportsDockIcon.toString();
				textArea.text += "\nsupportsMenu: "+contextAwareness.contextVO.supportsMenu.toString();
				textArea.text += "\nsupportsSystemTrayIcon: "+contextAwareness.contextVO.supportsSystemTrayIcon.toString();
				textArea.text += "\nsupportsNotification: "+contextAwareness.contextVO.supportsNotification.toString();
				textArea.text += "\nsupportsTransparency: "+contextAwareness.contextVO.supportsTransparency.toString();
				textArea.text += "\nsystemMaxSize: "+contextAwareness.contextVO.systemMaxSize.toString();
				textArea.text += "\nsystemMinSize: "+contextAwareness.contextVO.systemMinSize.toString();
			}

		]]>
	</Script>

	<VBox width="100%" height="100%" >
		<TextArea id="textArea" width="100%" height="95%" />

		<HBox>
			<Button label="Show System Capabilities" click="showCapabilitiesClickHandler(event)" />
			<Button label="Show System Support" click="showSystemSupportClickHandler(event)" />
		</HBox>

		<HBox>
			<Button label="Start Tracking" click="contextAwareness.start(); textArea.text+='\nStart Tracking';" />
			<Button label="Stop Tracking" click="contextAwareness.stop(); textArea.text+='\nStop Tracking';" />
			<Button label="Clear TextArea" click="textArea.text=''" />
		</HBox>		

	</VBox>

</WindowedApplication>

Download SWC, example and source code from Google code:
http://code.google.com/p/contextawarenessmanager/

22
Nov

Adobe Max 2008 notes - Adobe Mobile devices future vision

I am back from Adobe Max 2008 and I am trying to digest all the information regarding Flash on Mobile devices, we can finally know the direction Adobe is taking in regards to Mobile devices.

Adobe Max 2009 screen shot

After close to a year of not disclosing much information regarding the direction of Flash Lite Kevin Lynch (Adobe CTO) announced some interesting news at Adobe Max. Let’s take a look at what’s Adobe are cooking and try to interpret the information:

  1. Andy Rubin - director of Google android acknowledge G1 supporting Flash 10, Kevin Lynch displayed an Android phone working with Flash 10 player.
  2. Kevin Lynch pulled an iPhone and advice us that Adobe are still working on getting Flash 10 on the iPhone and they need to get the approval of Apple.
  3. Future vision: Multi screen awareness and Collaboration of more devices on the same screen. For instance, user will be able to drop images, FLV from phone (using a prototype Samsung) to desktop, TV and more. Another example, multi users awareness; two mobile devices will be able to share one screen to play a game.
  4. Adobe Air on Intel or mobile internet devices.
  5. Symbian distributed with Flash 10, somewhere in the future.
  6. FL distributed player and package was released and deployed to large number of phones and if flash is missing user will be able to install the flash Lite over the wire and download the Flash application.
  7. Windows mobile devices support Flash 10.

Regarding Google android that was an obvious, since I can’t imagine Google not placing Flash on G1 and not utilizing YouTube entire library which is based on FLV, although some videos are converted to other formats (that’s how iPhone users are able to view YouTube videos) the announcement was pretty obvious to me.

A Windows mobile devices to support Flash 10 was also an obvious announcement, since earlier this year we heard Microsoft bought a license of Flash 10 it was pretty clear that we will see Flash 10 on Windows mobile devices in 2009.

I was asking some senior Adobe employees, which I cannot disclose their names, and it seems that the entire Flash Lite team was transferred to work on Flash 10. Does that mean that Flash Lite is dead? Not so fast…

Adobe are moving in the direction of building Mobile applications for Flash 10 with Flex and AIR, and we can start seeing release versions hopefully in 2009, however don’t forget that there are close to billion mobile phones support Flash Lite 1.0/2.0 and 3.0 worldwide and many devices will not be able to support Flash 10 since they are not powerful enough, so we are looking at a small percentage of devices supporting Flash 10, however no doubt that the future of Flash mobile is Flash 10 and Flash Lite will slowly fade out in the next 5 years.

Regarding Multi screen awareness and Collaboration of more devices on the same screen, Kevin Lynce pulled a Samsung UMOC mobile device supporting Air applications, the device looked capable and I am sure Samsung will be able to release a normal sized phone supporting Air somewhere in the near future.

Feel free to leave comments here regarding your take or knowledge about Adobe future vision…