09 August 2012

Stage orientation woes

When developing an air application for android and/or iOS, you somtimes want the app not to be available in a certain orientation, for example landscape only. I'm making such an app.

The problem with air is that there is no easy solution to do this. There are several issues that make life hard.
First you need to specify what you want in the air application.xml config file. If you don't need a specific orientation you set autoOrients to true and you're almost done. For a specific orientation, you need to set this value to false. 

Then you need to specify the aspect ratio, which in our case needs to be landscape instead of portrait. It should not be necessary for the auto orient case to set this value, since it will automatically adjust, but it does have an impact on the start orientation of your app, regardless of the current orientation of your device. The problem here is that the orientation of the device is 'unknown' at startup, so the stage is set to the default until the device orientation is first known. 

But when your app can deal with all orientations this is not so big a problem. If you want only landscape or portrait however, it's another story. Setting the autoOrients value to false means you have to set the stage orientation yourself. Just settings the stage orientation to the device orientation is not an option unfortunately.
There are 5 values for the orientation: default, upsidedown, left, right and unknown.

With autoOrients set to true and tracing stage and device orientation I noticed the following rule: 

if stage.deviceOrientation == left then stage.orientation == right
if stage.deviceOrientation == right then stage.orientation == left

 
This was true both on android and iOS, on an iPad2, iPad3 and a Samsung Galaxy Tablet.
Also, the 'default' orientation for iOS is portrait, while for android it is landscape. For tablets at least, it might be different for mobiles.

The device orientaion when your application starts is still unknown, so we cannot just set the correct orientation based on that.

After much testing and googling, I came up with the following code. I found a lot of other sites stating to have the solution, but they did not cover all cases I found. This code might not cover all of yours, let me know if it doesn't, I'd like to know what I can do to improve.



public class LandscapeMain extends Sprite 
{
 private var isDefaultLandscape:Boolean = true; 
 private var cycles:int = 0;
 private var CHECK_ROTATION_INTERVAL:int = 16 - 1;
 
 public function LandscapeMain():void 
 {
  var isLandscapeNow:Boolean = (stage.fullScreenWidth > stage.fullScreenHeight);
  if(isLandscapeNow && (stage.orientation == StageOrientation.ROTATED_LEFT || stage.orientation == StageOrientation.ROTATED_RIGHT)){
   isDefaultLandscape = false;
  }
  stage.addEventListener(Event.ENTER_FRAME, onEnterFrame, false, 0, true);
 }
 
 private function onEnterFrame(e:Event):void
 {
  cycles++;
  if ( (cycles & CHECK_ROTATION_INTERVAL)  == 0 && stage.deviceOrientation != StageOrientation.UNKNOWN && !isEqualOrientation(stage.orientation, stage.deviceOrientation))
  {
   var goingToDefault:Boolean = (stage.deviceOrientation == StageOrientation.DEFAULT || stage.deviceOrientation == StageOrientation.UPSIDE_DOWN);
   if ((goingToDefault && isDefaultLandscape) || (!goingToDefault && !isDefaultLandscape))
   {
    if (stage.deviceOrientation == StageOrientation.ROTATED_LEFT)
     stage.setOrientation(StageOrientation.ROTATED_RIGHT);
    else if (stage.deviceOrientation == StageOrientation.ROTATED_RIGHT)
     stage.setOrientation(StageOrientation.ROTATED_LEFT);
    else
     stage.setOrientation(stage.deviceOrientation);
   }
  }
 }
 
 private static function isEqualOrientation(a:String, b:String):Boolean
 {
  if (a == StageOrientation.ROTATED_LEFT)
  {
   if (b == StageOrientation.ROTATED_RIGHT) 
    return true;
   else
    return false;
  }
  if (a == StageOrientation.ROTATED_RIGHT)
  {
   if (b == StageOrientation.ROTATED_LEFT) 
    return true;
   else
    return false;
  }
  return a == b;
 }
}

No comments: