I’ve been working on a startup company called MileTrack GPS for the past 3 years. It’s designed to eliminate the pain and frustration of business mileage tracking.

I developed this first and foremost for myself. I’m a software development consultant and was tracking my mileage by hand while driving around town working for various companies. Writing down mileage in a log book was painful.

Back when I started this project, Adobe Flex was the pinnacle of Rich Internet applications. This website spawned from those glory days. While the flash platform will live on and still be useful for many applications, for my purposes, it’s deprecated. The maps APIs for Flex I’ve relied on are steadily becoming deprecated. As such, I’ve taken on the task of moving to a JavaScript framework called Angular JS.

The new webapp is about 90% completed. You can view a demo of it below.

Angular JS has some similar features to Flex in that it does databinding. It’s not as easy to use or straightforward as the Flex implementation, but for my purposes, it works. I also now have access to the latest Google JS maps apis. This is especially important for being able to run the webapp on tablet computers.

If you have a chance, check out Angular JS

Post to Twitter

Posted by Andrew, filed under Flex, GPS, JavaScript. Date: January 13, 2014, 3:36 pm | 1 Comment »


What follows is a very brief synopsis of my experience developing an app for Android and then porting it to iOS. Please remember that my opinions are but a single data point. After you’ve remembered that, feel free to promptly forget it and start the flame wars in the comments section. If my coding skills are lacking and I’m missing a simple way to do something I claim is impossible, feel free to let me know that too.

The App

MileTrack GPS is an app that tracks mileage driven for IRS reimbursement purposes. It uploads any trips you take to the miletrackgps.com website where you can then categorize, map, and label trips. It then allows you to generate PDF reimbursement reports. I’ve been marketing the app as “Pain free mileage tracking” because I want the app to be completely brainless. You get into the car, plug in your phone, drive, arrive, unplug. Nothing else, the app should just do its thing.

Challenge 1: Launch App in Background when device is plugged in

For the app to perform properly, it needs to launch and run in the background when it’s plugged in. I can then start monitoring location services for movement so I can start tracking a new trip.

Android

In Android, this is accomplished by first registering a receiver in AndroidMainfest.xml inside the application tag.

<receiver android:name=".PowerBroadcastReceiver">
  <intent-filter>
    <action android:name="android.intent.action.ACTION_POWER_CONNECTED" />
    <action android:name="android.intent.action.ACTION_POWER_DISCONNECTED" />
  </intent-filter>
</receiver>

Then, in the PowerBroadcastReceiver java class, I can fire up an Intent to my GPS service that will notify it of the event so it can take proper action. Either start listening for movement and thus beginning a new trip, or uploading the current trip if power was just disconnected.

package com.swiftmako.miletrack;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;

public class PowerBroadcastReceiver extends BroadcastReceiver {

  private static String TAG = "MileTrackGPS";
   
  public static SharedPreferences pm = null;

  @Override
  public void onReceive(Context context, Intent intent) {
    Log.d(TAG, intent.getAction());
       
    if(pm == null) {
      pm = PreferenceManager.getDefaultSharedPreferences(context);
    }
       
    boolean automaticTracking = pm.getBoolean("automaticTracking", false);
       
    if(automaticTracking) {
      if(intent.getAction().equals("android.intent.action.ACTION_POWER_CONNECTED")) {
        Intent serviceIntent = new Intent("com.swiftmako.miletrack.intent.action.GPS");
        serviceIntent.putExtra("POWER_STATE", "CONNECTED");
        context.startService(serviceIntent);
      }
      else {
        Intent serviceIntent = new Intent("com.swiftmako.miletrack.intent.action.GPS");
        serviceIntent.putExtra("POWER_STATE", "DISCONNECTED");
        context.startService(serviceIntent);
      }
    }
  }

}

iOS

In Apple’s infinite wisdom, they decided to not allow apps to be launched based on a system event like connecting a charger. You can be notified if you’re already running, but if you’re not running, no such luck. According to this apple doc, it does look like if you add the UIBackgroundModes key voip, your app can be launched into the background after system startup. This would most likely get our app rejected since we’re not really a Voip app. Another way to do it is by requiring the user’s phone be jailbroken. Doesn’t seem like a good solution to me though.

