Using RX to detect shake Gestures by ThinqLinq

Using RX to detect shake Gestures

Part of the power of RX lies in it’s ability to compose complex operations  and keep the resulting code maintainable. I previously showed how to perform Drag-Drop operations with RX. This time, I want to take a look at a slightly more complex operation: Detecting “Shake” gestures on the Windows Phone 7.

The phone includes the ability to detect motion in 3D space through the built-in Accelerometer in the Microsoft.Devices.Sensors library. This sensor raises events when the phone is moved with information about how forcefully it was moved in the EventArgs. Detecting shakes is more complex than just knowing if the device was moved. We need to make sure that the user’s motion was aggressive enough to warrant a shake detection.

In addition, we need to know if the user moved the phone aggressively enough multiple times within a small enough time span. Simply monitoring the ReadingChanged event doesn’t fill the needs of detecting a real “Shake”. To manage all of these state changes and the times that each change occurs with traditional imperative code, we would either need to set up a number of queues remembering each motion that exceeds the tolerance and the times each happens and then act upon them when a sufficient number of these movements happen within a given time threshold. GoogleBinging this finds a number of sample implementations including Joel Johnson’s article and the recently released Shake Gesture Library. Both of these versions work with traditional events and manage the state internally.

If we use RX, we can simplify the code a bit by taking advantage of Observable.FromEvent to create an observable collection from the Accelerometer.ReadingChanged event, and the TimeInterval method to track the amount of time that passes between each accelerometer reading that exceeds the given tolerance (MinimumOffset).

Imports System.Linq
Imports Microsoft.Devices.Sensors
Imports Microsoft.Phone.Reactive

Public Module ShakeObserver
    Const MinimumOffset = 1.44
    Const TimeThreshold = 200

    Public Function GetObserver(ByVal accel As Accelerometer) As IObservable(Of IEvent(Of AccelerometerReadingEventArgs))

        Dim readingChangedObservable = Observable.FromEvent(Of AccelerometerReadingEventArgs)(accel, "ReadingChanged")

        Dim query = From knocks In
                    (From startEvent In readingChangedObservable
                     Where (startEvent.EventArgs.X ^ 2 + startEvent.EventArgs.Y ^ 2) > MinimumOffset).
                    Where knocks.Interval.TotalMilliseconds < TimeThreshold
                    Select knocks.Value

        Return query
    End Function
End Module

We can then consume this ShakeObserver in client code as we would any other Observable collection.

Dim accel As New Accelerometer
Dim query = From shake in GetObserver(accel)
            Select shake

query.Subscribe(Sub(_) DoSomething())

Of course, if we are composing even more complex interactions, the power of using Observables here would be even greater as that’s where RX truly shines.

Posted on - Comment
Categories: Rx - VB Dev Center - WP7 -
comments powered by Disqus