I'm trying to make a compass app for android, as many others have before me.

However, I'm having a problem understanding how the whole rotation matrix thing is supposed to work. I've tried my way forward and have come up with the code below:

public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState == null) { getSupportFragmentManager().beginTransaction() .add(R.id.container, new PlaceholderFragment()) .commit(); } } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. /*switch (item.getItemId()) { case R.id.action_settings: return true; }*/ return super.onOptionsItemSelected(item); } public static class PlaceholderFragment extends Fragment { TextView degreesText; ImageView needleView; private SensorManager sMan; private float[] mAcc = new float[3]; private float[] mMag = new float[3]; private float[] mRotationMatrix = new float[9]; private float[] mOrientation = new float[3]; private float fAzimuth; private boolean firstRun; public PlaceholderFragment() { } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_main, container, false); //Test if application has been started for the first time firstRun = getActivity().getSharedPreferences("PREFERENCE", MODE_PRIVATE).getBoolean("firstRun", true); if (firstRun) { //Build warning dialog AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); //Set message and title builder.setMessage(R.string.dialog_message).setTitle(R.string.dialog_title); //Add dismiss button builder.setNeutralButton(R.string.dismiss, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { //On okay click } }); AlertDialog dialog = builder.create(); //Show dialog dialog.show(); } //Set firstRun to false, so dialog doesn't show again getActivity().getSharedPreferences("PREFERENCE", MODE_PRIVATE).edit().putBoolean("firstRun", false).commit(); //Get text and image views degreesText = (TextView)rootView.findViewById(R.id.degrees_text); needleView = (ImageView)rootView.findViewById(R.id.needle); //Set up sensor manager sMan = (SensorManager)getActivity().getSystemService(SENSOR_SERVICE); //Listener for magnetic and accelerometer events SensorEventListener eventListener = new SensorEventListener() { @Override public void onSensorChanged(SensorEvent event) { switch (event.sensor.getType()) { case Sensor.TYPE_ACCELEROMETER: mAcc = event.values.clone(); break; case Sensor.TYPE_MAGNETIC_FIELD: mMag = event.values.clone(); break; } SensorManager.getRotationMatrix(mRotationMatrix, null, mAcc, mMag); SensorManager.getOrientation(mRotationMatrix, mOrientation); fAzimuth = (float)Math.round(Math.toDegrees(mOrientation[0])); fAzimuth = (fAzimuth + 360) % 360; degreesText.setText(String.valueOf(fAzimuth)); needleView.setRotation(-fAzimuth); } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { } }; //Register listeners for magnetic and accelerometer sensors sMan.registerListener(eventListener, sMan.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD), SensorManager.SENSOR_DELAY_NORMAL); sMan.registerListener(eventListener, sMan.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL); return rootView; } } }

This works perfectly well as long as my phone is in portrait mode and laying down on a flat surface. My problem is in understanding what I should do to make the same thing work in both portrait and landscape mode, as well as when I hold the phone in some other orientation.

From what I've read I need to use SensorManager.remapCoordinateSystem() , but I don't understand which two axes I'm supposed to supply, and how it works.

Any help in understanding all of this would be greatly appreciated, thank you!