SoftKeyboard Hacks

Soft-keyboard Experiment – AdobeAir – Mobile from @_Wad1m on Vimeo.

Keep Soft-keyboard open

credit : Zorgonski on adobe forums
class in zip file : “KeepAlive.as”


stage.addEventListener( FocusEvent.MOUSE_FOCUS_CHANGE, function( e:* ):void
{
	if ( keyboardEnabled )
	{
		e.preventDefault();
		e.stopImmediatePropagation();
		openKeyboard();
	}
});

Possible issues ( tested on Android only ) : touching textField / stageText instance and simultaneously dragging down the notifications area. This will result the OS to hide the soft-keyboard but will be kept registered in the app as if the soft-keyboard is opened. That means that all the values such as stage.focus ( =focusTarget ) and stage.softKeyboardRect.height ( =440 ) will behave as if the keyboard is visible and active.

One possible solution is to apply a quick hack to force open the soft-keyboard every second ( or any preferred time interval )


function onKeyboardOpen( ):void 
{
	softKeyboard_is_opened = true;
}
function onEverySecond( ):void 
{
	if ( softKeyboard_is_opened ) 
	{
		softKeyboard_is_opened = false;
		return;
	}
	
	if ( softKeyboard_enabled && ! softKeyboard_is_active ) 
	{
		forceOpenSoftKeyboard( );
	}
}
function forceOpenSoftKeyboard( ):void 
{
	softKeyboard_enabled = true;
	stage.focus = my_textField; // can also be stageText instance
	softKeyboard_enabled = stage.requestSoftKeyboard();
}

Get actual soft-keyboard bounds size

ANE : com.freshplanet.KeyboardSize
class in zip file : “KeyboardSize.as”


var softKeyboard_height:Number = MeasureKeyboard.getInstance().getKeyboardHeight() as Number;
var softKeyboard_y:Number = MeasureKeyboard.getInstance().getKeyboardY() as Number;

Known issue : MeasureKeyboard ANE will return 0 height, this happens only for a short period of time, right before the soft-keyboard has been opened.

Solution : Before using the ANE make sure to load it like so


MeasureKeyboard.getInstance();

Add time delay and wait for valid output ( 120ms seems to work fine )

Capture soft-panel measurements ( native text edit popup with functions like copy/past )

class in zip file : “StageTextSPCapture.as”

Once the soft-panel becomes visible, it’s very easy to access it’s values threw the stage.softKeyboard methods. Keep in mind that this values will change once it’s hidden to the default soft-keyboard values.

A possible hack would be to capture the soft-keyboard bounds and save them.
Now to capture the soft-panel instance it’s necessary to monitor change in the stage.softKeyboard value on every frame ( or on time interval ).

stageText.addEventListener('softKeyboardDeactivate', function(e:*):void
{
	/// param 1 : soft-panel is open, param 2 : soft-keyboard is open  
	updateViewPort( false, false );
});
stageText.addEventListener('softKeyboardActivate', function(e:*):void
{
	/// param 1 : soft-panel is open, param 2 : soft-keyboard is open  
	updateViewPort( false, true  );
	
	if ( capture_Interval == -1 ) capture_Interval = setInterval( 
		
		function():void 
		{
			clearInterval( capture_Interval ); 
			
			capture_Interval = -1; 
			
			// SPCapture is a class that monitors behavior change in stage.softkeyboardBounds variable 
			updateViewPort( SPCapture.isOpened, true );
		}, 
		
	300 ); // delay interval of 300 ms
});

Additional bug:
Force opening the keyboard on every frame will result the soft-keyboard to become impossible to be closed after the app has been closed. ( You might remove focus form target on app exit and force close it, it will work in most cases )

Reading:
@YoupSolo – How to use ANE with Flashdevelop

I recommend downloading the flashdevelop project and looking threw the code, the code is a bit messy, but there are additional hacks and tricks including information in the comments.

Test project download link :
SoftKeyboard_Test.zip

Reaction Time (as3)

Code


/// @mxmlc  -o ReactionTime.swf -swf-version 15 -publisher wad1m.com
package {
	import flash.display.Sprite;
	import flash.text.Font;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import flash.utils.getTimer;
	[SWF( width = "465", height = "465", frameRate = "60" )]
	/// @author wad1m.com
	public class ReactionTime extends Sprite
	{
		// http://www.dafont.com/caviar-dreams.font
		[Embed(source='embed/CaviarDreams_Bold.ttf',fontFamily  ='CaviarDreams_Bold',fontStyle   ='normal',fontWeight  ='normal',embedAsCFF='false'
		,unicodeRange='U+0020-U+002F,U+0030-U+0039,U+003A-U+0040,U+0041-U+005A,U+005B-U+0060,U+0061-U+007A,U+007B-U+007E')]
		private static const FontClass:Class;
		
		public function ReactionTime()
		{
			var reset:Boolean = false;
			var click:Boolean = false;
			var loop:Boolean = true;
			var ms:int = 0;
			
			stage.align = "tl";
			stage.scaleMode = "noScale";
			stage.addEventListener('rightMouseDown', function(_:*):void{} );
			
			Font.registerFont( FontClass );
			
			var textFormat:TextFormat = new TextFormat();
			textFormat.size = 96;
			textFormat.align = 'center';
			textFormat.color = 0xFFFFFF;
			textFormat.font = 'CaviarDreams_Bold';
			
			var textField:TextField = new TextField();
			textField.defaultTextFormat = textFormat;
			textField.autoSize = 'left';
			textField.sharpness = -100;
			textField.antiAliasType = 'advanced';
			textField.embedFonts = true;
			textField.mouseEnabled = false;
			
			stage.addChild( textField );
			
			function alignText():void {
				
				textField.x = ( stage.stageWidth - textField.width ) >> 1;
				textField.y = ( stage.stageHeight - textField.height ) >> 1;
			};
			
			loop = true;
			color = 0x48A9F9;
			textField.text = "Ready";
			alignText();
			var start:int = getTimer();
			var kick:int = 1000 + int ( 2000 * Math.random() );
			
			addEventListener('enterFrame', function(_:*):void {
				
				if ( ! loop ) return;
				
				var time:int = getTimer();
				
				if ( time - start > kick )
				{
					color = 0x7CEA0F;
					
					click = true;
					
					ms = ( time - start - kick );
					
					textField.text = " CLICK!\n" + ms + "\n" + "ms";
					
					alignText();
				}
			});
			
			stage.addEventListener('mouseDown', function(_:*):void {
				
				if ( reset ) 
				{
					reset = false;
					click = false;
					loop = true;
					color = 0x48A9F9;
					textField.text = "Ready";
					alignText();
					start = getTimer();
					kick = 1000 + int ( 2000 * Math.random() );
				}
				else if ( click ) 
				{
					color = 0x49F8C4;
					
					textField.text = ms + " " + "ms";
					
					alignText();
					
					loop = false;
					
					reset = true;
				}
				else
				{
					kick += 1500;
				}
			});
		}
		
		private var _color:uint = 0;
		private function set color( val:uint ):void
		{
			if ( val == _color ) return;
			
			_color = val;
			graphics.clear();
			graphics.beginFill( val );
			graphics.drawRect( 0, 0, stage.stageWidth, stage.stageHeight );
			graphics.endFill();
		}
	}
}

Displaying images from embed zip file (as3)

Final result preview:
( click to randomize color and display next image )

Embed static class


package 
{
	import com.terrynoya.image.PNG.PNGDecoder;
	import deng.fzip.FZip;
	import flash.display.BitmapData;
	import flash.utils.ByteArray;
	/** @author @_wad1m  */
	
	public class Embed 
	{
		[Embed(source="../embed/backgroundsTest.zip", mimeType="application/octet-stream")]
		/// Embed file zip class
		public static var EmbedImagesZip:Class;
		/// File Zip static instance
		private static var Zip:FZip = null;
		/// bitmapData atlas, static instance storing every image within zip folder 
		private static var atlas:Vector.<BitmapData>;
		/// List of image names ( file names within zip folder )
		private static var names:Vector.<String>;
		
		/** Index is image number stored within the zip file, return image bitmapData.
		 * Note: Disposing the bitmapData will dispose the static loaded image as well and will make it unaccasable */
		public static function getImage( index:int ):BitmapData 
		{
			// validate Zip instance
			if ( Zip == null ) initZip();
			
			// Check if bitmap data was allready extracted, if true return the image data
			if ( atlas[ index ] != null ) return atlas[ index ];
			
			// else extracet bitmapdata from embed zip
			var bytes:ByteArray = Zip.getFileAt( index ).content;
			atlas[ index ] = new PNGDecoder().decode( bytes );
			return atlas[ index ];
		}
		
		/// Get the amound of images withing the zip file 
		public static function getImageCount():uint 
		{
			// validate Zip instance
			if ( Zip == null ) initZip();
			
			// assuming the zip only containes images, return image count
			return Zip.getFileCount();
		}
		
		/// Return the image name from a given index 
		public static function getImageName( index:int ):String
		{
			// validate Zip instance
			if ( Zip == null ) initZip();
			
			// check if name has been allready extracted from embed zip
			if ( names[ index ] != null ) return names[ index ]; 
			
			// else extract, save and return the image name from embed zip
			names[ index ] = Zip.getFileAt( index ).filename;
			return names[ index ];
		}
		
		/// Get image index from name, return -1 if non found 
		public static function getImageNameIndex( name:String ):int 
		{
			// validate Zip instance
			if ( Zip == null ) initZip();
			
			// simple array index search method
			return names.indexOf( name );
		}
		
		
		/// Initialise zip 
		static private function initZip():void 
		{
			// new instance 
			Zip = new FZip();
			
			// cast emed zip as byteArray and load it to the Zip variable 
			Zip.loadBytes( new EmbedImagesZip() as ByteArray );
			
			// create fixed size vectors 
			var count:int = getImageCount();
			atlas = new Vector.<BitmapData>( count, true );
			names = new Vector.<String>( count, true );
		}
	}

}

Example


			
// read image at index [0] from embed zip
var target:BitmapData = Embed.getImage( 0 ); 
			

Download FlashDevelop Project
Link: DisplayImagesFromZip.zip

Textures:
http://www.transparenttextures.com/

Reference:
http://www.bytearray.org/?p=1089
http://codeazur.com.br/lab/fzip/docs/
https://github.com/claus/fzip/
https://github.com/terrynoya/ASImageLib
http://stackoverflow.com/questions/1053546/howto-embed-images-in-actionscript-3-flex-3-the-right-way