Ok Android, Broadcast My Location

Introduction and Background

So, I have got admission for my MCS classes in a (far off) university, and my parents always force me to send them a text or call them to notify them of my current location, where I am, what are the current stats, whether I got the bus or not and so on. That was not a trouble, the trouble was that I usually keep listening to Eminem and so I forget to inform them where am I and so on.

To solve the riddle, I thought I should make my Android a bit useful for myself and notify my parents by a text message, where currently I am! This article does not only focus on creating that one simple application, but also focused on the LocationManager, LocationListener, PendingIntent and a few other concepts in Android programming that you may want to understand to build other similar applications that require GPS or Network based location services and to notify the clients.

The application also uses SmsManager, the service that provides you with functions to send SMS messages using the client's network, rates apply. I hope the article will interest you and it may be catchy for you.

Construction and Concept

The concept for this application is just as easy as a listener and a broadcaster, although direct broadcasts won't be used, but the concept is similar to that one. Plus, once we are done we will be able to plug the SmsManagerto our application to send SMS messages to the ones we are interested in.

Here it goes:

  1. LocationManager for managing everything that a location-aware application needs.

  2. A listener that may work for our application to execute a code when we are being notified about any changes in the location. Now this point is divided into two more components and I will talk about them in details later.

  3. An object that gets triggered and notifies our clients. The object can be anything, in my case I am going to use SmsManager to send the SMS messages to the people that I want to receive those notifications.

This way, I will be able to broadcast the location updates to the ones that I want to be notified.

Demonstration of our requirement and work around
Figure 1: Demonstration of our requirement and work around. Explains what happens and what objects are being used in this demonstration.

Since this is a general and unit overview, you can easily change the implementation to suit your own needs. For example, you can change how you notify the clients. You can remove the SmsManager and implement your own API, send the details to an online cloud, transmit over to a web service, store the location on your own device or what ever! That is all a good side of this article because I will not hard code everything in one activity but instead I will try to provide you with multiple services classes and functions that can be ported for other usages, other functions, other services and other implementations.

Read the rest of the article to see how easy it is to implement the location awareness in your Android applications and how easy it is to actually share that data with clients or do what ever you want to do.

Understanding the Android Location APIs

First of all, I would like to teach you the basics about Android location APIs. Location APIs have been added to Android devices since the beginning, not the API 1 but quite a few versions later. Location APIs includes provider objects, managers, addresses, geocoders and so on. Each of them are categorized under a similar package and developers can use them in their applications to make a good use of location services in applications and developing location aware applications. Ever wondered, how did navigation application work? Keep wondering, or continue reading the post.

Android APIs are based on Java programming language, so the concept of those APIs is similar to what Java APIs have. Packages, objects, managers and then come listeners. In a parallel universe of C#, there are namespaces, objects, events and handlers. So, I hope you got the idea of what a LocationManager is and what aLocationListener is, didn't you?

LocationManager is a special object in location API which holds the information for location providers, location details, minimum time spans to check for location and a few other things. LocationListener on the other hand is the listener, handler, which is triggered when an update is ready for location. Putting it simply, for you: LocationManagers are requested to provide updates for location changes, and a LocationListenerobject is passed that will handled the change in location. LocationListeners are interested only to get executed when location is updated. They are not all the time holding a reference to GPS — GPS must be turned off periodically to save battery, more in Tips section — but are going to capture the location updates every 1 minute, or after user has moved 100m in any direction. This way, you can get notifications in application that the user has displaced from his previous location and you can proceed with what-so-ever you wanted to do with the new location.

Now here is the plot, in the following sections I will talk about these few objects and then I will continue to the programming section for application. I hope, I am clear with these technical aspects of Android location API programming.

LocationManager description

First comes the LocationManager object, no matter what type of application you are building, no matter how are you expecting to use the location, you are always going to use a LocationManager object in your application. LocationManager objects are built right in Android core and you can use them as a system-defined service. To consume this service you need to access the permissions first, in your manifest file enter the following:

Code

  1. <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />  
LocationManager provides you with the following two types of locations:

 

  1. Approximate location, (with accuracy of almost 200ft), ACCESS_COARSE_LOCATION.
  2. Accurate location (with accuracy of 20ft), ACCESS_FINE_LOCATION.

