Get the FULL version

Android: Obtaining the current orientation using a BroadcastReceiver

Android: Obtaining the current orientation using a BroadcastReceiver thumbnail

This Android tutorial explains how to create a Broadcast Receiver that detects screen orientation changes that are triggered by rotating the device. Since screen orientation changes don’t happen every second, it’s better to detect it using a BroadcastReceiver instead of a Service. Both can be used to execute tasks on the background, but the BroadcastReceiver execution will be triggered only when the desired Intent is filtered (in this case, a screen orientation change). Right after the execution, the background task is killed, which is ideal since a BroadcastReceiver can be created to obtain the new screen orientation only after it has changed.

All code featured in this tutorial is available for download at the end of the post, and has been created and tested in Android 2.0, both at real and emulated devices. There can be some issues when running this code on the emulator – you can find more information about that at the Final Thoughts section, at the bottom of the page.

As stated, the first thing that’s going to be required to make this work is to create a class that inherits from BroadcastReceiver and implements the onReceive() method. This method tells what the BroadcastReceiver should do when the correct Intent is detected. So, for now, a child of the BroadcastReceiver class is being defined so as the code that has to execute when the BroadcastReceiver is triggered. Later on this tutorial, you will find how to filter intents to detect orientation changes. For the sake of simplicity, the code below detects orientation changes and the angle of the device, displaying this info in Toast notifications. Here’s the code:

package fortyonepost.com.orientationbrec; 

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.view.Surface;
import android.view.WindowManager;
import android.widget.Toast;

public class OrientationBroadcastReceiver extends BroadcastReceiver
{
	//An integer that holds the value of the orientation given by the current configuration
	private int configOrientation;

	//A WindowManager object that will act as a handle to the window service
	private WindowManager wm;
	//An integer that holds the value of the orientation (in degrees) given by the window service
	private int windowServOrientation;

	@Override
	public void onReceive(Context context, Intent intent)
	{
		//Get the orientation from the current configuration object
		configOrientation = context.getResources().getConfiguration().orientation;

		//Display the current orientation using a Toast notification
		switch (configOrientation)
		{
			case Configuration.ORIENTATION_LANDSCAPE:
			{
				Toast.makeText(context, "Orientation is LANDSCAPE", Toast.LENGTH_SHORT).show();
				break;
			}
			case Configuration.ORIENTATION_PORTRAIT:
			{
				Toast.makeText(context, "Orientation is PORTRAIT", Toast.LENGTH_SHORT).show();
				break;
			}
			case Configuration.ORIENTATION_SQUARE:
			{
				Toast.makeText(context, "Orientation is SQUARE", Toast.LENGTH_SHORT).show();
				break;
			}

			case Configuration.ORIENTATION_UNDEFINED:
			default:
			{
				Toast.makeText(context, "Orientation is UNDEFINED", Toast.LENGTH_SHORT).show();
				break;
			}
		} 

		//Get a handle to the Window Service
		wm = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
		//Query the current orientation made available by the Window Service
		//The getOrientation() method is deprecated. Instead, use getRotation() when targeting Android API 8 (Android 2.2 - Froyo) or above.
		windowServOrientation =  wm.getDefaultDisplay().getOrientation();

		//Display the current orientation using a Toast notification
		switch (windowServOrientation)
		{
			case Surface.ROTATION_0:
			{
				Toast.makeText(context, "Rotation is 0 degrees.", Toast.LENGTH_SHORT).show();
				break;
			}

			case Surface.ROTATION_90:
			{
				Toast.makeText(context, "Rotation is 90 degrees.", Toast.LENGTH_SHORT).show();
				break;
			}

			case Surface.ROTATION_180:
			{
				Toast.makeText(context, "Rotation is 180 degrees.", Toast.LENGTH_SHORT).show();
				break;
			}

			case Surface.ROTATION_270:
			{
				Toast.makeText(context, "Rotation is 270 degrees.", Toast.LENGTH_SHORT).show();
				break;
			}
		}
	}
}

