Filter results by

Quick Apps: Plot Your Location in Real-Time With SAMI

Today we’re going to learn how easy it is to create a location-aware Android application using the SAMI platform. SAMI will serve as an online storage service and also as a real-time data broker for your devices.

At a glance, this could look like something complex—and indeed it is—however you’ll rapidly learn that SAMI provides the necessary tools to build the scenario really fast and easily.

The scenario

  1. A mobile device will constantly report the current location to SAMI, using either GPS or network resources.
  2. SAMI will store this information for historical usage. Also, location will be consumed using WebSockets for real-time visualization.
  3. Our application will, on user demand, display a marker on the last position reported by SAMI, and also will be able to render a polyline connecting the latest 100 reported positions.

The design

To accomplish this, we need 2 basic modules in our application:

  • Data producer
    • Get the current device location.
    • A /websocket WebSocket connection to send the location to SAMI.
  • Data consumer
    • A /live WebSocket connection to listen to the latest location linked to a WebView that displays a map with a marker.
    • A WebView (or web browser) to display a polyline connecting the latest 100 points.

Getting started

Before starting to code, we need to do some basic setup in SAMI.

This application requires only a device and a device token, and these can be created using the most basic functionality available at the User Portal.

Log into the SAMI User Portal, and connect a new device by typing “DemoGPS” into the box.

This device uses the DemoGPS device type. This type accepts two numeric fields: lat and lng.

In the dashboard, open the device you just created. From here, take note of the device ID and generate a device token. With these credentials, we will be able to send data to SAMI, and also consume this data.

Generate a device token

Finally, you downloaded from github the Android Studio source code for this tutorial. (Note: An update for this code is planned. The link has been temporarily disabled.) Please update both values at Config.java (at package com.samsung.locator):

1 public class Config {
2     public static final String DEVICE_ID = null;
3     public static final String ACCESS_TOKEN = null;
4     ...

Getting your location

There are lots of sample snippets on the Net that explain how to get the current location on Android. For this tutorial we will be using Android’s location service through LocationManager events.

Our application will try to use GPS (as preferred sensor) and Network location providers. We will implement the interface: android.location.LocationListener and the rest is pretty straightforward:

Get a handle to the service:

1 locationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE);

Let the service know we want to be notified by some provider (GPS/network), with some threshold (MIN_TIME, MIN_DISTANCE) on the target listener, which is an object of the class implementing LocationListener interface (in this case we refer to the current: this).

1 locationManager.requestLocationUpdates(provider, MIN_TIME, MIN_DISTANCE, this);

At some point, we should stop getting location events:

1 locationManager.removeUpdates(this);

The LocationListener interface will force us to implement the method we need here:

1 public void onLocationChanged(Location location) {  }

Now we have a location to send to SAMI!

Creating WebSocket connections

Connecting to the SAMI WebSockets API is also not too hard to do. You can get detailed documentation on how to use WebSockets from the documentation.

In this tutorial we’ll use a Java WebSockets library. You could either clone and build a JAR file to include in your project, or build it together with your source code.

Once you have the library in your workspace, building a WebSocket client is pretty simple. You can see the details in the source code. On top of your new bare-bones WebSocket client, we’re going to connect to the SAMI /websockets WebSocket API:

 1 private void connectWebsocketAndSendLocation(final Location location){
 2         final String messageLatLng = JsonUtil.getDeviceMessage(Config.DEVICE_ID, location);
 3 
 4         setWebsocketText(("Sending to SAMI:" + messageLatLng));
 5 
 6         if(websocket == null) {
 7             websocket = new Websocket();
 8         }
 9         if(websocket.isConnected()) {
10             websocket.send(messageLatLng);
11         }
12         else {
13             if(!websocket.isConnecting()) {
14                 websocket.connect(Config.WEBSOCKET_URL, new WebsocketEvents() {
15                     @Override
16                     public void onOpen(ServerHandshake handshakedata) {
17                         websocket.send(JsonUtil.getRegisterMessage(Config.DEVICE_ID, Config.ACCESS_TOKEN));
18                         websocket.send(messageLatLng);
19                     }
20 
21                     @Override
22                     public void onMessage(String message) {
23                         setWebsocketText(("onMessage(): "+ message));
24                     }
25 
26                     @Override
27                     public void onClose(int code, String reason, boolean remote) {
28                         setWebsocketText(("onClose(): "+ code));
29                     }
30 
31                     @Override
32                     public void onError(Exception ex) {
33                         setWebsocketText(("onError(): "+ ex.getMessage()));
34                     }
35                 });
36             }
37         }
38     }

What happens here?

First, we build a JSON payload with the location we want to send to SAMI. Something like:

(Replace the strings in the two examples below with a variable or the actual values.)

1 {"sdid": "<someDeviceID>", "data":{"lat": <latitude> ,"lng": <longitude>}}

If we’re already connected and the device is already registered in the connection, we send it right away.

If we’re not connected yet, then we connect, specifying on the WebSockets event listener that we want to send a “register” message first, and then the message that contains the location.

The register message looks like:

1 {"sdid": "<someDeviceID>", "Authorization": "bearer <deviceAccessToken>", "type": "register"}

Note: You can take a look on how to build JSON payloads at the file JsonUtil.java.

That’s it! We’re already sending our location to SAMI.

Data consumer: Get the location

Now… let’s display this data. For the sake of tutorial simplicity, we’re going to do this in the same application, but you could easily build a second application with this part and install it to another mobile phone, tablet or computer. Going real-time is a matter of connecting to the SAMI /live WebSocket API. This is almost exactly the same as connecting to /websocket. The following snippet shows the events handler:

 1 private void connectLiveWebsocket(){
 2         if(live == null) {
 3             live = new Websocket();
 4         }
 5         if(!live.isConnected() && !live.isConnecting()) {
 6             live.connect(Config.LIVE_URL, new WebsocketEvents() {
 7                 @Override
 8                 public void onOpen(ServerHandshake handshakedata) {
 9                     setLiveText("Live connected to from SAMI!");
10                 }
11 
12                 @Override
13                 public void onMessage(String message) {
14                     setLiveText("Live onMessage(): " + message);
15                     try {
16                         JSONObject json = new JSONObject(message);
17                         JSONObject dataNode = json.optJSONObject("data");
18                         if(dataNode != null) {
19                             lastLat = dataNode.getString("lat");
20                             lastLng = dataNode.getString("lng");
21                         }
22                     } catch (JSONException e) {
23                         // This message doesn't contain the data nodes we're looking for, might be a ping.
24                     }
25                 }
26 
27                 @Override
28                 public void onClose(int code, String reason, boolean remote) {
29                     setLiveText("Live closed: " + code);
30                 }
31 
32                 @Override
33                 public void onError(Exception ex) {
34                     setLiveText("Live error: " + ex.getMessage());
35                 }
36             });
37         }
38     }

We connect and watch for messages that contain the fields lat and lng, which are the expected fields for the device type we’re using here. We copy the values to member variables for further usage in a map. That’s pretty much it!

Displaying the marker in a map

In a typical map application, the correct way of doing this would be to have a Google Maps key and make use of the API to set the marker. Here we focus on the SAMI part, so again for simplicity, we’re going to set it the same way you would do in a web browser, typing the coordinates in the URL:

 1 public void whereIsThis(View v){
 2         String url = Config.MAPS_URL + lastLat + "," + lastLng;
 3             if(lastLat.length() > 0 && lastLng.length() > 0) {
 4                 try {
 5                 webView.loadUrl(url);
 6             } catch (Exception e) {
 7                 e.printStackTrace();
 8             }
 9         } else {
10             // Maybe show a "No location available yet" sign.
11         }
12     }

The result can be seen in the following image:

Map with marker

Where have I been? Show me the route!

For the historical visualization, we can use either the SAMI /messages or /messages/last API. Because in this case we’re purely interested in the latest 100 positions reported by our device, we’re going to use the /messages/last API.