Mostly, you should consider using ACCESS_FINE_LOCATION, if your application however wants to access location with less precision you can consider ACCESS_COARSE_LOCATION type. Along with the accuracy, there is a difference between the battery juice required by both of them.

To create a new instance of this object, you do not call the new operator on it, instead you call the getSystemService and then cast it to a LocationManager. For example,

Code

  1. LocationManager manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);  
This would allow you to work with the manager object whose reference you are holding in your application. You can then request for location updates too.

Requesting location updates

This one is a bit tricky concept as it depends on your application's architecture and design too. You are not going to use a LocationListener if your application does not run in the background, like on a separate worker thread that continuously executes. LocationListeners are removed as soon as your application is closed. For that, PendingIntents are introduced for capturing location updates.

The function used is similar, but is overloaded and can accept either one of these objects. Now it is your duty to determine which one would suit your needs to capture the location updates. The sleeping application won't be able to capture the updates and you may miss the updates from user as he is moving from one location to another, in those cases a PendingIntent object helps. It can execute on its own separate thread and thus you can allow LocationManager to perform actions using a PendingIntent on your behalf.

Information: If you have ever read Android documentations, you will know that a PendingIntent is a special kind of Intent. It is an Intent that does not need your application to be active in order to perform some actions, you create an Intent and pass it to the system so that system does that work on your behalf as it was you doing that job! PendingIntent are like tokens, access keys, that you grant other applications or frameworks with, and they use them to mimic your application.

Code
  1. manager.requestLocationUpdate(PROVIDER, minMinutes, minMeters, handler);  
The code for handling the updates is same in both the cases, now you should understand what each of these variables are.

 

  1. Provider

    As a Java developer, you might have got the idea that a "String" constant comes here. Built-in values for these providers are,

    • LocationManager.GPS_PROVIDER, provides access to the location services using GPS satellites.
    • LocationManager.NETWORK_PROVIDER, provides location services using the network. Less accurate location is provided as compared to GPS satellites.

  2. minMinutes

    You can control how often does LocationManager update your handlers with an update of location. Set it to a value greater than 0, to get notified after a while. The value is in minutes.

  3. minMeters

    Another flag to get notified only if user has moved a few meters away from his previous location. Helps if you want to notify them after 100m or 1000m (1km) and so on.

  4. handler

    Now this is a conceptual point and you want to understand this. The handler is just the code that gets triggered when an update is ready for your application to work on.

    The handler comes in two shapes and sizes,

    • LocationListener
    • PendingIntent

    The handler details will be discussed in the next sections. Upto this point, LocationManager must be cleared as to what it is and what it provides us with.

LocationListener description

Firstly, I would like to explain the LocationListener object and how can we use it in theLocationManager to receive updates for location changes. LocationListener is just the interface that has the handlers (functions) that you can use in your own application to work with updates for location. Just like any other interface, you implement it and then override the functions that it has!

Now, for example, you create a new Java class, SampleListener, and implement the LocationListener, you will have 4 additional functions that allow you to manage the location services in your application. Have a look at the following code:

Code

  1. package com.afzaalahmadzeeshan.mylocation; // Package name  
  2. // Some required imports  
  3. import android.location.Location;  
  4. import android.location.LocationListener;  
  5. import android.os.Bundle;  
  6. // Entire class structure with empty functions.  
  7. public class SampleListener implements LocationListener  
  8. {@  
  9.     Override  
  10.     public void onLocationChanged(Location location)  
  11.     {  
  12.         // Gets executed once location change has been notified to application.  
  13.     }@  
  14.     Override  
  15.     public void onStatusChanged(String provider, int status, Bundle extras)  
  16.     {  
  17.         // Status of location provider has been changed  
  18.     }@  
  19.     Override  
  20.     public void onProviderEnabled(String provider)  
  21.     {  
  22.         // Provider enabled by user  
  23.     }@  
  24.     Override  
  25.     public void onProviderDisabled(String provider)  
  26.     {  
  27.         // Opposite of onProviderEnabled(String)  
  28.     }  
  29. }  