As it’s possible to see, the OrientationBroadcastReceiver class inherits from BroadcastReceiver (line 12). There are three member variables in this class: two Integers and a WindowManager. The integers are going to hold the value of the obtained screen orientation constants, and the WindowManager will act as a handle to the system’s window service (lines 15 through 20).

Next, the onReceive() method is being overridden. There, the current Configuration object is being obtained from the Resources associated with the current Context. With the Configuration, a integer constant that translates to a screen orientation is can be read and stored at configOrientation (line 26). This value tells if the current screen orientation is one of the following four values: portrait, landscape, square or undefined. The configOrientation is then submitted to a switch/case block, displaying a Toast notification accordingly (line 29 through 53). In other words, a Toast notification with the screen rotation is displayed every time the device is rotated.

In some cases, knowing that the device is being held horizontally or vertically with a portrait or horizontal orientation isn’t enough. The device can be held vertically, upside down and the screen orientation will be set as portrait. To query the correct rotation of the device it’s necessary to attach to the window service. This is done at line 56. Therefore, to obtain the device’s angle of rotation, the getOrientation() method from the WindowManager object must be called (line 59). It returns one of the following angles represented as integer constants: 0, 90, 180 or 270. It’s important to note that this method has been replaced by the getRotation() method in Android API level 8 (Android 2.2).

Moving on, the second switch/case block displays the correct angle obtained from the window service as Toast notifications (lines 62 through 87).

Enough of this class; here’s the Activity that is responsible for registering and unregistering the BroadcastReceiver. Here’s the code:

package fortyonepost.com.orientationbrec;

import android.app.Activity;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;

public class OrientationBroadcastReceiverActivity extends Activity
{
	//Create and initialize an OrientationBroadcastReceiver object
	private OrientationBroadcastReceiver orientationBR = new OrientationBroadcastReceiver();
	//Create and initialize a new IntentFilter
	private IntentFilter orientationIF = new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED);

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

	@Override
	protected void onResume()
	{
		//Register the Orientation BroadcasReceiver
        this.registerReceiver(orientationBR, orientationIF);
		super.onResume();
	}

	@Override
	protected void onPause()
	{
		//Unregister the Orientation BroadcasReceiver to avoid a BroadcastReceiver leak
		this.unregisterReceiver(orientationBR);
		super.onPause();
	}
}

It should come as no surprise that a object of the OrientationBroadcastReceiver class defined above is a member of this Activity (line 11). Also, at this same line, the object is being initialized. Right below it, a IntentFilter object is being declared and initialized (line 13). As the name suggests, the IntentFilter is responsible for filtering all the Intents being broadcast on the Android system. Conveniently, the constructor allows us to set the string of the Intent that the filter needs to catch. For this case, only the Intent.ACTION_CONFIGURATION_CHANGED constant string is the one being passed.

This Intent is broadcast every time there is a configuration change. So, not only screen orientation changes will cause this Intent to be broadcast, but also a variety of events, such as a change on the screen size, location, input mode, etc.  That said, be careful when running code on a BroadcastReceiver based on this Intent. There’s no specific Intent for screen orientation changes.

Finally, in order to make the OrientationBroadcastReceiver work properly, it needs to be registered and unregistered by the Activity at the onResume() and onPause() methods (lines 27 and 35).

That’s it! Here are some screenshots of the example project:

Final Thoughts

Since there is no specific Intent for orientation changes, a more refined filtering may be needed at the onReceive method. Alternatively, the android:configChanges attribute can be used on the <activity> tag at the application’s manifest to tell the system which configuration changes the application will handle, although this approach is heavily discouraged by the official Android documentation.

As for this tutorial, the code will execute fine on the emulator. However, sometimes, when rotating the emulated device, the orientation and angle that the device had before the orientation change are displayed first and the current rotation and orientation are displayed right afterwards. This only happened a handful of times, and I not completely sure what’s causing it.

My best guess is that almost disappearing Toast notifications are still in the screen when the emulated device is rotated too fast, so they are queued up for rendering again when there’s a orientation change. I have noticed no problems when executing this application on a real device.

Downloads

Be the first to leave a comment!

Leave a Comment

Post Comments RSS