Screen Capture with AIR 2 NativeProcess

Prior to AIR 2 the only way to capture the screen was to use something like Marapi Java Bridge that acts as a conduit between your application and the native system.

However, with AIR 2 Beta and the upcoming AIR 2 release, you can now use the command-line tool “screencapture” that comes with Mac OS. Nothing extra needs to be bundled with the application for this to work on a Mac, but on Windows, you need an additional piece of code to execute the screen capture process.

The process of doing a screen capture is pretty easy. You first create a File object that points to the location of the “screencapture” command line on the Mac:

var file:File = File.applicationDirectory;
file = file.resolvePath("usr/sbin/screencapture");

Then create a NativeProcessStartupInfo object that will be used when we start the NativeProcess.The arguments property of the NativeProcessStartupInfo takes a list of arguments that will be passed to the command line tool when its started. For a complete list of arguments used by the screencapture command line tool, type “screencapture –help” in the terminal window. For this example I wanted to push out a file named “screencap.png” to the desktop from a selection of the screen. Optionally, you could capture the image to the clipboard and paste it directly.

var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
var args:Vector. = new Vector.();
args[0] = "-i";
args[1] = "screencapture.png";

nativeProcessStartupInfo.arguments = args;
nativeProcessStartupInfo.executable = file;

Now we can start the native process:

process = new NativeProcess();
process.start(nativeProcessStartupInfo);

The screencapture command tool will be launched and you will see the selection cursor on your screen. After you make a selection, the file will automatically be saved to the desktop as “screencapture.png”.

Here is the complete project code:

<?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"
                       creationComplete="init()">
    <fx:Script>
        <![CDATA[
            private var process:NativeProcess;
           
            private function init():void
            {
                launchNativeProcess();
            }
           
            private function launchNativeProcess():void
            {
                if(NativeProcess.isSupported) {
                   
                    var file:File = File.applicationDirectory;
                   
                    if (Capabilities.os.toLowerCase().indexOf("win") > -1) {
                        //file = file.resolvePath("bin/mylocalexe.exe");
                    } else if (Capabilities.os.toLowerCase().indexOf("mac") > -1) {
                        file = file.resolvePath("/usr/sbin/screencapture");
                    }
                   
                    var args:Vector.<String> = new Vector.<String>();
                        args[0] = "-i";
                        args[1] = "screencapture.png";
                   
                    var nativeProcessStartupInfo:NativeProcessStartupInfo = new NativeProcessStartupInfo();
                        nativeProcessStartupInfo.arguments = args;
                        nativeProcessStartupInfo.executable = file;
                        nativeProcessStartupInfo.workingDirectory = File.desktopDirectory;
                   
                    process = new NativeProcess();
                    process.start(nativeProcessStartupInfo);
                   
                } else {
                    trace("Native Process not supported");
                }
            }
        ]]>
    </fx:Script>
   
</s:WindowedApplication>

Don’t forget to include support for extended desktop in the application xml file:

<supportedProfiles>extendedDesktop</supportedProfiles>

To get something like this to work on Windows, you need to know a little bit of C or C# so you can call your own service and launch the screen print functionality on Windows. For more about using the NativeProcess in AIR 2 for both Windows and Mac, you check out this Adobe article.

-Mr

ImageCache, a cheap way to cache images in Adobe Flex

In a previous post about Ely’s SuperImage, I mentioned that we decided to implement a simpler method for caching images within a Flex application. ImageCache is a simple class file that extends the Image control by adding the ability for the control to cache bitmap data. Unlike SuperImage, ImacheCache can handled SWF files, it just lacks all the bells and whistles of SuperImage, though image flicker is eliminated.

Using ImageCache

ImacheCache is just two class files. ImageCache.as which extends the Image control and ImageCachUtility.as which is a Singleton class that used by Imagecache to store and retrieve the BitmapData for cached images. Bitmap data is stored in a regular ArrayCollection object and retrieved by using the full path to the image (because image names can be duplicates but paths should be unique). Once the amount of cached images reaches the cache limit, the controls works on first in first out, dropping off older cached images as it caches new ones and staying within the cache limit. SWF fiels are not actually cached and will load as normal. You just use ImageCache like you would the Image control within your project:


