A lot of charade games use this feature to do an action on forward or backward tilt. This tutorial will show you how to detect forward and backward tilts when the phone is in landscape mode. You can check out Bollywood Charades the app I’ve created using this feature to see how it works.
AndroidManifest
This specific code detects tilt movements in landscape mode. So add android:screenOrientation=”landscape” to the manifest file first.
File: AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.ahotbrew.detecttilt" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="21" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" android:screenOrientation="landscape" > <activity android:name=".MainActivity" android:label="@string/title_activity_main" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
You can make it work to detect vertical tilt movements by changing the inclination in the isTiltUpward and isTiltDownward method.
Implement Sensors
We’ll be using the accelerometer and geomagnetic field sensor to accomplish this. Move on and implement the code below in your MainActivity.
File: MainActivity.java
package com.ahotbrew.detecttilt; import android.app.Activity; import android.hardware.Sensor; import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; public class MainActivity extends Activity implements SensorEventListener { private SensorManager mSensorManager; private Sensor accelerometer; private Sensor magnetometer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mSensorManager = (SensorManager) getSystemService(SENSOR_SERVICE); accelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); magnetometer = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD); initListeners(); } public void initListeners() { mSensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_FASTEST); mSensorManager.registerListener(this, magnetometer, SensorManager.SENSOR_DELAY_FASTEST); } @Override public void onDestroy() { mSensorManager.unregisterListener(this); super.onDestroy(); } @Override public void onBackPressed() { mSensorManager.unregisterListener(this); super.onBackPressed(); } @Override public void onResume() { initListeners(); super.onResume(); } @Override protected void onPause() { mSensorManager.unregisterListener(this); super.onPause(); } float[] inclineGravity = new float[3]; float[] mGravity; float[] mGeomagnetic; float orientation[] = new float[3]; float pitch; float roll; @Override public void onSensorChanged(SensorEvent event) { //If type is accelerometer only assign values to global property mGravity if (event.sensor.getType() == Sensor.TYPE_ACCELEROMETER) { mGravity = event.values; } else if (event.sensor.getType() == Sensor.TYPE_MAGNETIC_FIELD) { mGeomagnetic = event.values; if (isTiltDownward()) { Log.d("test", "downwards"); } else if (isTiltUpward()) { Log.d("test", "upwards"); } } } @Override public void onAccuracyChanged(Sensor sensor, int accuracy) { // TODO Auto-generated method stub } public boolean isTiltUpward() { 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); /* * If the roll is positive, you're in reverse landscape (landscape right), and if the roll is negative you're in landscape (landscape left) * * Similarly, you can use the pitch to differentiate between portrait and reverse portrait. * If the pitch is positive, you're in reverse portrait, and if the pitch is negative you're in portrait. * * orientation -> azimut, pitch and roll * * */ pitch = orientation[1]; roll = orientation[2]; inclineGravity = mGravity.clone(); double norm_Of_g = Math.sqrt(inclineGravity[0] * inclineGravity[0] + inclineGravity[1] * inclineGravity[1] + inclineGravity[2] * inclineGravity[2]); // Normalize the accelerometer vector inclineGravity[0] = (float) (inclineGravity[0] / norm_Of_g); inclineGravity[1] = (float) (inclineGravity[1] / norm_Of_g); inclineGravity[2] = (float) (inclineGravity[2] / norm_Of_g); //Checks if device is flat on ground or not int inclination = (int) Math.round(Math.toDegrees(Math.acos(inclineGravity[2]))); /* * Float obj1 = new Float("10.2"); * Float obj2 = new Float("10.20"); * int retval = obj1.compareTo(obj2); * * if(retval > 0) { * System.out.println("obj1 is greater than obj2"); * } * else if(retval < 0) { * System.out.println("obj1 is less than obj2"); * } * else { * System.out.println("obj1 is equal to obj2"); * } */ Float objPitch = new Float(pitch); Float objZero = new Float(0.0); Float objZeroPointTwo = new Float(0.2); Float objZeroPointTwoNegative = new Float(-0.2); int objPitchZeroResult = objPitch.compareTo(objZero); int objPitchZeroPointTwoResult = objZeroPointTwo.compareTo(objPitch); int objPitchZeroPointTwoNegativeResult = objPitch.compareTo(objZeroPointTwoNegative); if (roll < 0 && ((objPitchZeroResult > 0 && objPitchZeroPointTwoResult > 0) || (objPitchZeroResult < 0 && objPitchZeroPointTwoNegativeResult > 0)) && (inclination > 30 && inclination < 40)) { return true; } else { return false; } } } return false; } public boolean isTiltDownward() { 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); pitch = orientation[1]; roll = orientation[2]; inclineGravity = mGravity.clone(); double norm_Of_g = Math.sqrt(inclineGravity[0] * inclineGravity[0] + inclineGravity[1] * inclineGravity[1] + inclineGravity[2] * inclineGravity[2]); // Normalize the accelerometer vector inclineGravity[0] = (float) (inclineGravity[0] / norm_Of_g); inclineGravity[1] = (float) (inclineGravity[1] / norm_Of_g); inclineGravity[2] = (float) (inclineGravity[2] / norm_Of_g); //Checks if device is flat on groud or not int inclination = (int) Math.round(Math.toDegrees(Math.acos(inclineGravity[2]))); Float objPitch = new Float(pitch); Float objZero = new Float(0.0); Float objZeroPointTwo = new Float(0.2); Float objZeroPointTwoNegative = new Float(-0.2); int objPitchZeroResult = objPitch.compareTo(objZero); int objPitchZeroPointTwoResult = objZeroPointTwo.compareTo(objPitch); int objPitchZeroPointTwoNegativeResult = objPitch.compareTo(objZeroPointTwoNegative); if (roll < 0 && ((objPitchZeroResult > 0 && objPitchZeroPointTwoResult > 0) || (objPitchZeroResult < 0 && objPitchZeroPointTwoNegativeResult > 0)) && (inclination > 140 && inclination < 170)) { return true; } else { return false; } } } return false; } }
We implement the SensorEventListener and override it’s two methods onAccuracyChanged and onSensorChanged. The method onSensorChanged is called every time the phone moves, but because it’s extremely sensible it is even called when the phone is laying flat on the ground.
In order to save battery life and make sure the sensors don’t run when the app is paused or stopped we must unregister them OnPause, OnDestroy and OnBackPressed and resume them accordingly.
You can play around with the values inclination, objZeroPointTwo, objZeroPointTwoNegative to fine tune the tilt angle.
Sit back, take a sip from your hot brew and run your project.
Don’t forget to join or newsletter and get free android tutorials to enjoy with your hot brew.