“You uploaded an APK with Invalid or Missing Signing Information for Some of Its files” Google Play, Adobe Air Error

Getting

“You uploaded an APK with invalid or missing signing information for some of its files. You need to create a valid signed APK”

while trying to publish your Adobe Air Android app to Google Play?

It’s due to a bug in Air 3.6 (and 3.7 for me). For future reference, you can find out what the error in the signing of an app are by using a tool called jarsigner. This exists in your JDK.

On windows it’s in your JDK path/bin folder. Just open up command prompt/terminal and run:

jarsigner -verify "[PATH TO YOUR APK]"

Here is the error I was getting

jarsigner: java.lang.SecurityException: SHA1 digest error for res/drawable-xhdpi
/icon.png

Here’s how I fixed it. I opened up the APK in 7zip (remember, APK is collection of files zipped together. You can actually open it up) and navigated to that res/rawable-xhdpi folder. What do you know… looks like there are TWO icon.pngs. It’s a bug in Adobe’s latest Air which packages the APK incorrectly by creating duplicated icon.png files. Probably will be fixed in later versions.

If you want to avoid upgrading your Air SDK, all you need to do is delete one of those two icon.png files from the zip file.

Command line way to do it (will delete both):
zip -d [YOUR APK.APK] res/drawable-xhdpi/icon.png

Now, if you tried uploading this APK you might get an error about the APK not being zipaligned. Due to us deleting files the APK is no longer zip aligned. Which means we have to re align the file.

Navigate to your Android Developer Tools (ADT) folder. Under the SDK’s /tools you’ll see that zipalign. You’ll need to do

zipalign -f -v -c 4 “Your APK” “Your APK 2″

Reupload the new APK (remember if you don’t put an absolute path it’ll be in the same directory as where zipalign is) and it should be accepted.

Oh the joys of Adobe Air for mobile development.

Simple Circle Packing

I am working on a new game which is was in need of an algorithm to pack characters as close together without touching. Here is a demo of my algorithm:

Get Adobe Flash player

And the code (uses TweenLite)

import flash.display.MovieClip;
import flash.geom.Point;
import flash.events.Event;
import flash.utils.Timer;
import flash.events.TimerEvent;
import com.greensock.TweenLite;

var mcs:Array = [];
var radius:Number = 5;
var c:Point = new Point(stage.stageWidth / 2,stage.stageHeight / 2);
var animationTime:Number = .5;

function addMC(e:Event= null)
{
	var mc:MovieClip = new MovieClip();
	mc.cacheAsBitmap=true;
	mc.graphics.lineStyle(1);
	mc.graphics.beginFill(Math.random()*0xFFFFFF);
	mc.graphics.drawCircle(0,0,radius);
	mc.graphics.endFill();
	mc.x = c.x;
	mc.y = c.y;
	addChild(mc);
	mcs.push(mc);
}
var t:Timer = new Timer(animationTime*1000,1500);
t.addEventListener(TimerEvent.TIMER,function(e:Event):void{
   addMC();
   arrange();
   });
addMC();
arrange();
t.start()
function arrange()
{
	var curDistanceAwayFromCenter:Number = radius * 2;
	var numMoved = 0;
	var n:Number = mcs.length;
	// Place the very first circle in the center, as the algorithm won't work for n==1
	mcs[0].x = c.x;
	mcs[0].y = c.y;
	numMoved++;
	while (numMoved < n)
	{
		/*R*SIN(180/n) = r
		SIN(180/n) = r/R;
		ARCSIN(r/R) = 180/n
		n = 180/ARCSIN(r/R)
		*/
		var numberToFit:int = Math.PI/Math.asin(radius/curDistanceAwayFromCenter);
		if (numberToFit > mcs.length-numMoved)
		{
			numberToFit = mcs.length - numMoved;
		}
		for (var j:int = 0; j < numberToFit; j++)
		{
			var cur:MovieClip = mcs[numMoved];
			// ang to center
			var ang:Number = Math.PI * 2 * j / numberToFit;
			var np:Point = new Point();
			np.x = c.x + Math.cos(ang) * curDistanceAwayFromCenter;
			np.y = c.y + Math.sin(ang) * curDistanceAwayFromCenter;
			// this if improves performance a tiny bit, would probably be way more performant if we detected if this was in the "last" circle, if not then we don't need to loop through it anymore
			if (np.x !== cur.x || np.y !== cur.y)
				TweenLite.to(cur,animationTime,{x:np.x,y:np.y})
			numMoved++;
		}
		curDistanceAwayFromCenter +=  radius * 2;

	}
}

Note that this doesn’t pack circles optimally in a space. That is a much harder problem. I was looking for a solution that would “cluster” the characters.

Annoying to Very Annoying Newsletter Practices