<controls:ImageCache cacheLimit="200" id="image" source="{data.url}" width="100" height="100" complete="imageComplete()" ioError="imageError()" xmlns:controls="com.thanksmister.controls.*"/>

Cache Limit

ImageCache has a property called “cacheLimit” which tells the control how many images to cache. Because caching images can reduce the performance of your application. The larger the cache limit value, the more memory your application will use because it is storing all the Bitmap data in memory. If you want to reset the image cache, create an instance of the ImageCacheUtility and call the method “clear()”. ImageCache has been used and tested extensively for large media sites. Below is a screen shot of images from Flickr, the list on the left uses the standard Image control, the list on the right uses the ImageCache control. It seems that there may be some security issue when loading Flickr images without doing a proxying the images. Run the example locally or proxy Flickr images for best results.

Example

Code

You can download the code for the above project here. You will need your own Flickr API key to make the application work.

-Mister

Scrolling List Control with Tweener

I was on a simple little side task last week, to create a scrolling effect using the Flex control and also remove the default scroll bars. I didn’t want to do the usual trick, scrolling a HBox with items added to it using a repeater, or whatever. I wanted to animate the scrolling that occurs within an actual List control but give it a little easing effect. The call went out to Twitter for suggestions, and within minutes Jesse Warden responded with with “use the tween class dude” and also my friend Jerry Don reminded me of a new smooth scrolling List created by Alex Harui. This helped to point me in the right direction.

Scrollbars and Mouse Wheel

The first objective was to remove the default List scroll bar because I wanted to use buttons to scroll the list from the top to bottom and vice versa with just one click of a button. Ok, no problem, verticalScrollPolicy=”off” just hides the scroll bar, however it also removes the ability to scroll the list with the mouse wheel. Now I had to hack into the List control to add this functionality back. This part was not too hard, I found a good code example, though it was in German, and repurposed it for my needs.

 /**
 * Override the mouseWheelHandler so if the verticalScrollPolicy is "off"
 * we use the MouseEvent.delta value to manually update the List control
 * scroll position when there is no ScrollBar.
 *
 * */
 override protected function mouseWheelHandler(event:MouseEvent):void
 {
 if(verticalScrollPolicy == ScrollPolicy.OFF && maxVerticalScrollPosition != 0){
 var len:Number = dataProvider.length;
 var delta:Number = Number(event.delta);
 var pos:Number = verticalScrollPosition;
 var newpos:Number = verticalScrollPosition;

if(delta = maxVerticalScrollPosition) {
 newpos = maxVerticalScrollPosition;
 } else {
 newpos = pos + Math.abs(delta);
 }
 } else {
 if( ((pos - delta) <= 0 ) || pos == 0){
 newpos = 0;
 } else {
 newpos = pos - delta;
 }
 }

verticalScrollPosition = newpos;
 }

super.mouseWheelHandler(event);
 }
 

Tween List

I then through on some buttons that would scroll the list from the current position to either the start or the end. Now began the work with to add the easing function so the list would scroll fast at the start of the scroll and then slow down just before reaching the end or beginning of the list, depending on which way you want to scroll. I started with the built in mx.effects.Tween class which really proved to be the least effective. One problem with the List control is that its not incremental, it scrolls by row, making for a choppy scroll experience. So instead of being able to scroll in smaller increments, you have to scroll by whole numbers, which doesn’t allow enough runway to make the list easing effect noticeable. Using the regular Tween and a mx.effects.easing.Circle effect might look something like this:

 private function tweenDown():void
 {
 var pos:Number = list.verticalScrollPosition;
 var end:Number = list.maxVerticalScrollPosition;
 var tween:Tween = new Tween(this, pos, end, 1400, 10, tweenUpdateHandler, tweenEndHandler);
 tween.easingFunction = Circular.easeOut;
 }

private function tweenUpdateHandler(value:String):void
 {
 list.verticalScrollPosition = Math.round(Number(value));
 }

private function tweenEndHandler(value:String):void
 {
 trace('tween end value: ' + value);
 }
 

Notice that you need to round the value returned in the tweenUpdateFunction, if you don’t, then your list will get hosed because the list can’t handle values like

tween value: 0.03486542280255378
tween value: 0.01960467102681207
tween value: 0.01022360862317484
tween value: 0.0032527319110684516
tween value: 0.0001680006720050642

Smooth Scrolling List

