Monday, August 8, 2011

Orientation for both phones and tablets

Tablets are now a new component to take into account when programming applications on Android.
And it brings issues with your application orientation...

My game Word Prospector was meant to be played in portrait mode on a phone.
But on tablets, I want it to be played in landscape ( as it feels more natural for tablets ).

But how to achieve that ?

Activity orientation is fixed in the manifest, and you can't have different manifests for different screen configurations.

So how to manage that ?
I found three different ways...

First solution : different apk for different screen configurations

Now that the Android market gives us support for multiple apk, ( see here ), you can use it just to have some differences in the manifest.
So you can have the very same application and only have different orientations in the manifest, and let the market make the work for you.

But having several apk is painful for developers : it's a lot of work to develop, and to maintain.
So I would  not recommend this solution.

Second Solution : have some activity code to manage the orientation

You can impose the orientation of your activity in the onCreate method of the activity, with the setRequestedOrientation( ActivityInfo.SCREEN_ORIENTATION_XXX ) function.

So you can have some code like this :
if ( isXLargeScreen() )
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
else
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

With this code, when there is an orientation change, the activity is destroyed, then re created.
To avoid this destruction / recreation step, that would break the smoothness of the user experience, you have to disconnect the destruction of the activity mechanism.
So you have to add a
android:configChanges="keyboardHidden|orientation"
parameter in the activity declaration in the manifest, and add an empty onConfigurationChanged method.

Now you can even be more flexible.
If you want one configuration ( for instance the tablet one ) to handle both orientation, and the other ( the smartphone ) to handle only one orientation, you can do it :

In the OnCreate method, impose the orientation only for the smartphone :
if ( ! isXLargeScreen() )
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

Then in the onConfigurationChanged, you have to recreate the Activity only when on tablet. And in this case, you have to save the activty state, and restore it, as the classic destruction / creation of the activity is deactivated :

    public void onConfigurationChanged (Configuration newConfig)
    {
        //Log.d("tag", "config changed");
        super.onConfigurationChanged(newConfig);

        if ( ! isXLargeScreen() )
            return;
      
        // CODE FOR ROTATION
        // FOR BIGSCREENS :
        SaveActivityState();
        setContentView( R.layout.MyActivity );
        RestoreActivityState();
    }

Third solution : let's get back to the manifest...

The manifest really is the tool Android developers gave us to handle the orientation...
So my last ( and prefered ) solution is to have two different activities, with different orientation capacities :

        <activity     android:name="LetterGame"
                    android:screenOrientation="portrait"
                    android:configChanges="keyboardHidden|orientation"
                      android:label="@string/app_name">
        </activity>
        <activity     android:name="LetterGameLandscape"
                    android:configChanges="keyboardHidden|orientation"
                    android:screenOrientation="landscape"
                      android:label="@string/app_name">
        </activity>


and to create a MyActivityLandscape class, that just do ... nothing !
It just inherits from MyActivity :

package com.alocaly.LetterGame;

public class MyActivityLandscape extends MyActivity
{
}

Then you just have to choose what activity to launch in the caller activity :

Intent i;
if ( isXLargeScreen() )
    i = new Intent(MainMenu.this, MyActivityLandscape.class);
else
    i = new Intent(MainMenu.this, MyActivity.class);
startActivity( i );

And that's it !!
The onConfigurationChanged function should still be empty, or you can specialize it for your second activity.

Do you see another technique to handle different orientations with different devices ?