I really… really hate newsletters from websites I don’t want them from. What I hate even more is the unsubscribing process that websites employ. It’s like cancelling a gym membership.

Annoying Newsletter Practices

 

My flow of frustration in the unsubscribing process. Some websites employ a Hotel California approach in newsletters – you can check out any time you’d like but you can never leave.

Is it unreasonable for me to demand that I shouldn’t have to log in to a website to unsubscribe from their email list?

Custom Options Set Price to 0 in Magento 1.7 – The Fix

I’m working on a side project with Magento… There is a bug in 1.7 where when you use custom options, and your theme doesn’t include it’s own options.phtml file, the price will set to $0 (zero) when a user selects the price.

A bunch of forum posts have people talking about the problem. Basically it’s a silly bug in the javascript in options.phtml.

Basically, if your theme doesn’t have that options file in it’s theme directory, then magento looks like it defaults to the base‘s folder and includes the “default” options.phtml.

Here is the fix. I hope Magento includes it in the next Magento release!

Line 123 of options.phtml in
app/design/frontend/base/default/template/catalog/product/view/

Right now is

price += parseFloat(config[optionId][element.getValue()]);

Should be

price += parseFloat(config[optionId][element.getValue()].price);

Basically the code was trying to convert a javascript Object to a float… making the price 0.

Please share this page to others who experience the same issue.

Camera quality on iOS for Air

Having quality issues? Getting different dimensions from the camera than what you request? Read this note from Adobe:

https://bugbase.adobe.com/index.cfm?event=bug&id=2942275

cam = Camera.getCamera();
cam.setMode(320, 240, 20, false);
cam.setQuality(0, 100);
cameraVideo.attachCamera(cam);
cameraVideo.width = cam.width;
cameraVideo.height = cam.height;


//Actual Result:

cameraVideo's dimension are 320x240 when on IPhone emulator
cameraVideo's dimension are 192x144 when on IPhone

//Expected Result:
cameraVideo's dimension are 320x240 when on IPhone emulator
cameraVideo's dimension are 320x240 when on IPhone

This was Adobe’s response

When you call Setmode with some requested width and height, its not necessary that you will get the requested width and height.
The returned width and height depend on a number of factors –
1. Obviously, the values you passed in as width and height.
2. If you passed favorArea=true/false in the setmode API
3. And most importantly, the camera hardware – what are the possible resolutions (and fps) the hardware of camera on you device supports.

Since “3″ can be different for different devices you can get different values on different devices (values can vary on WIN Desktop as well with different webcams)

In this case, iOS devices again support different set of possoble resolutions –
ios Camera Presets AVCaptureSessionPresetLow AVCaptureSessionPresetMedium AVCaptureSessionPreset640x40 AVCaptureSessionPreset1280x720 AVCaptureSessionPresetHigh
3G 400×304 400×304 NA NA 400×304
3GS 192×144 480×360 640×480 NA 640×480
4 front 192×144 480×360 640×480 NA 640×480
4 back 192×144 480×360 640×480 1280×720 1280×720

The logic to compute the width and height is somewhat like this (and happens in core, same for all platforms) –

1. core asks platform to provide a list of supported resolutions. (In ios we provide the list as mentioned above , and in the spec)
2. core selects one of these resolutions, the one closest to user’s requirement (basically comparing area using percentatges but is more involved to describe in words).
3. Say user asks for 320 x 240
4. core has now to select the native supported mode closest to this. SO it can either choose 192×144 or 480×360. Seems in this case core decides to use 192×144.
5. core also changes the resolution based on the aspect ratio of the requested resolution (doing any required cropping).
6. So, you will be getting 192 x 144.

Similarly when you choose 384 x 288, core selects 480 x 360 to be the closest native resolution. Camera captures at 480 x 360 and then a cropped part of it (384 x 288) is provided to you.

Also, on Android, you get the desired values because 320 x 240 is one of the supported values by most Android cameras, thus the requested and supported better match in case of Android.
Also as I have genrally observed, Android cameras have a large number of supported resolutions compared to just 4 on iOS. Thus Android camera has a better chance to match the requested resolution. But if you ask for weird numbers like 700 x 400, you will not get that on Android too (but something else based on the above described logic)

Also the same information is mentioned in implementation spec (and comments on the spec) – https://zerowing.corp.adobe.com/display/airlinux/Mobile+Camera+Implementation+spec

See my suggestion here:

https://bugbase.adobe.com/index.cfm?event=bug&id=2953037

Might shed some light on why your video from webcam is so bad. Try increase the setMode values and get the highest quality camera quality returned. Be mindful that the resolution may be different!

Auto create polygon AS3

Made a Flash on Wonderfl the other day.

My friend studying for his PhD at Georgia Tech in architecture called me regarding a problem. He said,