WINNER: Android

Challenge 2: Track movement using GPS

The most important part of our app is getting good data from the GPS device built into the smartphone. If we get inaccurate data, then the purpose of our app is rendered impotent.

Android

In our AndroidManifest.xml, we first ask for permission to use GPS.

  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

We then ask for a LocationManager instance in our GPSService’s onCreate() method.

  lm =  (LocationManager)getSystemService(LOCATION_SERVICE);

We can then create a LocationListener to notify us of location events once every second. The accuracy we get from this method is good. We can rate “fair” as anything under 15 meters of accuracy and record those points. Everything above that should be ignored.

  private void startGPSListener() {
    if(lm.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
      locationListener = new LocationListener() {
       
        public void onStatusChanged(String provider, int status, Bundle extras) {
        }
       
        public void onProviderEnabled(String provider) {
          sendClientMessages(MSG_ACCURACY_CHANGED, getText(R.string.gps_accuracy_srch));
        }
       
        public void onProviderDisabled(String provider) {
          sendClientMessages(MSG_ACCURACY_CHANGED, getText(R.string.gps_accuracy_off));
        }
       
        public void onLocationChanged(Location location) {
          if(location.hasAccuracy()) {
            if(location.getAccuracy() < 5) {
              sendClientMessages(MSG_ACCURACY_CHANGED, getText(R.string.gps_accuracy_excellent));
            }
            else if(location.getAccuracy() < 10) {
              sendClientMessages(MSG_ACCURACY_CHANGED, getText(R.string.gps_accuracy_good));
            }
            else if(location.getAccuracy() < 15) {
              sendClientMessages(MSG_ACCURACY_CHANGED, getText(R.string.gps_accuracy_fair));
            }
            else if(location.getAccuracy() < 20) {
              sendClientMessages(MSG_ACCURACY_CHANGED, getText(R.string.gps_accuracy_poor));
            }
            else {
              sendClientMessages(MSG_ACCURACY_CHANGED, getText(R.string.gps_accuracy_bad));
            }
           
            writeTrackPoint(location);
          }
        }
      };
     
      lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 1, locationListener);
     
      isListening = true;
     
          // Display a Toast notification about us starting.
          Toast.makeText(this, R.string.gps_service_started, Toast.LENGTH_SHORT).show();
         
          listenerStartTime = System.currentTimeMillis();
         
          Log.d(TAG, "startGPSListener");
    }
    else {
      //show notification about GPS being disabled
      sendClientMessages(MSG_GPS_DISABLED, null);
    }
  }

iOS

In Obj-C, we first setup our info.plist file to tell it what kind of capabilities we expect the device to have.

  <key>UIRequiredDeviceCapabilities</key>
  <array>
    <string>gps</string>
    <string>location-services</string>
    <string>armv7</string>
  </array>

Next, we get the CLLocationManager and tell it to start updating the location. The manager in iOS differs from Android in that you can’t specify a set period of time to get updates.

    //initialize location tracking
    locationManager = [[CLLocationManager alloc] init];
   
    //set ourselves as the locationmanager delegate
    [locationManager setDelegate:trackViewController];
   
    //get all results
    [locationManager setDistanceFilter:kCLDistanceFilterNone];
       
    //be as accurate as possible no matter the battery life
    [locationManager setDesiredAccuracy:kCLLocationAccuracyBest];
   
    [locationManager startUpdatingLocation];

We have to do quite a bit of work to get decent locations out of iOS. Even then, the speeds (both calculated by us OR calculated by iOS) are old and inaccurate. Most of this code was taken from a stackoverflow post in trying to get decent data out of iOS. We can get decent location tracking out of this. Speed tracking remains inaccurate though.

- (void)locationManager:(CLLocationManager *)manager
    didUpdateToLocation:(CLLocation *)newLocation
           fromLocation:(CLLocation *)ignoredOldLocation
{
    static int attempts = 0;
    static CLLocation *oldLocation = nil;
    BOOL locationChanged;
   
    NSDate* eventDate = newLocation.timestamp;
    NSTimeInterval howRecent = abs([eventDate timeIntervalSinceNow]);
    attempts++;
   
    double hdop = [newLocation horizontalAccuracy];
   
    if(hdop < 0) {
        //invalid fix
        return;
    }
    if(hdop < 10) {
        [self setAccuracy:@"Excellent"];
    }
    else if(hdop < 50) {
        [self setAccuracy:@"Good"];
    }
    else if(hdop < 75) {
        [self setAccuracy:@"Fair"];
    }
    else if(hdop < 150) {
        [self setAccuracy:@"Poor"];
    }
    else {
        [self setAccuracy:@"Bad"];
    }
   
    if((newLocation.coordinate.latitude != oldLocation.coordinate.latitude) || (newLocation.coordinate.longitude != oldLocation.coordinate.longitude))
        locationChanged = YES;
    else
        locationChanged = NO;
   
#ifdef __i386__
    //Do this for the simulator since location always returns Cupertino
    if (howRecent < 5.0)
#else
    // Here's the theory of operation
    // If the value is recent AND
    // If the new location has slightly better accuracy take the new location OR
    // If the new location has an accuracy < 50 meters take the new location OR
    // If the attempts is maxed (5) AND the accuracy < 75 AND the location has changed, then this must be a new location and the device moved
    // so take this new value even though it's accuracy might be worse
    if ((howRecent < 5.0) && ( (newLocation.horizontalAccuracy < (oldLocation.horizontalAccuracy - 10.0)) || (newLocation.horizontalAccuracy < 50.0)
                              || ((attempts >= 5) && (newLocation.horizontalAccuracy <= 75.0) && locationChanged)))
#endif            
    {
        attempts = 0;
       
        currentLocation = newLocation;
       
        if(lastTrackedLocation == nil && isLoggingStarted)
        {
            lastTrackedLocation = newLocation;
        }
       
        double distance;
        if(oldLocation != nil) {
            distance = [newLocation distanceFromLocation:oldLocation];
            double time = [[newLocation timestamp] timeIntervalSinceDate:[oldLocation timestamp]];
           
            if(distance > 0.0 && time > 0.0) {
                double speedMs = distance / time;
                double mph = speedMs * MS_TO_MPH;
               
                NSLog(@"Timestamp: %@, lat: %f, lon: %f, speed: %f mph, hdop: %f", [newLocation timestamp], [newLocation coordinate].latitude, [newLocation coordinate].longitude, mph, hdop);
               
                //this value is usually old and not accurate
                //[self setCurrentSpeed:[newLocation speed]];
               
                if(mph >= 0.0) {
                    [self setCurrentSpeed:mph];
                }
            }
        }

        oldLocation = newLocation;
       
        if(lastTrackedLocation != nil && isLoggingStarted) {
            distance = [newLocation distanceFromLocation:lastTrackedLocation];
            if(distance > MINIMUM_TRACKPOINT_DISTANCE) {
                totalDistance += distance * METERS_TO_MILES;
                [self distanceChanged];
                lastTrackedLocation = newLocation;
                [self writeTrackPoint: newLocation];
            }
        }
        else if (isLoggingStarted) {
            lastTrackedLocation = newLocation;
            [self writeTrackPoint: newLocation];
        }
    }
    else {
        NSLog(@"howRecent: %f, HDOP: %f, locationChanged: %d", howRecent, hdop, locationChanged);
    }
}

WINNER: Android

Challenge 3: Notify the User

Our app needs a way to notify the user that we’re tracking a trip, that we’re uploading a trip, and that we’ve finished uploading a trip. This needs to happen because our app will run in the background most of the time.

Android

In Android, we just get an instance of the NoficationManager in onCreate() of our upload service.

  nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);

We can then use the nofication manager to add our icon to the try to let the user know that an upload has succeeded.