And my turn to ask a question :
How can we easily implement an isXLargeScreen method ? ( my current implementation is something I'm really proud of :) )




15 comments:

Anonymous said...

public static boolean isXLargeScreen(Context context) {
return (context.getResources().getConfiguration().screenLayout
& Configuration.SCREENLAYOUT_SIZE_MASK)
>= Configuration.SCREENLAYOUT_SIZE_XLARGE;
}

AndroidBlogger said...

It's that simple ?

Thank you 'Anonymous' !!

santilezica said...

@OP I was just thinking about this today! I find your full inheritance solution simple and elegant. Thanks for sharing!

@Anonymous nice implementation.

Bhavya said...

I found a very simple solution that works as far as I've tested it--
Just disable the orientation sensor on the phone!
Now when you launch the app, by default it starts in portrait mode on a phone and in landscape mode on a tablet, and thereon stays in that orientation.
Is there something I've overlooked or is it just that simple?

Anonymous said...

Great Post! I have some android source code for an app I developed that I'd like to sell. Can anybody suggests a site I can post my app for sale? I post on here: http://www.sellmyapplication.com

Anonymous said...

Sir, I don't think you need to do all that. All you have to do is create a layout-land folder and the proper xml for handling the landscape mode. I'm doing that it works perfectly ;-)

AndroidBlogger said...

@anonymous2 :
I already have two different set of Xmls, for landscape and portrait mode.
The point here is to force the tablet to use the landscape mode, and the phone to use the portrait mode.
And to switch off the orientation change capacity.

TDV said...

im a phone man myself, so I skimmed the tablet stuff. Regardless, Good post.

Sharma said...

hey i've started using andoid 4.03 but i'm unable to create an avd ! what should i do?

Sharma said...

hey i've started using andoid 4.03 but i'm unable to create an avd ! what should i do?

Sharma said...

hey i've started using andoid 4.03 but i'm unable to create an avd ! what should i do?

Anonymous said...

Thanks..The second way of your was excellent..Helped me a lot...Thanks Again..

AndroidDev said...

My requirement is both orientation for Tablet and For phone only portrait.

I have used third solution















This is working fine for Tablet but not for Phone (Nexus 5x)

I am facing issue if auto rotate is on in phone(Nexus 5x) and it is in landscape then splash activity first start as landscape for brief moment and then it goes to portrait.

AndroidDev said...

Some devices are taking different orientation (for brief moment) then what I set in Manifest

I have checked every possible solutions in all stack overflow solutions and other web sites also but no luck.

My Requirement is :

Phone - only portrait mode

Tablet - portrait and landscape mode.
So tried using following:

For finding device if it is a Phone or a Tablet I used

if (getResources().getBoolean(R.bool.is_sw_600dp_size)) {

intent = new Intent(LauncherEmptyActivity.this, SplashActivityTablet.class);

} else {

intent = new Intent(LauncherEmptyActivity.this, SplashActivity.class);
}

And for my requirement if I set orientation as following for all activities (all orientation set at Manifest, tried also through programming but no luck)

LauncherEmptyActivity - Set Sensor
(or Set any of these above - "nosenser", "userSensor", "unspecified", "fullUser", "user")
SplashActivity - Set Portrait
SplashActivityTabet - Same As LauncherEmptyActivity
Result :

Application open in both portrait and landscape mode in Tablet (Expected O/P)
But for Phone If it is held in landscape mode then - SplashActivity will be landscape for brief moment and then it will be in portrait (This is the issue and I dont want that for brief moment).
Issue happens in

Device - Nexus 5x (Nought),
Emulator - Google Pixel (Oreo)
But working fine in Device - Moto G4 Plus (Nought)

This is for more explanation purpose, Following solution perfectly fine If my requirement is

(working fine for all above devices mentioned)

Phone - only portrait

Tablet - only landscape
LauncherEmptyActivity - Set Portrait
SplashActivity - Set Portrait
SplashActivityTablet - Set landscape
(all orientation set at Manifest)

Result : Correct As Expecte

AndroidBlogger said...

Sorry to hear that.

If I understand correctly, on the Nexus 5x, the splash screen, set as portrait only in the manifest, will appear in Landscape mode for a moment ?

Another solution would be to provide two different Apks for the two sort of devices.

I can't say that I like this solution, but if it's the only one you have...

It's explained here :
https://developer.android.com/guide/practices/screens-distribution.html