Tutorial hero
Lesson icon

Converting a Native Android App to Sencha Touch: Part 5

Originally published March 18, 2014 Time 8 mins

logo

It’s finished! If you’re not familiar with this series, I’ve been working on converting a native Android mobile application I created a little while ago into Sencha Touch. You can follow along from the start here.

First up, if you want to check out the final product you can find a download link here. I’ve released it for free on both iOS and Android. Please give it a try and let me know what you think or if you manage to break it somehow (I think the Android version is a bit clunkier than iOS at the moment).

In this post I’ll discuss the rest of the process of building the application including how I solved the problems I was having with SoundCloud and how I implemented the GPS / Geolocation feature to track how far the user has run.

I’ll likely make another summary post in the near future. Since the features have changed quite a bit, it may be a little silly to do a before and after comparison between the native and web approach. There is certainly some interesting things to talk about though and I’d also like to see how differently users react to each version.

Trouble with SoundCloud

If you’re following along, you will have seen in my last post that I was having some trouble with SoundCloud when I installed it on a phone. Everything seemed to be working fine when the application was being run through the browser, but when I installed the application with PhoneGap it could no longer retrieve or playback tracks from SoundCloud.

Fortunately, the solution was quite simple but unfortunately it took excruciatingly long to figure out the problem. It all came down to two things that I was doing wrong.

The first problem was that the following file also needed to be included:

<script
  type="text/javascript"
  src="http://connect.soundcloud.com/sdk.js"
></script>
<!-- This one -->
<script
  type="text/javascript"
  src="http://connect.soundcloud.com/soundmanager2/soundmanager2.js"
></script>

(Note: As far as I’m aware these files have to be included remotely)

And the other was simply that you need to use the full address to the API when it is installed (for whatever reason). So I had to use:

SC.get(
  'https://api.soundcloud.com/tracks/',
  { genres: query, bpm: { from: 150 }, filter: 'streamable' },
  function (tracks) {
    //...
  }
);

Instead of:

SC.get(
  '/tracks/',
  { genres: query, bpm: { from: 150 }, filter: 'streamable' },
  function (tracks) {
    //...
  }
);

These two changes finally allowed music to be played when installed on a phone, and what a relief that was. Annoyingly though, these same changes stop it from working in the browser.

Tracking distance with Geolocation

The final piece of the puzzle was implementing HTML5 Geolocation service. In a perfect world all I would be required to do is grab the users current location when they start the timer, and grab their location again when they stop the timer. The distance in between would be how far they have run.

But, people don’t often just run in straight lines. I’m sure someone would be more than a little disheartened if they did 10 laps of an oval and were told they had run 0kms.

The solution to this is to periodically grab their location throughout the run, work out the distance between two close points and add that to a running total.

First, make sure you are including the geolocation plugin:

<gap:plugin name="org.apache.cordova.geolocation" />

if you are using PhoneGap Build. To start the service, I just added the following code to my launch function:

var options = {
  timeout: 15000,
  frequency: 3000,
  enableHighAccuracy: true,
  maximumAge: 90000,
};
navigator.geolocation.watchPosition(
  runtap.util.gps.onSuccess,
  runtap.util.gps.onError,
  options
);

watchPosition allows you to track the users movement by periodically grabbing their position with the frequency specified. As you can see, it’s calling functions in a utility file I created. Each time the geolocation is triggered, it will run the onSuccess function. Here’s what that function looks like:

onSuccess: function(position) {

     //Check if we have a previous location
     if(this.lastLatitude != null)
     {
               //Calculate the distance between the new and old points
               var R = 6371;
               var dLat = (position.coords.latitude-this.lastLatitude) * (Math.PI/180);  // deg2rad below
               var dLon = (position.coords.longitude-this.lastLongitude) * (Math.PI/180);
               var a =
               Math.sin(dLat/2) * Math.sin(dLat/2) +
               Math.cos(this.lastLatitude * (Math.PI/180)) * Math.cos(position.coords.latitude * (Math.PI/180)) *
               Math.sin(dLon/2) * Math.sin(dLon/2);
               var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
               var distance = R * c; // Distance in km
               this.lastLatitude = position.coords.latitude;
               this.lastLongitude = position.coords.longitude;

               //This is somewhat of a hack, see explanation below
               if(this.cycles < 5)                {                     this.cycles++;                }                else if(runtap.globals.running && distance > 0.0005)
               {
                    runtap.globals.totalDistance = runtap.globals.totalDistance + distance;
               }

          //Set timer HTML to total distance
          var tracker = Ext.ComponentQuery.query("timer #gps")[0];
          var value = Math.round(runtap.globals.totalDistance * 100) / 100;
          tracker.setHtml(value + "<span style="font-size: 18px;">km</span>");

     }
     //Record the first set of coordinates
     else
     {
          if(!isNaN(position.coords.latitude))
          {
               this.lastLatitude = position.coords.latitude;
               this.lastLongitude = position.coords.longitude;
          }

          runtap.globals.totalDistance = 0;

          //Set timer HTML to total distance
          var tracker = Ext.ComponentQuery.query("timer #gps")[0];
          var value = Math.round(runtap.globals.totalDistance * 100) / 100;
          tracker.setHtml(value + "<span style="font-size: 18px;">km</span>");
     }

}

Most of this code is pretty self explanatory apart from a couple of things. First, I’m measuring how many “cycles” have occured, or, how many times the latitude and longitude have been updated previously. It seemed that whenever the GPS started, the distance would immediately jump up about 200-300 meters before it would settle. To avoid this, I only started adding up the distance once it had already run a few times. I’m not sure if this was the correct approach to this or why it happens but it seemed to work.

The other is that I’m only adding the change in distance to the running total if it is above a certain amount. Even sitting still, there are very small variations in the coordinates which cause small distances to be recorded constantly. Since I’m consistently adding this to a running total, the distance would slowly climb whilst sitting still – this solves that problem.

That just about brings this series to an end! I hope you’ve enjoyed following along and if you have any questions or comments please leave them below – I’d love to hear from you.

PS: Don’t forget to take a look for yourself!

Learn to build modern Angular apps with my course