How can I properly control stage orientation on Android with AIR mobile?

bvaughn picture bvaughn · Sep 3, 2012 · Viewed 7.1k times · Source

I have an AIR application that I wish to lock into landscape orientation. I never want the application to rotate to portrait mode. Adobe's solution to this problem, seemingly, would be to configure my *-app.xml file as follows:

<!-- The initial aspect ratio of the app when launched (either "portrait" or "landscape"). Optional. Mobile only. Default is the natural orientation of the device -->
<aspectRatio>landscape</aspectRatio>
<!-- Whether the app will begin auto-orienting on launch. Optional. Mobile only. Default false -->
<autoOrients>false</autoOrients>

This works most of the time- but not always. For instance, in certain flavors of Android (example: 2.x, running on a Nook Tablet) if my device's screen turns off while Flash Builder is packaging and deploying my APK, the app starts in a semi-broken state where its aspect ratio is landscape but everything has been measured with portrait dimensions. On other devices, like my Google Nexus (Android 4.1), the application launches correctly (in landscape mode) but if I turn off the screen, rotate the device to portrait mode, and turn the screen back on- my AIR app has been invisibly rotated into portrait mode again. What's worse, no StageOrientationEvent.ORIENTATION_CHANGE events even get dispatched to indicate that this has happened.

I've tried a variant of the above approach, which is to programmatically set the device orientation:

private function onAddedToStage( event:Event ):void {
    stage.autoOrients = false;
    stage.setOrientation( StageOrientation.ROTATED_RIGHT );
}

This approach has the same problems mentioned above.

I've also tried the approach mentioned in this other Stack Overflow thread. Instead of disabling auto-orient, I've tried listening to StageOrientationEvent.ORIENTATION_CHANGE and preventing the device from entering portrait mode:

private function onAddedToStage( event:Event ):void {
    stage.addEventListener( StageOrientationEvent.ORIENTATION_CHANGE, onStageOrientationChange, true, int.MAX_VALUE );
}

private function onStageOrientationChange( event:StageOrientationEvent ):void {
    switch( event.afterOrientation ) {
        case StageOrientation.DEFAULT:
        case StageOrientation.UPSIDE_DOWN:
        case StageOrientation.UNKNOWN:
            event.preventDefault();
            break;
        case StageOrientation.ROTATED_RIGHT:
        case StageOrientation.ROTATED_LEFT:
            break;
    }
}

This, too, seems to work only some of the time. In many cases, event.preventDefault() has no effect and the application rotates anyway.

So... my question to other AIR developers out there is: does anyone know of a way to reliably lock a device into a single orientation?

Answer

bvaughn picture bvaughn · Sep 5, 2012

Here's the approach I've found to work for me, on Android 2.3 (Fire and Nook) as well as 4.1 (Nexus 7). I haven't tested with more devices yet but so far it seems promising.

First, I've set my -app.xml configuration as follows:

<aspectRatio>landscape</aspectRatio>
<autoOrients>false</autoOrients>

Then I've added the following code to my spark Application:

private function onAddedToStage():void {
    stage.addEventListener( Event.RESIZE, onStageResize );
    NativeApplication.nativeApplication.addEventListener( Event.ACTIVATE, onNativeApplicationActivate );
}

private function onStageResize( event:Event ):void {
    checkForOrientationChange();
}

private function onNativeApplicationActivate( event:Event ):void {
    checkForOrientationChange();
}

private function checkForOrientationChange():void {
    if ( height > width ) {
        if ( stage ) {
            stage.setOrientation( StageOrientation.ROTATED_RIGHT );
        } else {
            // The first ACTIVATE event occurs before the Application has been added to the stage
            callLater( checkForOrientationChange );
        }
    }
}