/**
 * Show a notification that we're done uploading.
 */

 private void showSuccessNotification(String message) {
    // In this sample, we'll use the same text for the ticker and the expanded notification
    CharSequence text = message + getText(R.string.upload_track_success);

    // Set the icon, scrolling text and timestamp
    Notification notification = new Notification(R.drawable.success_128, text, System.currentTimeMillis());
    notification.flags = Notification.FLAG_ONLY_ALERT_ONCE | Notification.FLAG_AUTO_CANCEL;
       
    // The PendingIntent to launch our activity if the user selects this notification
    PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);

    // Set the info for the views that show in the notification panel.
    notification.setLatestEventInfo(this, "SUCCESS", text, contentIntent);

    // Send the notification.
    // We use a string id because it is a unique number.  We use it later to cancel.
    nm.notify(R.string.upload_track_success, notification);
}

iOS

In iOS, notifications work very similarly. Only Apple is allowed to show a title bar icon though. Your local notifications are silently ignored if the app is running in the foreground.

uhh, no code for this one right now because I haven't implemented it yet.  It looks pretty straightforward though from the docs.

WINNER: iOS

Summary

The architecture of Android is just plain BETTER. They don’t restrict what you can do and there always seems to be a good tutorial to follow telling you exactly how to do the exact crazy thing that you want to do. As far as the languages go, I don’t really have a preference for Obj-C vs. Java. They both do the job pretty well. I still don’t quite have my head wrapped around ARC, but that’s a post for another day. I hope you enjoyed this little rant. Flame on people!

Post to Twitter

Posted by Andrew, filed under Android, GPS, iOS, Java, Obj-C. Date: July 5, 2012, 3:46 pm | 6 Comments »

MileTrack GPS is pain-free business mileage tracking. The gist is that you track your mileage driven using a smartphone or GPS device, upload to the service, and later generate a mileage expense reimbursement report. If you’re interested, we offer a 60-day free trial to test out the system. Head on over to miletrackgps.com to check it out. I’ll also be blogging over there a bit more in the future, so make sure to add that blog to your RSS Feed. We’re on twitter as well @MileTrackGPS

I’ve been working on this project in some way/shape/form for a year and a half now. It started as an electronics project where I was just working on a hardware GPS device. It later morphed into a software project as well once I realized that I couldn’t just sell a piece of hardware alone without competing against really cheap knockoffs manufactured in China.

So, miletrackgps.com was born to solve a pain I had in my life. I wrote a bit about solving this pain with a process over on ourstartupstory.com.

The web front-end is designed of course, with Flex 4.5. The backend is using Java, Spring, GraniteDS, Hibernate, Amazon RDS, all running on the Amazon Elastic Beanstalk. Everything is built with Flex-mojos and maven.

We just released our FREE Android App for tracking mileage to the Google Market. This was developed using Titanium Appcelerator. I blogged a bit about my experience over at riarockstars.com about why I chose that platform for mobile instead of Adobe AIR. Even if you have no real business need for tracking mileage, please give our service a try for the fun of it and send feedback to help us improve the service.

Post to Twitter

Posted by Andrew, filed under Android, Flex, GPS. Date: June 8, 2011, 7:00 am | No Comments »

I recently had an opportunity to give a talk at 360|Flex along with Randy Troppmann. Slides and code examples are posted below. Video of the session will come sometime in the unforeseen future as they have many hours of other sessions to post-process before getting to mine.

Presentation PDF (warning: large)

MileageBuddy (viewsrc enabled)

HeatmapExample (viewsrc enabled)

Test GPX file

Post to Twitter

Posted by Andrew, filed under 360 Flex, AIR, as3, Data, Degrafa, Flex, GPS. Date: March 11, 2010, 5:04 pm | No Comments »

I’m slated to speak at 360|Flex San Jose on March 7-10 on the topic of GPS data and doing neat things with it in Flex/AIR. I’ll be co-presenting along with Randy Troppmann. Randy had been doing work for quite awhile in the GPS space with his work on runningmap.com. Runningmap.com is a website that allows runners/walkers/joggers/movers to track and catalog their accomplishments. They’ve also developed an iPhone application that can record your gps track as you run. It’s quite an honor to be able to present with such an accomplished developer.