This is where I thought Alex’s smooth scrolling list would come into play because it allows for or smooth scrolling by increments. In theory, you shouldn’t have to round up the number, you should be able to apply the values directly to the verticalScrollPosition property of the list. Alex’s prototype is suppose to allow this, which it does to some extent. The prototype list can handle it when you scroll down, but not back up to the top, it breaks down. So this doesn’t really solve the problem of smoothly scrolling the list when updating the verticalScrollPosition with a Tween effect. However, his list does have some value as it allows for smooth scrolling when you use the mouse wheel.

Example

Below is the example and code. Don’t expect the mouse wheel to work if you are on Mac OS X, because its not supported in the Flash Player. If you are interested in a work around for the mouse wheel issue, check out this the piexelbreaker.com post for a workaround. Of course the mouse wheel works fine in Adobe AIR. Also, the longer the list of data, the better the animation effect because there is more data to show the full effect. The effect could still be improved, but until Adobe releases a new set of List components that scroll properly, this is as good as it may get. Let me know if anyone has any other suggestions.

Download the ScrollTweenEffect.zip file for the source code.

UPDATE

A friend suggested I look at the Flex mx.effects.AnimateProperty to animate the scrollbar for the List. He pointed to me to a blog post by Thomas Decaux. I will try to work up an example using his approach to compare the two.

- Mister

SuperImage Redux, caching images in Flex List

I have been meaning for so long to post this code. Some time ago I had the pleasure of working with John Yanarella from Universal Mind. John was helping my employer at the time to put together an application that allows users to upload, manage, and share media assets. We needed an efficient way to cache and display images to optimize the performance of loading and viewing a large number of thumbnails.

SuperImage

Last year Ely Greenfield posted on his blog QuietlyScheming a way to end images from flickering when you display them in a Flex List control. Ely created a new component called SuperImage that replaces the Flex Image control. SuperImage fixes a few issues with the current Image control layout in addition to adding the ability to cache loaded images to stop them from constantly reloading (causing the flicker when scrolling a list).

SuperImage Update

In our project we wanted use SuperImage, but what we wanted was for SuperImage to behave more like the Flex Image control. Specifically we wanted the control to broadcast the same standard events as the Image control; ioError, securityError, imageComplete, progress, completem, completeEffect. The new SuperImage also implements IDataRenderer, IDropInListItemRenderer, and IListItemRenderer interfaces. John Yanarella did a great job cleaning up the SuperImage control and add the missing functionality. We ended up not using the SuperImage and instead used a simpler implementation for caching the Image control.

Example

Below is an example similar to Ely’s SuperImage that demonstrates the problem (on the left) with the Image control and the fix (on right) using the updated SuperImage. This example also shows a text dump of events broadcast from the new SuperImage control.

Limitations

One thing the updated SuperImage still lacks is the ability to display loaded SWF files. Since our company never used the code in any project and it was based on Ely’s code, I thought it only fair to repost the update and the code for everyone to use as needed. If anyone has an further updates to the code or suggestions to fix the SWF loading issue, please post them here and I will continue to update the component.

Code

You can download the code for the above project here. You will need your own Flickr API key to make the application work. Be sure to also read Ely’s original post to see the other benefits of SuperImage. Many thanks to John Yanarella who actually did the coding on the project, I finally posted it as I promised him many months ago :) .

-Mister

Custom RichTextEditor

One of the longest running post on my blog has been about creating XHTML output from the Flex RichTextEditor control. I always thought it would would be nice if someone took all the comments and suggestions for that post and created a customized control that outputs proper XTHML. Well, Axel Jenson has created such a custom control. I want to send many thanks to all those who posted over the past year and to Axel for stepping up and creating this custom control for Flex. Give the component a try and let Axel know what you think.

-Mr

Error #2025 – Clash between Flex 2 & Flash CS3

I got my new copy of Flash CS3 and proceeded to make a new preloader for my Flex application.  I made a document Class for my CS3 file and attached a CS3 ProgressBar component.  I had a public function in the Document Class for setting the progress of the Flex preloading.   I dropped the SWF file into my Flex document and set up all the preloading code.  If you set a preloader in Flex it will load that file in the first frame before it loads the rest of the Flex application.  You can do progressive preloading the same way you would in Flash.   The CS3 preloader worked like a charm because with CS3, I can now (though still not elegantly) talk to the functions inside the loaded SWF file.   Here is the CS3 Document Class code:

