[Xamarin] Implement a ShakeDetector in your Xamarin.Android application

January 26th, 2016 | Posted by Tom in .NET | Xamarin | Read: 4,234

Today, I wanted to try something new and to play a bit with sensors, on my Android device. After looking for an idea, I thought it could be interesting to implement a “ShakeDetector”, that means I wanted to be able to detect when the user shakes the device.

As I was a bit lazy, I first checked on Internet if nothing have been done and I just found different implementations of ShakeDetector for Android but written in Java, like this one: http://jasonmcreynolds.com/?p=388

Well, it does not matter: let’s see how we can adapt the Java code to get a clean C# code which allow us to detect the Shake gesture!

First step is to implement the ShakeDetector class, that will compute all the necessary stuff to detect the shake event, using the accelerometer. This class should implement SensorEventListener but this does not exists directly in Xamarin, we need to use the interface ISensorEventListener:

public class ShakeDetector : ISensorEventListener

But as soon as we try to implement the interface, we are ask to implement the following properties/methods:

image

If you try to implement them, you’ll see that they will never be called and so your code will never be called too. The reason and solution are described on this post by CheeseBaron: http://blog.ostebaronen.dk/2013/09/how-to-implement-intptr-handle-and.html

The brief explanation is that the Handle provides an Android Callable Wrapperto Android. All native Android interfaces extend IJavaObject so they will expect that to be implemented. So the simple answer is that you do not implement Handle or Disposeyourself, however you inherit from Java.Lang.Object which does that for you.

So to solve this error, you just need to modify the class ShakeDetector like this:

public class ShakeDetector : Java.Lang.Object, ISensorEventListener

Once this is done, we can continue to implement the rest of the class:

public class ShakeDetector : Java.Lang.Object, ISensorEventListener
{
    private const float SHAKE_THRESHOLD_GRAVITY = 2.7F;
    private const int SHAKE_SLOP_TIME_MS = 500;
    private const int SHAKE_COUNT_RESET_TIME_MS = 3000;

    private long mShakeTimestamp;
    private int mShakeCount;

    public delegate void OnshakeHandler(object sender, int shakeCount);
    public event OnshakeHandler Shaked;

    public void OnAccuracyChanged(Sensor sensor, SensorStatus accuracy)
    {
        // Do nothing in our case.
    }

    public void OnSensorChanged(SensorEvent e)
    {
        var x = e.Values[0];
        var y = e.Values[1];
        var z = e.Values[2];

        var gX = x / SensorManager.GravityEarth;
        var gY = y / SensorManager.GravityEarth;
        var gZ = z / SensorManager.GravityEarth;

        // gForce will be close to 1 when there is no movement.
        var gForce = Java.Lang.Math.Sqrt(gX * gX + gY * gY + gZ * gZ);

        if (!(gForce > SHAKE_THRESHOLD_GRAVITY))
            return;

        var now = JavaSystem.CurrentTimeMillis();

        // Ignore shake events too close to each other (500ms)
        if (mShakeTimestamp + SHAKE_SLOP_TIME_MS > now)
        {
            return;
        }

        // Reset the shake count after 3 seconds of no shakes
        if (mShakeTimestamp + SHAKE_COUNT_RESET_TIME_MS < now)
        {
            mShakeCount = 0;
        }

        mShakeTimestamp = now;
        mShakeCount++;

        if (this.Shaked != null)
        {
            this.Shaked(this, mShakeCount);
        }
    }
}

Note that I’ve adapt the code a bit to raise an event when the shake gesture is detected!

Now, we can continue to implement the code by registering/unregistering our new class (which implement ISensorEventListener) in the SensorManager:

[Activity(Label = "AndroidShakeDetector",
    MainLauncher = true,
    Icon = "@drawable/icon",
    ScreenOrientation = ScreenOrientation.Portrait)]
public class MainActivity : Activity
{
    private static readonly object _syncLock = new object();

    private SensorManager _sensorManager;
    private Sensor _sensor;
    private ShakeDetector _shakeDetector;

    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);

        SetContentView(Resource.Layout.Main);

        var button = FindViewById<Button>(Resource.Id.MyButton);

        _sensorManager = (SensorManager)GetSystemService(Context.SensorService);
        _sensor = _sensorManager.GetDefaultSensor(SensorType.Accelerometer);

        _shakeDetector = new ShakeDetector();
        _shakeDetector.Shaked += (sender, shakeCount) =>
        {
            lock (_syncLock)
            {
                button.Text = shakeCount.ToString();
            }
        };
    }

    protected override void OnResume()
    {
        base.OnResume();

        _sensorManager.RegisterListener(_shakeDetector, _sensor, SensorDelay.Ui);
    }

    protected override void OnPause()
    {
        base.OnPause();

        _sensorManager.UnregisterListener(_shakeDetector);
    }
}

Be sure to allow access to the accelerometer and launch the application. As soon as you “shake” the device, you’ll see that the counter will increment and reset to 0 after 3 seconds of inactivity!

You can found the code on Github: https://github.com/ThomasLebrun/XamarinAndroidShakeDetector

 

Happy coding!

You can follow any responses to this entry through the RSS 2.0 You can leave a response, or trackback.

Add Comment Register



Leave a Reply