Building an HTML page to do this with JavaScript is something simple. Here’s the JavaScript code:

 1 var sdid = <TheDeviceID>;
 2         var accessToken = <TheAccessTokenForTheDevice>;
 3         var mapCenter;
 4  
 5         // Query SAMI API for the latest 100 places sent by the app's device
 6         function getMessagesLast(){
 7             $.ajax({
 8                 url: "https://api.samsungsami.io/v1.1/messages/last?sdids="+sdid+"&count=100”,
 9                 dataType: 'json',
10                 type: "GET",
11                 beforeSend: function(xhr){
12                     xhr.setRequestHeader('Authorization', 'bearer '+accessToken);
13                     xhr.setRequestHeader('Content-type', 'application/json');
14                 },
15                 success: function(result) {
16                     var legs = [];
17                     $.each( result.data, function( index ) {
18                         var latLng = result.data[index].data;
19                         if(index==0){
20                             mapCenter = new google.maps.LatLng(latLng.lat, latLng.lng);
21                         }
22                         legs.push(new google.maps.LatLng(latLng.lat, latLng.lng));
23                     });
24                     initialize(legs);
25                 }
26             });
27         }
28 
29         // Init the map using as center the first point returned by SAMI
30         function initialize(legs) {
31             var mapProp = {
32                 center: legs[0],
33                 zoom: 18,
34                 mapTypeId: google.maps.MapTypeId.ROADMAP
35             };
36 
37             var map=new google.maps.Map(document.getElementById("map_canvas"),mapProp);
38 
39             var devicePath=new google.maps.Polyline({
40                 path:legs,
41                 strokeColor:"#0000FF",
42                 strokeOpacity:0.8,
43                 strokeWeight:2
44             });
45             devicePath.setMap(map);
46         }
47 
48         // Code starts here
49         $(document).ready(function() {
50             getMessagesLast();
51         });

This code does the following:

  1. Calls the SAMI API /messages/last (using as authentication header the access token we have), passing our current device ID and the message count to limit the items in the result.
  2. Build an array of Gmaps LatLng objects using the lat and lng fields from the SAMI messages.
  3. Initialize and draw a polyline in the map, using the array as source for the path.

You can store this HTML page in a computer and browse it, or, more interestingly, you can bundle it into the application assets and pass the device ID and access token variables from our app’s Java to the page’s JavaScript, to finally display it in a WebView. You can use the addJavascriptInterface method of a WebView object to let JavaScript call Java code as follows:

Define a class that will be used as interface:

 1 static final class JavaScriptInterface {
 2         JavaScriptInterface () { }
 3         @JavascriptInterface
 4         public String getAccessToken() {
 5             return ACCESS_TOKEN;
 6         }
 7         @JavascriptInterface
 8         public String getDeviceId() {
 9             return DEVICE_ID;
10         }
11     }

Pass an object of this class to the WebView:

1 webView.addJavascriptInterface(new JavaScriptInterface(), "jsinterface");

Obtain the values from JavaScript using the added interface:

1 var sdid = window.jsinterface.getDeviceId();
2 var accessToken = window.jsinterface.getAccessToken();

The result can be seen in the image:

Map with polyline

Conclusion

As you can see, working with SAMI is really easy and fast. The first obvious improvement here would be to draw a nicer map using the Directions API. You could also move the LocationManager code to an Android service. The list is probably endless, but at least, now you know how to have fun with SAMI WebSockets!

Top image: raph.ae/

Get the ARTIK Newsletter

You like your news fresh! Sign up now and you will be the first to know about our latest software releases, coding tips, upcoming events, blog posts, datasheet updates, and more.

* By checking either box, you may receive notifications by phone, email, text, and/or other electronic means from Samsung Semiconductor, Inc. and its affiliates. If you choose to receive partner notifications, we may forward your contact information to our partners. You may unsubscribe from these services at any time by clicking on the unsubscribe link in our communications or by submitting a request here. Please see our Privacy Policy and Terms of Use for more information about how your data is stored and used.