Orientation Compass Example

This example shows how to use an orientation sensor in a device in order to display a compass.

A screenshot of the application.

We import the classes and modules that will be needed by the application. The most relevant are the classes from the android.hardware module.

from java.lang import Math
from android.app import Activity
from android.content import Context
from android.graphics import Canvas, Paint, Path
from android.hardware import Sensor, SensorEvent, SensorEventListener, SensorManager
import android.os
from android.view import View, ViewGroup
from android.widget import LinearLayout, TextView

The OrientationCompassActivity is derived from the standard Activity class and represents the application. Android will create an instance of this class when the user runs it.

class OrientationCompassActivity(Activity):

    __interfaces__ = [SensorEventListener]

We declare that the class implements the SensorEventListener interface. This means that we need to implement the onAccuracyChanged and onSensorChanged methods.

The initialisation method calls the corresponding method in the base class before requesting access to sensors and setting up the user interface.

    def __init__(self):
    
        Activity.__init__(self)
    
    @args(void, [android.os.Bundle])
    def onCreate(self, bundle):
    
        Activity.onCreate(self, bundle)

We call the getSystemService method to obtain a sensor manager object which we cast to the correct type so that we can call its methods. From this object, we obtain an object that represents the orientation sensor.

        sensorManager = self.getSystemService(Context.SENSOR_SERVICE)
        self.sensorManager = CAST(sensorManager, SensorManager)
        self.orientation = self.sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION)

We create a layout and a view to display information about the sensor.

        layout = LinearLayout(self)
        layout.setLayoutParams(ViewGroup.LayoutParams(
            ViewGroup.LayoutParams.MATCH_PARENT,
            ViewGroup.LayoutParams.WRAP_CONTENT))
        layout.setOrientation(LinearLayout.VERTICAL)
        
        self.infoView = TextView(self)
        layout.addView(self.infoView)

If the sensor was not found, we write a message in the text label to indicate this.

        if self.orientation == None:
            self.infoView.setText("No orientation sensor found.")

Before making the layout the main view in the activity, we create an instance of a custom view class and add it to the layout.

        self.compass = CompassView(self)
        layout.addView(self.compass)
        
        self.setContentView(layout)

When the activity starts or is navigated to by the user, the onResume method is called. We call the corresponding method in the base class before showing the name of the sensor in the name label and registering the activity as a listener for the sensor if a sensor is available.

    def onResume(self):
    
        Activity.onResume(self)
        
        if self.orientation != None:
            self.infoView.setText(self.orientation.getName())
            self.sensorManager.registerListener(self, self.orientation,
                SensorManager.SENSOR_DELAY_UI)

When the user navigates away from the activity, the onPause method is called. We call the onPause method in the base class and unregister the activity as a listener with the sensor if it was previously registered.

    def onPause(self):
    
        Activity.onPause(self)
        
        if self.orientation != None:
            self.sensorManager.unregisterListener(self)

The following two methods are required to implement the SensorEventListener interface. Although we do not handle the first one in this example, we still need to provide an implementation.

    @args(void, [Sensor, int])
    def onAccuracyChanged(self, sensor, accuracy):
        pass

The onSensorChanged method is called when the sensor notifies the activity that a change has occurred. We respond to this by passing on information about the sensor's new values to the custom view we created earlier. This only occurs if a sensor is available.

    @args(void, [SensorEvent])
    def onSensorChanged(self, event):
    
        if self.orientation != None:
            self.compass.updateNeedle(event.values)

The CompassView class is derived from the standard View class and is used to display the representation of a compass in the activity.

As with other views, the initialisation method accepts a Context as its parameter and initialises itself by calling the initialisation method of the View class.

class CompassView(View):

    @args(void, [Context])
    def __init__(self, context):
    
        View.__init__(self, context)

We define a paint that defines how lines will be drawn and a path that will be used to display a compass needle.

        self.paint = Paint()
        self.paint.setStrokeWidth(float(2))
        self.paint.setFlags(Paint.ANTI_ALIAS_FLAG)
        self.paint.setStyle(Paint.Style.STROKE)
        self.path = Path()

We also define and initialise some attributes that we use to hold information about the position and size of the graphics in the view, and the orientation of the compass needle.

        self.x = self.y = self.r = float(0)
        self.angle = float(0)

The onSizeChanged method is called when the view is first shown and whenever it changes size afterwards.

    @args(void, [int, int, int, int])
    def onSizeChanged(self, width, height, oldWidth, oldHeight):
    
        self.x = width/2
        self.y = height/2
        self.r = Math.min(self.x, self.y) * 0.8
        
        L = self.r * 0.9
        l = self.r * 0.1
        
        self.path.rewind()
        self.path.moveTo(0, -L)
        self.path.lineTo(l, 0)
        self.path.lineTo(0, -l)
        self.path.lineTo(-l, 0)
        self.path.close()

The onDraw method is called when the view needs to be displayed. The parameter is a Canvas object that we draw onto. We call the onDraw method in the base class before adding our own decorations.

    @args(void, [Canvas])
    def onDraw(self, canvas):
    
        android.view.View.onDraw(self, canvas)
        
        self.paint.setARGB(255, 192, 192, 192)
        canvas.drawCircle(self.x, self.y, self.r, self.paint)
        
        canvas.translate(self.x, self.y)
        canvas.rotate(-self.angle)
        self.paint.setARGB(255, 255, 192, 192)
        canvas.drawPath(self.path, self.paint)

When we have orientation information, we call this method to update the orientation of the compass needle.

    @args(void, [[float]])
    def updateNeedle(self, values):
    
        self.angle = values[0]
        self.invalidate()

Files