package com.preloader
{
	import flash.display.MovieClip;
	import fl.controls.ProgressBar;
	import fl.controls.ProgressBarMode;

	public class WelcomeScreenClass extends flash.display.MovieClip
       {
		private var progressBar:ProgressBar;

	        public function WelcomeScreenClass()
               {
			progressBar = new ProgressBar();
			progressBar.indeterminate = false;
			progressBar.mode = ProgressBarMode.MANUAL;
			progressBar.setSize(150, 22);
			progressBar.move(10, 50);
			progressBar.setStyle("barPadding", 3);
			progressBar.setProgress(50, 100);

			addChild(progressBar);
	        }

		public function setLoaderProgress(loaded:Number, total:Number):void
		{
			progressBar.setProgress(loaded, total);
			trace("loaded: " + loaded + " of total: " + total);
		}
	}
}

This all sounds good so far, a decent marriage between Flash and Flex, weeee!!   About a week later I noticed a bug in my Flex application.  Every time you hit the Tab key on any dialog created with the PopUpManager or even the Alert, I would get the following error code:

ArgumentError: Error #2025: The supplied DisplayObject must be a child of the caller.
 at flash.display::DisplayObjectContainer/getChildIndex()
 at mx.core::Container/getChildIndex()
 at mx.containers::Panel/getChildIndex()
 at fl.managers::FocusManager/::getChildIndex()
 at fl.managers::FocusManager/::sortByDepth()
 at fl.managers::FocusManager/::sortByTabIndex()
 at Array$/Array::_sort()
 at Array/http://adobe.com/AS3/2006/builtin::sort()
 at fl.managers::FocusManager/::sortFocusableObjectsTabIndex()
 at fl.managers::FocusManager/::sortFocusableObjects()
 at fl.managers::FocusManager/::keyDownHandler()

This particular bug is very vague and a search of the Internet yielded nothing that seem to fit my situation.  I am using Cairngorm with Modules and my suspicion was that I had created a shared code typology.   Modules are cool because they can reduce your initial application load, but they are a real pain in the ass to manage because you run into these strange bugs that always seem related to a race condition with loading one Class or Manager into one module.    I spent a few days tracking down this issue, testing each module, commenting out code.  I finally created a new project without any modules, determined that it was the shared code typology issue.  I still had the same issue.

Something about the error dawned on me, the “fl.managers”.   This is a CS3 code package that Flex does not have.   I then decided to remove my CS3 prelaoder, and guess what, the bug went away.  I had inadvertently created my own bug by using CS3 with the new ProgressBar component.  This component most likely extends UIComponent, which contains the FocusManager, and therefore, causes the issue with the FocusManager in Flex.  According to shared code typology 101, the first instance of the loaded manager wins.

The solution was to yank out the CS3 ProgressBar component and just create my own progress bar using the Flash drawing tools.   This was a tough error, and probably not the first one that will be encountered as Flash and Flex start mingling more in the near future.  

-mr

Flex List Control with Editable InputText ItemRenderer

I think the title of this post says it all.   I needed a InputText for the label of images in my TitleList control.  I wanted users to click on the text and edit it in place.  I started out creating a simple ItemRenderer with a InputText and skinned the InputText to look like a regular label until you click to edit the information.   I then decided to go a little further and make a custom control based on InputText which did all the style changes and kept broadcast a change event only when the text actually changes.   I also removed the extension from any titles (like myimage.jpg becomes myimage instead).

This was all fine and dandy until I used the component within the TileList.   You see, the List controls allow Alphanumeric keys as a form of navigation.  This is really cool because if you have images and titles below them, you can skip from image to image by typing the first letter of the image title.  However, this conflicted with the InputText.  Each time I clicked on a title to edit and started to type, the TitleList would navigate based on the character I typed.   This was not so cool. 

I ended up with a simple solution:

event.stopImmediatePropagation();

I used stopImmediatePropogation to prevent my keyDown events on the InputText from bubbling up to the List control.  Problem solved!  Here is a working example, right-click on the Flex application to get the code (I can’t tell you how many people post messages about how to get the Flex source code from examples, I mean, if you have to ask….).

 

ItemRendererEditor Example and Source

 

- Mister