In this post we will show how to get the sensor informatión of the device to do a simple compass application.
Getting the magnetic orientation with the TYPE_ORIENTATION sensor is deprecated, and now you must subscribe to TYPE_ACCELEROMETER and TYPE_MAGNETIC_FIELD to get the correct orientation. This way is a little tricky, but with this sample you will get it very easy.
This is the code for an activity that shows in a canvas a simple compass:
package com.samplecompass;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.view.View;
public class CompassActivity extends Activity implements SensorEventListener {
Float azimut; // View to draw a compass
public class CustomDrawableView extends View {
Paint paint = new Paint();
public CustomDrawableView(Context context) {
super(context);
paint.setColor(0xff00ff00);
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(2);
paint.setAntiAlias(true);
};
protected void onDraw(Canvas canvas) {
int width = getWidth();
int height = getHeight();
int centerx = width/2;
int centery = height/2;
canvas.drawLine(centerx, 0, centerx, height, paint);
canvas.drawLine(0, centery, width, centery, paint);
// Rotate the canvas with the azimut
if (azimut != null)
canvas.rotate(-azimut*360/(2*3.14159f), centerx, centery);
paint.setColor(0xff0000ff);
canvas.drawLine(centerx, -1000, centerx, +1000, paint);
canvas.drawLine(-1000, centery, 1000, centery, paint);
canvas.drawText("N", centerx+5, centery-10, paint);
canvas.drawText("S", centerx-10, centery+15, paint);
paint.setColor(0xff00ff00);
}
}
CustomDrawableView mCustomDrawableView;
private SensorManager mSensorManager;
Sensor accelerometer;
Sensor magnetometer;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCustomDrawableView = new CustomDrawableView(this);
setContentView(mCustomDrawableView); // Register the sensor listeners
mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
accelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
magnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);
}
protected void onResume() {
super.onResume();
mSensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_UI);
mSensorManager.registerListener(this, magnetometer, SensorManager.SENSOR_DELAY_UI);
}
protected void onPause() {
super.onPause();
mSensorManager.unregisterListener(this);
}
public void onAccuracyChanged(Sensor sensor, int accuracy) { }
float[] mGravity;
float[] mGeomagnetic;
public void onSensorChanged(SensorEvent event) {
if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER)
mGravity = event.values;
if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD)
mGeomagnetic = event.values;
if (mGravity != null && mGeomagnetic != null) {
float R[] = new float[9];
float I[] = new float[9];
boolean success = SensorManager.getRotationMatrix(R, I, mGravity, mGeomagnetic);
if (success) {
float orientation[] = new float[3];
SensorManager.getOrientation(R, orientation);
azimut = orientation[0]; // orientation contains: azimut, pitch and roll
}
}
mCustomDrawableView.invalidate();
}
}
I hope It will be useful for you.
Note: This is real code from Trackeen: your Mushrooms and Fishing GPS.
Hi,
ReplyDeleteI just try this code and it don't work...
It need any special permission in the manifest?
or any special code in the main.xml?
Thankyou
Yes you needn both or one of the following permissions:
ReplyDeleteandroid.permission.ACCESS_COARSE_LOCATION
android.permission.ACCESS_FINE_LOCATION
thank you for this nice example.
ReplyDeletei have been tried many times but every time i faced failure thank you...
keep this help up for beginners ..
Thanks to you, for commenting the post.
ReplyDeleteAnd remember, everyone is a beginner at some time.
Thank you for this great post.. I wanted to know how I could modify this code to get true North azimut as opposed to Magnetic North. Again thanks for this great post...
ReplyDeleteOrlando
Hello Orlando,
ReplyDeleteThe difference between Geographic and Magnetic Norths, varies both from place to place, and with the passage of time. Yo must get the Magnetic Declination, witch is the difference between both.
There are tables to get the magnetic declination in base of latitude and longitude and time, but I never used them.
Nice, great post.
ReplyDeleteI have made a similar compass app with map display and bearings as well and released the code OS.
Check my blog @ compastic.blogpost.com
Cheers
Sir i have created a screen from xml when I changes the orientation of mobile the buttons position are getting changed...can you tell me a better way to create screen that doesnot change the position of those buttons.....for example i created four buttons at the bottom of the screen ,when I changed the position of the screen to landscape the positon are changing .....
ReplyDeleteHi,
ReplyDeleteWhat happens if I work with 5 Sensors? do I need to create a type of "Switch case" to check which sensor triggered the event? (you did "if" for 2)
Or maybe it is possible to create different event handlers to each sensor type, and then the performance will be better?
Thank you,
Diego
not use <uses-permission..... in my htc Wildfire
ReplyDeletein Android 2.3.3
Thank you very much this helped me a lot.
ReplyDeleteA small hint:
If you want to have azimut in degrees, there's a simple Math-function for it Math.toDegrees(azimut)
This will give you something between -180° and 180°.
I made a radar simulator some time ago using TYPE_ORIENTATION sensor. I tried updating my implementation with this new way, but the results are not as smooth as before. Targets get a little shaky now. Any ideas of why this could be happening?
ReplyDelete@Androideas I have the same problem with the readings of Azimuth.
ReplyDeleteIt is not smooth at all. Besides, the values are not accurate. I am talking about 30-40 degrees of error. Does any one know who to compensate that error?
this is such a nice example. it will working fine in 2.3 but will it work in 4.0?
ReplyDeleteI've been doing this onSensorChanged and it seems to work so far. Why did you choose to do this onAccuracyChanged?
ReplyDeleteit's on onSensorChanged. Look again, probably an optical illusion..
DeleteIndeed it is an optical illusion :)
Deleteit got me too, because I didn't notice the closing curly brace of onAccuracyChanged
My test compass app worked on a Kindle Fire using TYPE_ORIENTATION. When I tried the new code I discovered that the Kindle returns null for sensor TYPE_MAGNETIC_FIELD. Yet it was able to provide a compass bearing using the deprecated method. To work on more devices the code apparently needs to try other sensors.
ReplyDelete@Joe Mattioni did you find a solution? I Try it using a samsung galaxy tab 7.7" P6800 and it shows nothing...
DeleteMy old test app using TYPE_ORIENTATION update orientation value about 30~40 ms
ReplyDelete(I register listener in SENSOR_DELAY_FASTEST mode).
But when I use this new solution to avoid deprecation,
my test app update orientation value about 2~3 s.
It's so lated
Is there any solution about increasing orientation sensor updated rate and also avoiding deprecation?
Use SENSOR_DELAY_GAME, it is faster for me than SENSOR_DELAY_FASTEST... For shake readings use low pass filter :)
ReplyDeleteThank you so much for this, it's really helpful and exactly what I needed. Was stuck on getting orientation for the longest time before I found this.
ReplyDeleteIt seems that no one had faced a similar problem with me. SensorManager.getRotationMatrix(...) method returns false nearly all the time.
ReplyDeleteWith this example north just turns with the device and doesn't stay pointed north, it just kind of lags around. Anyone have this problem?
ReplyDeleteHi,
ReplyDeleteYou've one serious bug
mGravity = event.values;
mGeomagnetic = event.values;
Here you should copy data, otherwise mGravity could be equal to mGeomagnetic , because they are just references!
it takes different values for each event...
DeleteHi,
ReplyDeleteI am developing a compass app for some other platform. Problem is i have only raw magnetometer reading and accelerometer readings. Can you tell me how can i use these reading to calculate magnetic heading for the app.
Any help will be appreciated. Thank you.
Thanks for sharing! This is not towards you or your code, but the depricated way gives me a much more "stable" result, the new way makes my compass flicker 0.5 degrees back and forward. Sure I can code around that problem, but why did the android devs do that in first place?
ReplyDelete