In this tutorial, we're going to launch into a series that will allow two mobile devices to transfer information with a "Bump" gesture. This app will require a combination of client-side and server-side programming, and we'll cover all the steps to code both aspects. From here on, this app will affectionately be referred to as "Thump."
The main function of our "Thump" app will be the cross-device communication of data through an intermediary web server. The act of thumping two mobile devices together will appear to the end-user as local device to device communication, when in fact the web server has processed the data exchange. While the idea of local device communication may seem like the most practical approach at first, in practice it is filled with cross-platform hangups and security nightmares! So instead, we will use a Rails app hosted on the Heroku platform to handle receiving messages from our devices and delivering the message to its intended recipient.
The way this works is quite interesting. The server will essentially make an estimation of who the most likely recipient of a message will be based on a combination of GPS coordinates and a server timestamp. By simultaneously (or near simultaneously) thumping our two devices together, we will send latitude and longitude information over to the server so it can determine that our two locationally similar devices were trying to communicate with each other in something near real-time. Simple, right?
While this method isn't exactly perfect, it does provides a statistical likelihood that our two devices intended to communicate with each other. One of the big problems with a service like this (and our app is no exception) would be an event like a tradeshow or somewhere where lots of people might be trying to "thump" all at the same time. While it may be unlikely, it could potentially allow for the transmission of a message to an unintended recipient.
We'll start by creating some basic functionality for our mobile app. In our main.lua file, we will add some essential lines of code to get us started.
local ui = require('ui') system.setLocationAccuracy( 10 ) local isSimulator = "simulator" == system.getInfo("environment") local deviceId = system.getInfo("deviceID")
The first line requires that we include the UI library which greatly assists the creation of native components in Corona. This code will be included in a source-code zip file attached to this Mobiletuts+ tutorial. Next, we will set a location accuracy threshold for our devices. We need the device to try its best to get us a location within an error tolerance of no more than 10 meters. If the distance is any greater than this, we run the risk of picking up accidental thumps from devices that are not in our vicinity. To make things easier while we develop, we will detect whether our app is running in the Corona simulator or on the device. This will be to primarily prevent odd behavior from features not currently supported by the simulator. Lastly, we need to identify a device with a unique value. A deviceID like this will prevent the server from trying to deliver a message back to the device that sent it instead of the intended recipient.
local bg = display.newRect(0, 0, display.contentWidth, display.contentHeight) local logo = display.newImageRect("logo.png", 172, 107) logo.x = display.contentWidth / 2 logo.y = (display.contentHeight / 2) - 150
Next, we create a background rectangle that gives our app a nice, white background. The next 3 lines will display a logo in the top-center part of the screen.
local titleLabel = ui.newLabel{ bounds = { 15, 5, 290, 55 }, text = "Message", font = native.systemFontBold, textColor = { 12, 12, 100, 255 }, size = 24, align = "center" } titleLabel:setReferencePoint(display.TopCenterReferencePoint) titleLabel.y = logo.y + 60
The above code uses the UI libary's access to the native display components for the device. In this case, we are simply displaying the word "Message" in a dark blue hue. The scope of this article doesn't include all the intricacies of the native display library, so I suggest taking a look on the Corona site if you're new to the SDK. The Y coordinates are being set to 60 pixels greater than the position of the logo we just displayed.
if isSimulator then -- Simulator (simulate the textField area) textField = display.newRect( 20, titleLabel.y + 60, 280, 30 ) textField:setFillColor( 200, 200, 200 ) else local function fieldHandler( event ) if ( "submitted" == event.phase ) then native.setKeyboardFocus( nil ) end end textField = native.newTextField( 20, titleLabel.y + 60, 280, 30, fieldHandler ) end textField:setReferencePoint(display.TopCenterReferencePoint) textField.x = display.contentWidth / 2 textField.y = titleLabel.y + 60
One of the limitations of the simulator is that it cannot display all native components of mobile devices properly. To prevent it from throwing errors, we will detect if we are running the app in the simulator and display a gray box instead of an input field. This will help us with our positioning of the elements. If the app is not running in the simulator, we are going to display a native "textField" component that will allow the end user to type a message. The fieldHandler callback is necessary to detect for a phase of "submitted", or, in other words, the user pressing the "return" button. By catching this event we can hide the keyboard after the user has finished typing their message.
local removeKeyboard = function( event ) -- Hide keyboard native.setKeyboardFocus( nil ) end bg:addEventListener( "tap", removeKeyboard )
As an added user interface bonus, we can add an event listener to our white background that waits for a "tap" event. This way, if the user taps the screen outside of the keyboard area, it will remove focus from it and cause it to disappear.
local latitudeText = "" local longitudeText = "" if isSimulator then local alert = native.showAlert( "Error", "GPS not available in Simulator" ) else local locationHandler = function(event) latitudeText = string.format( '%.8f', event.latitude ) longitudeText = string.format( '%.8f', event.longitude ) end Runtime:addEventListener( "location", locationHandler ) end
Now on to the good stuff! First, we'll detect whether we are running in the simulator and simply display an error message that the GPS is not available. When we are running on the device, we create a runtime listener that continuously grabs our location from the device's location service and calls our locationHandler method with this data. We convert this down to have 8 decimal places of accuracy which should be more than accurate for our purposes.
local getThump = function(event) if event.isShake == true then local alert = native.showAlert( "Thump!", "Location: "..latitudeText..","..longitudeText.."\r\nMessage: "..textField.text, {"OK"} ) system.vibrate() end end Runtime:addEventListener("accelerometer", getThump)
Lastly, we will create another runtime event listener that takes data from the device's accelerometer and passes it to our method getThump. Inside this method we will detect whether the event was a "shake" event. A shake event is another name for what will happen when we "thump" two devices together in hopes of transmitting a message. Since we haven't written our server component yet, we will simply display this data in an alert box to show that our app is working thus far. To test this out, you can simply type a message and give the device that is running the app a shake.
Next Time. . .
Stay tuned for part II of this series next week, where we will tackle the server-side functionality in Rails on Heroku!
Comments