“I need to program something to create a polygon from points. Right now, the points are not being drawn in the right order so lines are being overlapped. How do I have the program draw them in the right order?”

So I came up with a theory of how to do it. The idea is that you find the average point between the points, and from that point you find the arc tangent to each of the points and order the points based on their angle to the center. This idea would only work in 2D, but I’m sure there is a general way to do it for all dimensions.

Here is the code written in actionscript. You can click to add a point and ctrl+click to delete a point. I put it on wonderfl because I am thinking some people could fork some more interesting things from it.

Auto create Polygon – wonderfl build flash online

Superfast Base64 Actionscript Library

So I was using this Base64 Actionscript class for Base64 encoding/decoding a byte array while working on my cofounded project 15Seconds.me. The Base64 class is by Jean-Philippe Auclair, and according to his own benchmarks, runs about 700% faster than Adobe’s own Base64. He has a nice explanation of how he made some improvements, and considering it was high up on Google’s search results for Base64, I started using it.

That is until I came across another Base64 Actionscript library over at Valentin Simonov’s blog.

BlooDHounD’s Crypt class turns out to be even faster than Jean’s class – about 17 times faster. The code for BlooDHounD’s class is unavailable (only a SWC is given). I imagine the library was written in C/C++ and exported to a SWC using Adobe Alchemy considering the speed improvements (alchemy applications can run way faster than AS3 native code).

JavaScript Function declaration cautions!

Just found Kontagent‘s JavaScript library has a issue. When you declare functions in JavaScript as so below, the behavior isn’t as expected!

a = 5;

if (a == 5)
{
    function yo()
    {
        alert("good");
    }
}
else if (a == 6)
{
    function yo()
    {
        alert("bad");
    }
}

yo();

Be sure to declare your dynamic functions like so:

a = 5;
var yo;
if (a == 5)
{
    yo = function()
    {
        alert("good");
    }
}
else if (a == 6)
{
    yo = function()
    {
        alert("bad");
    }
}
yo();

As in Actionscript (also ECMAScript based), declaring functions the first way will be global scope. And the last function defined will be the one that is used.

We were trying to debug why Kontagent‘s library wasn’t pinging the right events (in our case Invite Sent notifications). When we investigated the code we saw they wrapped function calls inside ifs as above… Hopefully they will read this blog post and implement a fix as suggested :-) .
So be careful developers!

*Edit: We sent a Pull request to their github with our fix

LocalConnection Bug in Flash Player

So I’ve had this problem for ages, and couldn’t find anyone online writing about it.

LocalConnection in AS3 is used to send messages between Flash SWFs in a browser window. It’s also used as a hack to invoke garbage collection in the Flash Player.

I’ve been using it in some recently applications to ensure only one instance of a SWF is loaded at a time on a computer and to display a message to a user if a window with the application is already open.

It’s usually be pretty straight forward.

var LocalConn:LocalConnection = new LocalConnection();

Declares it
then

			try {
				LocalConn.connect("_App");
			} catch(error : ArgumentError) {
trace("Uh oh... Looks like it's already running");
				return;
			}

Only one connection string can be used at a time, and if another SWF in the another browser window (or another tab… or another browser for that matter) tries to connect that ArgumentError will be thrown.

Unfortunately, there is a annoying bug where sometimes, even if no other instances are open, the LocalConnection thinks someone is still running on the same connection string…

I’ve experienced it happening when simply testing in Flash Builder and FDT… Sometimes I am working on another part of my application when one test the application says it’s already running… I’m confused at this point because I know nothing else is running…

I think I’ve narrowed down WHY it happens. It seems to happen when Flash is closed unexpectedly. For example, on Google Chrome, if you run the SWF and then for some reason Flash crashes (you know… the “uh oh Flash crashed” bar that appears at the top of Chrome), it seems that the LocalConnection is never officially closed.

What sucks is that you either have to change the connection string or reboot your computer (restarting browsers don’t work).

If anyone else has experienced this or knows any fixes, please let me know in the comments. I’ve been experiencing this problem since at least Flash Player 9. I’ve reported it on Adobe’s ticketing system… unfortunately they don’t allow anyone to read the ticket because they restricted viewing deeming it a possible “security” flaw – but maybe if other people are experiencing the issue they will put in a fix for it.

Update 5/10/2011
Qingyan Zhu from Adobe replied to my bug report:

Hi Danny,

Thanks for clarifying the issue.

But this is actually as designed.

So when there’s only one browser, if it crashes, then the segment gets reset, and the dangling LC endpoints get cleared.

But if there are two browser processes (depending, say on how IE is launched), or two processes that use LocalConnection (including any AIR app, Flash Projector, AIM, Y!IM, etc etc), then the corrupt memory segment will stick around.

–Qingyan