I’ve been holding off on doing a blog post about 360|Flex until I could give you a sneak peak of the new prototypes of Swift GPS. Swift GPS is a wearable hardware device that will allow you to record GPX files to a USB thumbdrive that tracks your current position and movement. During my part of this session, we’ll go into how to take this GPX data and do neat things with it in Flex like overlay the data onto a 3D map. Other highlights will be doing a bit of heat-mapping using the GPS data.

This conference WILL SELL OUT, so don’t be one of the hundreds of chumps posting regrets to your twitter account after the conference starts and you didn’t get in. Click on my 360|Flex badge on the right-side of the blog to sign up today. If I win the speaker suite for the most signups through my blog badge, I’ll give away a prototype device to one of the people who helped put me over the top. Don’t wait. Sign up today.

Post to Twitter

Posted by Andrew, filed under 360 Flex, Data, Flex, GPS. Date: February 9, 2010, 8:42 pm | No Comments »

swift_gps_bank_trip

I’ve worked out the majority of the kinks with Swift GPS, so I’m calling it in Alpha right now.  To give you a bit more information about the vision for this product, it will be a GPS track data logging device.  You stick a USB thumb drive in it, and it will track the movement of your car, wife’s car, dog, RC aircraft, whatever you want to attach it to.  The target market will be developers or GPS hobbyists, or geocachers who want a real challenge.  It outputs a GPX data file which is nothing more than a special XML file format for GPS data.  This should be useful for developers since it’s an open standard.

Click the image above for a screencast of my magnificently exciting trip to the bank. (Warning, screen recordings of Google Earth are HUGE).

I still have a few features to add such as adding a little screen so geocachers and others can view the raw lat/lon data in real-time.  I ordered a book from amazon that should help me with writing the microcontroller code necessary for that piece.

This will probably be my last GPS post for quite awhile.  I plan on going back to your regularly scheduled “Flex Junk” in the near future.

This blog’s comment section has been pretty quiet for some time.  Any comments on this project would be appreciated.  Even if it’s just to say, “Go back to posting on Flex topics dude!”

Post to Twitter

Posted by Andrew, filed under C++, GPS. Date: March 10, 2009, 8:55 pm | No Comments »

01  Mar
First GPS data

gps_route

Well, my GPS device is coming along.  I’m getting my first batch of data off the board that is close to usable. I walked it to the end of my block and back at night with all the lights blinking and flashing on the board.  The neighbors probably think I’m a terrorist building a bomb or something.  Looks like I need to add a bit of code to ignore points whose accuracy is in question.  The start of my walk was all over the place (in my neighbors living room if you believe the device), but by the end, it seemed to hone in and nail down my position pretty well.

I’m targeting this device to output GPX data which should be an easily readable XML format for Flex/AIR developers to use.  Right now, the screenshot you’re viewing is from Google Earth which also accepts GPX data files.

Post to Twitter

Posted by Andrew, filed under AIR, Flex, GPS. Date: March 1, 2009, 10:13 pm | No Comments »

My Cluttered Desk

My Cluttered Desk

I’ve started a new project. It’s quite a bit different than the Flex/AIR stuff I’ve been posting about in the past. My day job is still doing Flex/AIR, so consider this a minor interruption in your regularly scheduled programming.

In my home office, my desktop is now cluttered with the tools of the trade. I’ve figured out how to wire a demo GPS board to my microcontroller development board. The whole mess is wired to my laptop using a USB cable. I write C code on the laptop that I can push down to the microcontroller for testing and debugging. The first major hurdle has been cleared which is getting the devices talking to each other.

I hope to post updates every so often on the project. In some ways, I guess it’s somewhat Flex/AIR related since the device (when released) will eventually target other developers who can do some interesting things in their software with it.

Post to Twitter

Posted by Andrew, filed under GPS. Date: February 15, 2009, 10:29 pm | No Comments »