Now a bit of explanation to these functions would suffice the purpose of this section. Before I go any further, the purpose of LocationListener is just to receive the notifications from LocationManager about changes in users' location nothing else!

 

  1. onLocationChanged(Location)

    This is the first function in the above list of available functions that we can use to get notified about changes in location API. The function provides us with the new location that user is currently holding, the new location is passed as a parameter for our function.

    This function get executed each time an update is available for our application. You can use it as the base for your application's business logic and execute the tasks here, like re-drawing the UI, updating the databases and so on.

  2. onStatusChanged(String, int, Bundle)

    This function is executed when the status for a provider is changed from available to not-available and so on and so forth. The function is passed with information about the provider, status code and other details that might help in processing the change.

    • Provider

      Provider is, as already discussed, the service provider for location APIs. It can be GPS_PROVIDER or other ones that may provide you with location services. It helps you find out which provider has changed the status and so that you can work appropriately for that change.

    • Status

      This parameter determines what status it has now. Collectively with provider, it will let you find out which provider is active and which is down at the moment. It has the following values, using which you can determine how your application should continue receiving the updates and if none of them is available then it must notify the user about scenario:

      • OUT_OF_SERVICE
      • TEMPORARILY_UNAVAILABLE
      • AVAILABLE

    • Extras

      As already said, any other detail that may help in this function is added to an Android Bundle as is passed to the function.

  3. onProviderEnabled(String)

    This (and the later function) are triggered by user interaction. Like, when user has enabled the provider manually. You can use this function to reactivate the service and continue processing the location updates.

  4. onProviderDisabled(String)

    Just like the former one, this one also gets triggered when user interaction takes place and a provider is disabled. Use it to disable the services in your application for location.

This way you can manage how you get the updates and what happens when anything changes.LocationListener is a very easy and effective way of handling the location updates. But wait, there is also a downside for this. To handle location updates using LocationListener your application needs to be visible and active, as soon as your application is removed, listeners are also removed so you cannot capture the location activity from background, like a service.

In other words, your application no longer acts as a location capturing service but a "current location viewer" kind of app. If that is the case, then great, this listener is enough for you! But, if you want to get updates when your application is not running or is in background, and the main worker thread may not be available, then read the next section for PendingIntent. For background services, PendingIntent works perfectly and provides you with updates even if your application is closed, since PendingIntent executes on its own thread.

PendingIntent description

Now I will point out how you can use a PendingIntent along with this manager and capture updates for location in your application.In the previous section you were taught how to use listeners, in this one you will be using services and then receiving updates on those background services which don't need your application to be always active in front of the users.

Create a service

First step is to create a service and write the logic that needs to be handled when an update is ready for our application to work on. We need to create an IntentService in our application. IntentService allows us to have a function in which we can write the logic code that gets executed each time that service is started. By extending the object, you get to have the same features that this object has in it. Connecting the article to the source code, I created a service and named it, "BroadcastLocationService" and it is extendingIntentService.

Code

  1. package com.afzaalahmadzeeshan.mylocation;  
  2. import android.app.IntentService;  
  3. import android.content.Intent;  
  4. import android.content.Context;  
  5. import android.location.Location;  
  6. public class BroadcastLocationService extends IntentService  
  7. {  
  8.     public BroadcastLocationService()  
  9.     {  
  10.         super("BroadcastLocationService");  
  11.     }@  
  12.     Override  
  13.     protected void onHandleIntent(Intent intent)  
  14.     {  
  15.         sendMessage(getApplicationContext(), new LocationService(getApplicationContext()).getLocation());  
  16.     }  
  17.     private void sendMessage(Context mContext, Location location)  
  18.     {  
  19.         // Code here to broadcast the location  
  20.     }  
  21. }  
In the above class for our application, we are having two functions. One is inherited and other one is custom one, that we are creating our self. Now, consider this, when a new request is sent to this service to execute, it would execute the code under onHandleIntent(Intent) function. The Intent that needs to be handled is passed as a parameter.

How is that intent passed, you will learn in the next step. If you don't have any idea of PendingIntent, next section will give you one, so keep reading.

Creating an intent to run the service

We can create an intent that executes the service and does the job for our application, on the background. Notice that user no longers need to have the screen active on their device to use this method of capturing location updates.

Code
  1. Intent intent = new Intent(getApplicationContext(), BroadcastLocationService.class);  
The above line is enough to create the intent that needs to be triggered when a location update is present. But that is not enough, we now need to create a PendingIntent for this intent and wait for the manager to provide us with services and updates, as they are made available.

To create a new PendingIntent, write this,

Code
  1. PendingIntent pIntent = PendingIntent.getService(  
  2.    getApplicationContext(), // Get the context  
  3.    0// Request code  
  4.    intent, // The intent we created  
  5.    FLAG_CANCEL_CURRENT // A flag  
  6. );  
This now creates a PendingIntent token for us, that we can pass to other applications, services or objects and they can execute it later on our behalf, when we need it to be triggered.

Receiving the updates

Now, again the same topic... We are now going to pass the pIntent to our manager and wait in that service until the location updates are available. Same code would work in this scenario, but with a difference of the last parameter.

Code
  1. manager.requestLocationUpdates(GPS_PROVIDER, 101000, pIntent);  
For instance in the above code, I am accessing the location updates from GPS_PROVIDER after 10 minutes and only if user has moved 1000 meters; 1km. If that is the case, then pIntent would be triggered, which in turn would call the onHandleIntent function and would allow us to process the location and do what we want to do.

A little bit of SmsManager

Although this article is not trying to focus on hardcoded application, but was meant to provide you with an overview of broadcasting the location updates. SmsManager is just an example of such an API that can be used. You can create your own services, API libraries and so on to broadcast the location. Previous sections will remain same, it is just the way you want your location to be shared.

Getting an instance of SmsManager

To get an instance of SmsManager, you call the following code:

Code
  1. SmsManager manager = SmsManager.getDefault();  
Once that has been done, you can send the text messages or multimedia messages! That is also very much easy, just pass the destination number, text message and you're good to go. But for that your application need to have the following permission,

Code
  1. <uses-permission android:name="android.permission.SEND_SMS" />  
It would allow your application to send the messages.

Writing the application

While writing this section, I am thinking about what to write because the main things have already been discussed. In this section, I will show you how to match up everything and create a running Android application. First step, is to create a manager and to allow the user to enter the details for the service updates. We need to allow the user to be able to provide us with those values, in my application I had created 2 EditText fields to get the values, or otherwise I had set up a few defaults for the flags.

Main page input fields for time interval
Figure 2: Main page input fields for time interval and the minimum meters moved

Also, we want the user to control when is the service running and when is it turned off. This way he can manage when his resources are being used and when they are not being wasted. GPS, SMS and all similar resources consume battery too, battery is also a resource and must be considered wisely. So, for that I had created another CheckBox object to hold the key to be used to activate or deactivate the service.

Disabled service interface on the main activity
Figure 3: Disabled service interface on the main activity

It is also a good UX to show user the details about the service and what is going on underground in the application. For example, we are going to send location updates to a number of contacts and based on a few checks. So it would be wise to show those details on the screen. Like the following one,

Android application displaying
Figure 4: Android application displaying the details about our service

The XML for the above Android UI would be like this,

Code
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.                 xmlns:tools="http://schemas.android.com/tools"  
  3.                 android:layout_width="match_parent"  
  4.                 android:layout_height="match_parent"  
  5.                 android:paddingLeft="@dimen/activity_horizontal_margin"  
  6.                 android:paddingRight="@dimen/activity_horizontal_margin"  
  7.                 android:paddingTop="@dimen/activity_vertical_margin"  
  8.                 android:paddingBottom="@dimen/activity_vertical_margin"  
  9.                 tools:context=".HomeActivity">  
  10.   
  11.     <CheckBox  
  12.         android:layout_width="wrap_content"  
  13.         android:layout_height="wrap_content"  
  14.         android:text="@string/enable_service"  
  15.         android:id="@+id/enable_service"  
  16.         android:layout_alignParentTop="true"  
  17.         android:layout_alignParentLeft="true"  
  18.         android:layout_alignParentStart="true"  
  19.         android:checked="false" />  
  20.   
  21.     <TextView  
  22.         android:layout_width="wrap_content"  
  23.         android:layout_height="wrap_content"  
  24.         android:text=""  
  25.         android:id="@+id/welcome_text"  
  26.         android:layout_below="@+id/enable_service"  
  27.         android:layout_alignParentLeft="true"  
  28.         android:layout_alignParentStart="true"  
  29.         android:layout_marginTop="10dp"  
  30.         android:textColor="#000000" />  
  31.   
  32.     <TextView  
  33.         android:layout_width="wrap_content"  
  34.         android:layout_height="wrap_content"  
  35.         android:id="@+id/welcome_information_home"  
  36.         android:layout_alignTop="@+id/welcome_text"  
  37.         android:layout_alignParentLeft="true"  
  38.         android:layout_alignParentStart="true"  
  39.         android:textColor="#000" />  
  40.   
  41.     <TextView  
  42.         android:layout_width="wrap_content"  
  43.         android:layout_height="wrap_content"  
  44.         android:textAppearance="?android:attr/textAppearanceMedium"  
  45.         android:text="Configuration"  
  46.         android:id="@+id/textView4"  
  47.         android:layout_below="@+id/welcome_text"  
  48.         android:layout_alignParentLeft="true"  
  49.         android:layout_alignParentStart="true"  
  50.         android:layout_marginTop="50dp"  
  51.         android:textColor="#000" />  
  52.   
  53.     <TextView  
  54.         android:layout_width="wrap_content"  
  55.         android:layout_height="wrap_content"  
  56.         android:textAppearance="?android:attr/textAppearanceSmall"  
  57.         android:text="Time interval"  
  58.         android:id="@+id/textView5"  
  59.         android:layout_below="@+id/textView4"  
  60.         android:layout_alignParentLeft="true"  
  61.         android:layout_alignParentStart="true"  
  62.         android:layout_marginTop="28dp"  
  63.         android:textColor="#000" />  
  64.   
  65.     <EditText  
  66.         android:layout_width="wrap_content"  
  67.         android:layout_height="wrap_content"  
  68.         android:inputType="number"  
  69.         android:ems="10"  
  70.         android:id="@+id/time_interval"  
  71.         android:layout_below="@+id/textView5"  
  72.         android:layout_alignParentLeft="true"  
  73.         android:layout_alignParentStart="true"  
  74.         android:layout_alignParentRight="true"  
  75.         android:layout_alignParentEnd="true"  
  76.         android:hint="In minutes; 5-18000" />  
  77.   
  78.     <TextView  
  79.         android:layout_width="wrap_content"  
  80.         android:layout_height="wrap_content"  
  81.         android:textAppearance="?android:attr/textAppearanceSmall"  
  82.         android:text="Minimum distance covered"  
  83.         android:id="@+id/textView6"  
  84.         android:layout_centerVertical="true"  
  85.         android:layout_alignParentLeft="true"  
  86.         android:layout_alignParentStart="true"  
  87.         android:textColor="#000" />  
  88.   
  89.     <EditText  
  90.         android:layout_width="wrap_content"  
  91.         android:layout_height="wrap_content"  
  92.         android:inputType="number"  
  93.         android:ems="10"  
  94.         android:id="@+id/meters_distance"  
  95.         android:layout_below="@+id/textView6"  
  96.         android:layout_alignParentLeft="true"  
  97.         android:layout_alignParentStart="true"  
  98.         android:layout_alignParentRight="true"  
  99.         android:layout_alignParentEnd="true"  
  100.         android:hint="In meters." />  
  101.   
  102.     <Button  
  103.         android:layout_width="wrap_content"  
  104.         android:layout_height="wrap_content"  
  105.         android:text="Update"  
  106.         android:id="@+id/update_button"  
  107.         android:layout_below="@+id/meters_distance"  
  108.         android:layout_centerHorizontal="true" />  
  109.   
  110. </RelativeLayout>  
The above Android UI is useful as it contains the UI components already set up, we can then use the same UI and change the view from code behind as we need to change it.

Managing the data sources

In Android, you manage the data in SQLite databases. They are very small and compact databases provided by default that you can use and store your data efficiently in it.

I have used the same thing, created tables and database for stuff and provided a few functions in the class that I can execute from external world! I wouldn't want external objects to directly call functions and execute commands on the databases. That is why, I have kept everything under the hood and provided public functions for CRUD operations.

In this section, there are only two classes that you need to understand in implementing SQLite database functionality in your application.

 

  1. SQLiteOpenHelper class
  2. BaseColumns interface

1. SQLiteOpenHelper class

This class provides us with the functions and other resources that we may require in actually imitating as an SQL engine. Allows us to manage our SQL databases and connections to those databases. Provides us with functions to create databases, upgrade them or delete the databases as required.

There are many functions, I want to talk about only one function, onCreate(SQLiteDatabase), here. This function is called when database is created. You need to handle the function and write the SQL commands to create the tables and design the schema for your objects in the database. The function is called only once! So make sure you keep the schema in mind, because to change the schema you would have the delete the previous databases and re-create them.

An example code that should go in onCreate(SQLiteDatabase) function would be like the following one,

Code

  1. private final static String COMMAND = "CREATE TABLE " + TABLE_NAME + "(" + _ID + " INTEGER PRIMARY KEY, " + COLUMN_ONE + " TEXT," + COLUMN_TWO + " TEXT" + ")";@  
  2. Override  
  3. public void onCreate(SQLiteDatabase db)  
  4. {  
  5.     // Execute the codes here...  
  6.     db.execSQL(COMMAND);  
  7. }  
This code above once executed, create a table and three columns in it. You can execute multiple commands to create multiple columns in it and so on. For more on SQL commands, I recommend you learn SQL language!

2. BaseColumns interface

There is nothing important to understand here! It just adds another member, _ID to the object that Cursorobject may be expecting. In the code above, the _ID comes from this interface.

Code
  1. public class MyClass extends SQLiteOpenHelper implements BaseColumns {  
  2.    /* 
  3.    * The code comes here to manage the SQL databases. 
  4.    * The functions as discussed also need to be implemented to trigger our own logic each time a database needs to be created. 
  5.    */  
  6. }  
I also have used same methods to create the databases and store the values for each of the item. It is included in the sample!

Capturing the location

As already said, the application's logic is based on a backend service which runs and capture the location and sends the location to contacts. That is why, the application doesn't need to have a UI for each of the component that we have in our application. Our application contains services and other background processes that don't need a UI, IntentService, SmsManager and others, they do not need a UI to work on. Building a UI won't cause much, but why waste another activity where you can trigger functions in basic classes!

I wrote the same Java class which does everything do application. It capture the location and then triggers the broadcaster to broadcast the location.

Code
  1. package com.afzaalahmadzeeshan.mylocation;  
  2. import android.app.AlertDialog;  
  3. import android.app.PendingIntent;  
  4. import android.content.Context;  
  5. import android.content.DialogInterface;  
  6. import android.content.Intent;  
  7. import android.location.*;  
  8. import android.provider.Settings;  
  9. public class LocationService  
  10. {  
  11.     private Context mContext;  
  12.     private Location mLocation;  
  13.     private LocationManager locationManager;  
  14.     private PendingIntent intent;  
  15.     private static long distance;  
  16.     private static long minutes;  
  17.     public LocationService(Context context)  
  18.     {  
  19.         mContext = context;  
  20.         // Set up to capture the location updates  
  21.         Intent smsIntent = new Intent(mContext, BroadcastLocationService.class);  
  22.         intent = PendingIntent.getService(mContext, 0, smsIntent, 0);  
  23.     }  
  24.     public static long getDistance()  
  25.     {  
  26.         return distance;  
  27.     }  
  28.     public static long getMinutes()  
  29.     {  
  30.         return minutes;  
  31.     }  
  32.     public void cancelUpdates()  
  33.     {  
  34.         if (locationManager != null)  
  35.         {  
  36.             locationManager.removeUpdates(intent);  
  37.         }  
  38.     }  
  39.     public Location getLocation()  
  40.     {  
  41.         locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);  
  42.         // Check if the tracking is enabled.  
  43.         boolean isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);  
  44.         boolean isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);  
  45.         if (!isGPSEnabled && !isNetworkEnabled)  
  46.         {  
  47.             // Prompt to get the settings enabled by the user.  
  48.             showSettingsDialog();  
  49.         }  
  50.         else  
  51.         {  
  52.             // Either one is enabled  
  53.             // 10 * 60 * 1000 = 10 minutes  
  54.             // 1000 = 1 km  
  55.             // this = listener  
  56.             if (isGPSEnabled)  
  57.             {  
  58.                 // Get the location from GPS  
  59.                 try  
  60.                 {  
  61.                     locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, getMinutes() * 1000 * 60, getDistance(), intent);  
  62.                     if (mLocation == null)  
  63.                     {  
  64.                         mLocation = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);  
  65.                     }  
  66.                 }  
  67.                 catch (SecurityException e)  
  68.                 {  
  69.                     showSettingsDialog();  
  70.                 }  
  71.             }  
  72.             else  
  73.             {  
  74.                 // Get the location from GPS  
  75.                 try  
  76.                 {  
  77.                     // Set up to capture the location updates  
  78.                     Intent smsIntent = new Intent(mContext, BroadcastLocationService.class);  
  79.                     PendingIntent intent = PendingIntent.getService(mContext, 0, smsIntent, 0);  
  80.                     locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, getMinutes() * 1000 * 60, getDistance(), intent);  
  81.                     if (mLocation == null)  
  82.                     {  
  83.                         mLocation = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);  
  84.                     }  
  85.                 }  
  86.                 catch (SecurityException e)  
  87.                 {  
  88.                     showSettingsDialog();  
  89.                 }  
  90.             }  
  91.         }  
  92.         return mLocation;  
  93.     }  
  94.     public void showSettingsDialog()  
  95.     {  
  96.         new AlertDialog.Builder(mContext).setTitle("Enable GPS").setMessage("Enable GPS in your settings for receiving active location details.").setNegativeButton("Cancel"null).setPositiveButton("Settings"new DialogInterface.OnClickListener()  
  97.         {@  
  98.             Override  
  99.             public void onClick(DialogInterface dialog, int which)  
  100.             {  
  101.                 Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);  
  102.                 mContext.startActivity(intent);  
  103.             }  
  104.         }).create().show();  
  105.     }  
  106. }  
This code runs in background, and our home activity uses the functions available here to perform actions and to get details about the location manager and location APIs.

Broadcasting the location

Final stage is to actually broadcast the location, I used SmsManager for this purpose! The following code does the trick for that.

Code
  1. package com.afzaalahmadzeeshan.mylocation;  
  2. import android.content.Context;  
  3. import android.telephony.SmsManager;  
  4. import java.util.ArrayList;  
  5. public class SmsService  
  6. {  
  7.     public static boolean sendMessage(Context context, String location)  
  8.     {  
  9.         boolean result = false;  
  10.         try  
  11.         {  
  12.             SmsManager manager = SmsManager.getDefault();  
  13.             String message;  
  14.             // Get the string  
  15.             message = "[AUTOMATIC MESSAGE]\n" + "I am currently at " + location + " (approximately; accuracy within 100 meters).";  
  16.             ArrayList < Number > numbers = new ContentManager(context).getNumbers();  
  17.             if (numbers != null && numbers.size() > 0)  
  18.             {  
  19.                 for (Number number: numbers)  
  20.                 {  
  21.                     String telNumber = number.getNumber();  
  22.                     manager.sendTextMessage(telNumber, null, message, nullnull);  
  23.                 }  
  24.                 result = true;  
  25.             }  
  26.         }  
  27.         catch (Exception ignored)  
  28.         {}  
  29.         return result;  
  30.     }  
  31. }  
The above code does the job for us. If you can see, it gets the list of contacts that we want to notify and then loops to send the message to each of them based on the location. So this does the job for us!

Bonus: Handling the errors and good practices

Now, I may share a few personal experience with errors and bad application code so that once you start writing the application for your clients, you don't repeat the same problems and that the design may be implemented to its best level.

 

  1. Table not found

    The first error that may raise in your application is, "table not found". Well that is legit error in your application and is a cause that a table you may wanted to create initially when database was created, (table) was not created and now it cannot be created until you remove previous database and re-execute the onCreate function.

    To solve it, on the development environment, you can delete the data and then re-execute the application. But remember to always create the tables, and define the schema in the database's onCreate function.

  2. Providers not available

    Since our application depends on the location providers, we need to make sure that they are available before we start cap*turing the location from our application and to get notified about about other changes in the location of the user. You can use manager and get the location services and providers.

    The following code for example, determines whether the provider for GPS is enabled or not:

    Code

    1. boolean isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);  
    You can get other providers in a similar manner. Since that is a boolean object, you can use it in a condition and work appropriately. You can also create a logic to show a dialog and open the settings if user needs to trigger the providers.

    Code
    1. if(!isGPSEnabled && !isNetworkEnabled) {  
    2. showSettingsDialog();  
    3. }  
    This would trigger another function that prompts the user to activate the provider in settings. The function has the following structure:

    Code
    1. public void showSettingsDialog()  
    2. {  
    3.     new AlertDialog.Builder(mContext).setTitle("Enable GPS").setMessage("Enable GPS in your settings for receiving active location details.").setNegativeButton("Cancel"null).setPositiveButton("Settings"new DialogInterface.OnClickListener()  
    4.     {@  
    5.         Override  
    6.         public void onClick(DialogInterface dialog, int which)  
    7.         {  
    8.             Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);  
    9.             mContext.startActivity(intent);  
    10.         }  
    11.     }).create().show();  
    12. }  
    This would prompt the user to activate the location services. Just for sake of this article, I can mimic the scenario and show you how it works. For example, we have an application that activates the location providers and gets location from them.

    Android device showing the settings
    Figure 5: Android device showing the settings and location is enabled by default

    We can disable the location to see how our application would behave.

    Location is now turned off
    Figure 6: Location is now turned off

    Now, we can trigger the capturing function in our application to see what happens. Firstly, consider we are having a condition that checks if the providers are enabled. If they are not, then the function displays an alert box, otherwise if that was not the case, then an exception may have been raised because providers are not available.

    When we click to enable the service, our location manager would tell us that providers are not enabled and thus our application prompts user to enable the services. Otherwise, cancel the operation.

    Android alert dialog
    Figure 7: Android alert dialog, to prompt him to enable the location

    User can then enable the location in their settings, since they were disabled.

    Code
    1. Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);  
    The above intent would take the user to location settings by default, so that is the same thing other applications do which take you directly to the settings.

    Location turned off
    Figure 8: Location turned off. User can turn it on to enable the providers

    GPS can then be seen to notify that the location providers are working and location can be captured by your application for users.

    GPS sign shown in the system tray
    Figure 9: GPS sign shown in the system tray

    So, this explains how you should consider checking the paths that your application goes from and make sure you write the efficient application that takes care of most of the things for user.

     

  3. Managing the resources

    The application shown above uses SMS resources, location resources and battery. For this sake, since we just want to notify the users, you must consider keeping the time span large to save battery. Each time GPS engine is turned on, it would start consuming battery juice so you may want to make sure your application doesn't waste the juice.

    1. Minimum time

      Set it to like, 10 minutes at minimum. You do not want constant updates unless you are showing the GPS on screen.

    2. Minimum distance

      100 meters doesn't matter at all, so, this may be set as a minimum distance travelled.

This would allow our application to sleep for a while and then access the location after a while. Since our application is able to work in background, we don't need to worry whether user triggers the request himself or application is going to manage that itself.

SMS manager would consume resources and would charge the user, so make sure you write the text very briefly, yet a small one.

In the application that I wrote, has the following code,

Code

  1. if(m > 99 && min > 4 && min < 18001) {  
  2.     /* m is the variable for meters 
  3.      * min is the variable for minutes 
  4.      *  
  5.      * The above condition checks if minimum minutes are less than 18000, that is enough! 
  6.      * also checks whether meters providers are greater than 100 or not.  
  7.      * This ensures that user receives updates for location changes and battery isn't  
  8.      * drained without need.  
  9.      */  
  10.     new ContentManager(getBaseContext()).setupSettings(getBaseContext(), m, min);  
  11. }  
The code works and check for the validation of configuration.

DO NOT SEND BULK MESSAGES!
I wrote an application back a few months ago, which sent the messages in an array using loop. That throttle entire network and the message I had to receive right now, was received like 5 to 9 hours later with a delay and many time my own messages did not send because of the bulk messages on the network. Try using the delivery and sending reports to send the next message to network.

Point of Interests

The application was developed for personal use, but article does not reflect the personal application, instead it explains about the APIs and libraries. The application explains the use of:

 

  1. LocationManager
  2. LocationListener
  3. PendingIntent
  4. SmsManager
  5. SQLiteOpenHelper
  6. BaseColumns
  7. And much more.

The article doesn't force you to stick to one framework or method, you are free to write it in your own way and implement it. The article is just a resource that you can use to build your own application from scratch! Download the source code and get started.