Back in Sept 2009 I did a few presentations for FITC Mobile 2009 and in the sample code I included a modified version of the Sensor API available on Codeplex. The modified version of the API included some code that leveraged the accelerometer on some devices to detect when a device was shaken. This code base was for Windows Phone 6.x and used .NET Compact Framework 3.5. Essentially all you would have to do is wire up a ShakeDetected event and whenever the API detects a shake you would be notified via an event.
private IGSensor m_sensor;
public Form1()
{
InitializeComponent();
m_sensor = GSensorFactory.CreateGSensor();
m_sensor.ShakeDetected += m_sensor_ShakeDetected;
}
void m_sensor_ShakeDetected(object sender, EventArgs e)
{
//Invoke into the main thread since we are coming form a background thream
this.Invoke(new EventHandler(delegate(object s, EventArgs ea)
{
//Code once shake is detected
MessageBox.Show("Shake Detected");
}));
}
Couple of things I didn’t like about this code.
- There is a thread running in the background doing a calculation of the X,Y,Z values and determining whether a shake has occurred. This will drain the battery.
- It’s not a standard API and does not work on all devices because either the device does not have the hardware or the PInvokes are specific for only one OEM such as HTC.
To try it out for your self download the sample code for shake detection here. (Find it under Release Presentation Code\New Windows Mobile 6.5\Demo1\ShakeDetection).
Shake Detection On Windows Phone 7
Since the development environment for Windows Phone 7 is completely different now and uses Silverlight, the ‘shake detection’ code essentially is useless but that doesn’t mean the code can’t be ported over.
The good thing is Windows Phone 7 introduces standard hardware and standard APIs for sensors which should alleviate some developer pains from previous Windows Phone versions. All Windows Phone 7 devices from OEMs will include an accelerometer so no more worrying about code not working. Before I get into how to detect shake, see here for an overview of the accelerometer on Windows Phone 7.
To get shake detection working on Windows Phone 7, I had to wrap the AccelerometerSensor class within a wrapper class called AccelerometerSensorWithShakeDetection. Using the new class is pretty straight forward and basically have to create an instance of the class and wire up the ShakeDetected event handler.
AccelerometerSensorWithShakeDetection m_shakeSensor;
public MainPage()
{
InitializeComponent();
m_shakeSensor = new AccelerometerSensorWithShakeDetection();
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
m_shakeSensor.ShakeDetected += m_shakeSensor_ShakeDetected;
}
private void button2_Click(object sender, RoutedEventArgs e)
{
m_shakeSensor.ShakeDetected -= m_shakeSensor_ShakeDetected;
}
void m_shakeSensor_ShakeDetected(object sender, EventArgs e)
{
lblShakeStatus.Text = string.Format("Shake Detected @ {0}",
DateTime.Now.ToString("hh:mm:ss"));
}
AccelerometerSensorWithShakeDetection implementation is different from the previous version because we now have a standard API. Internally we wire up the AccelerometerSensor.ReadingChanged event when a user wires up the ShakeDetected event and call the AccelerometerSensor.Start and AccelerometerSensor.Stop when the AccelerometerSensorWithShakeDetection.Start/Stop methods are called.
public event EventHandler ShakeDetected
{
add
{
m_ShakeDetectedHandler += value;
m_sensor.ReadingChanged += Default_ReadingChanged;
}
remove
{
m_ShakeDetectedHandler -= value;
m_sensor.ReadingChanged -= Default_ReadingChanged;
}
}
public void Start()
{
if (m_sensor != null)
m_sensor.Start();
}
public void Stop()
{
if (m_sensor != null)
m_sensor.Stop();
}
When the reading change event occurs, we run our shake detection logic to see if the user has shaken the device.
private void Default_ReadingChanged(object sender, AccelerometerReadingAsyncEventArgs e)
{
//Code for checking shake detection
if (e.Value.State == SensorState.Ready)
{
AccelerometerReading reading = e.Value.Value;
DateTimeOffset timeStamp = e.Value.Timestamp;
try
{
if (!shaking && this.CheckForShake(m_lastReading, reading,
shakeThreshold) && shakeCount >= 5)
{
//We are shaking
shaking = true;
shakeCount = 0;
OnShakeDetected();
}
else if (this.CheckForShake(m_lastReading, reading, shakeThreshold))
{
shakeCount++;
}
else if (!this.CheckForShake(m_lastReading, reading, 0.2))
{
shakeCount = 0;
shaking = false;
}
m_lastReading = reading;
}
catch { /* ignore errors */ }
}
}
private void OnShakeDetected()
{
if (this.m_ShakeDetectedHandler != null)
this.m_ShakeDetectedHandler(this, EventArgs.Empty);
}
private bool CheckForShake(AccelerometerReading last,
AccelerometerReading current, double threshold)
{
double deltaX = Math.Abs((double)(last.X - current.X));
double deltaY = Math.Abs((double)(last.Y - current.Y));
double deltaZ = Math.Abs((double)(last.Z - current.Z));
return (deltaX > threshold && deltaY > threshold) ||
(deltaX > threshold && deltaZ > threshold) ||
(deltaY > threshold && deltaZ > threshold);
}
When a shake is detected, the ShakeDetected handler is called to notify the listeners.
So porting code from previous Windows Phone versions to Windows Phone 7 is definitely possible because we are still using Managed code. Some advantages to the new code are
- Standard APIs are used for the Accelerometer
- No threads running in the background to detect shake. Instead an event based mechanism is used which will preserve the user experience and preserve the battery life.
One thing to note, to code compiles and runs on the emulator, but because I don’t have a real Windows Phone 7 device, I cannot test the code. The version for .NET CF 3.5 works and the shake detection logic is essentially the same for Windows Phone 7 so I’m assuming it will work. But if anyone in the product group wants to test on a real device (or send me a real device :) please do and let me know if it works.
Once I get a real device, I’ll test and update this post. In the meantime, download the shake detection source code here to take a peak.