Previously in this series we’ve looked at various ways in which we can improve the experience for our user in terms of both simple aesthetics (making the app look nicer) and improving the controls and navigation through the app. In this final article in the series, we’ll focus on how we can speed the user directly to the content that they want as quickly as possible.
When we first developed the Bluetooth LE upon which we’ve based this series, the aim was to demonstrate the various steps that we need to go through in order to connect to and obtain data from the TI SensorTag device. However what works well as an explanation for developers does not necessarily make for something which will be well suited to an end user.
Although fairly simple, our app requires the user to perform a number of steps before they will see the temperature obtained from the SensorTag:
- They launch the app
- They start scanning for devices
- Wait for the device scan to complete
- Select one of the discovered devices
While this may not seem very much, let’s consider how the average end-user may differ from the developer. I have two SensorTag devices, so the ability to switch between them is an advantage, but a typical end-user is much more likely to have a single device. If that’s the case then it’s actually a bit of a pain to have to go through the entire device discovery phase again. Even for me with multiple devices, having to go through device discovery each time I start the app is quite painful because many times I am using the same device and it would be much smoother if it remembered the last device. In Part 4 of this series we added the ability to return to the DeviceListFragment from the DisplayFragment in order to scan and select a different device. Because we have that functionality in place, it would make sense to attempt to re-connect to the previous device when the app starts.
The code to do this is not particularly tricky, so I’m not going to attempt a full line-by-line breakdown here. To do so would result in 2 to 3 long but fairly uninteresting blog posts. The full, completed code is available, and anyone interested in seeing the exact changes can look at a delta between the code on branches Part5 and Part6.
The things that I have implemented to achieve this are as follows:
When the user connects to a device following device discovery, the MAC address of the BLE device are stored to SharedPreferences. If the user manually disconnects from the device from the “Disconnect” option in the ActionBar, the SharedPreferences entry is cleared.
When BleService receives a
MSG_REGISTER event (which occurs during app startup) it checks SharedPreferences to see if there is a stored MAC address. If not, it goes in to IDLE state (as before). If there is a MAC address stored it will automate the device discovery process, exiting discovery as soon as a device matching the MAC address is seen. It will then go through the rest of the stages of connection, service discovery, and notification registration.
There is an edge-case that needs to be handled – the device is no longer in range or otherwise available during auto-reconnection. In this case, a delayed handler has been added to DeviceFragment. If no data has been retrieved within 15 seconds of the Fragment being attached to an Activity it will assume a connection failure, the DeveiceListFragment will be shown, and the SharedPreferences entry will be cleared.
When we run this we can see the auto-reconnect behaviour if we quit the app and restart, and also the timeout if we do the same when the SensorTag is no longer available:
That concludes are look at improving the overall look and feel, and the user flow through the app. While it’s still by no means a fully polished app (we could make the DevliceListFragment a bit prettier, for example) the techniques for doing so are probably not as interesting as some of those already covered in this series, hence we’ll skip them.
The source code for this article is available here.
© 2014, Mark Allison. All rights reserved.
Copyright © 2014 Styling Android. All Rights Reserved.
Information about how to reuse or republish this work may be available at http://blog.stylingandroid.com/license-information.
Great article in general in relation to BLE and SensorTag.
I like how you used ConcurrentLinkedQueues to deal with the asynchronous
issue when reading and writing GATT characteristics rather than using a simple state machine.
I am trying to extend your code to query the accelerometer data from the (sensorTag) device. You use messages to send the data back up to the UI thread. However the message object only supports two arguments but the accelerometer data obviously has a x,y and z component. I was thinking about passing back the characteristic as the argument of the messages instead of the raw data and to process the data on the UI thread instead. Not sure this is the best solution. What do you recommend?
Why not parse the raw data in the
Service, and create a custom class to contain the x, y, & z values, then pass this object instance in the
objfield of the
Mark Quick update – I wrote a new class to contain the 3D data and assign this as the argument to the message instead of packaging the raw data as part of the BLE service. This is a neater solution rather then passing the raw characteristic back that I had considered/suggested previously..
Just read your comment – looks like we are proposing the same solution..