Android Proximity Alerts Tutorial
Smart-phones are taking over the mobile world, this is a fact. Since GPS devices are usually found embedded in those phones, there is already a notable rise in applications that take advantage of the offered geographical positioning functionality. A type of those applications is the one of Location Based Services, where the service exploits knowledge about where the mobile user is globally positioned at. Pretty common are also the applications that use geocoding (finding associated geographic coordinates from geographic data, such as a street address) and reverse geocoding (providing information based on given coordinates). One other aspect of that type of applications is the creation of proximity alerts. As their name suggests, these are alerts that get generated when the user is physically located near a specific Point Of Interest (POI). Proximity alert are going to be a “hot” thing the following years, since a lot of applications are going to make use of them, with the most prominent example being targeted advertising. In this tutorial I am going to show you how to take advantage of Android’s built-in proximity alert capabilities.
Before we begin, it would be helpful to have read introductory articles about location based application and/or geocoding. You might want to take a look at some previous tutorials of mine, such as “Android Location Based Services Application – GPS location” and “Android Reverse Geocoding with Yahoo API – PlaceFinder”. One other thing to note is that this tutorial was inspired by a very cool tutorial named “Developing Proximity Alerts for Mobile Applications using the Android Platform”. This is a four part tutorial, which gets a little complicated at some points and might intimidate a beginner. For that reason, I decided to provided a shorter and more straightforward tutorial.
What we will build is a simple application that stores the user’s coordinates for a point that interests him and then provide a notification when the user is near that point. The coordinates are retrieved on demand when the user is located at that point.
We begin by creating a new Eclipse project, named “AndroidProximityAlertProject” in our case. We also create a main activity for our application under the name “ProxAlertActivity”. Here is what the application’s main page will look like:
Here is the declaration file for the main UI layout, named “main.xml”:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <EditText android:id="@+id/point_latitude" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="25dip" android:layout_marginRight="25dip" /> <EditText android:id="@+id/point_longitude" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginLeft="25dip" android:layout_marginRight="25dip" /> <Button android:id="@+id/find_coordinates_button" android:text="Find Coordinates" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/save_point_button" android:text="Save Point" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
Let’s now get started with the interesting stuff. First of all, we need a reference to the LocationManager class, which provides access to the system location services. This is done via a call to the getSystemService method of our activity. We can then use the requestLocationUpdates method in order to request notifications when the user’s location changes. This is not strictly required when developing proximity alerts, but I will use it here in order to calculate the distance between the point of interest and the current user location. At any given time, we can call the getLastKnownLocation method and retrieve the last known location of a specific provider, in our case the GPS device. Finally, we will make use of the addProximityAlert method that can be used to set a proximity alert for a location given by specific coordinates (latitude, longitude) and a given radius. We can also optionally define an expiration time for that alert if we wish to monitor the alert for a specific time period. A PendingIntent can also be provided, which will be used to generate an Intent to fire when entry to or exit from the alert region is detected.
All these are translated into code as follows:
package com.javacodegeeks.android.lbs; import java.text.DecimalFormat; import java.text.NumberFormat; import android.app.Activity; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; public class ProxAlertActivity extends Activity { private static final long MINIMUM_DISTANCECHANGE_FOR_UPDATE = 1; // in Meters private static final long MINIMUM_TIME_BETWEEN_UPDATE = 1000; // in Milliseconds private static final long POINT_RADIUS = 1000; // in Meters private static final long PROX_ALERT_EXPIRATION = -1; private static final String POINT_LATITUDE_KEY = "POINT_LATITUDE_KEY"; private static final String POINT_LONGITUDE_KEY = "POINT_LONGITUDE_KEY"; private static final String PROX_ALERT_INTENT = "com.javacodegeeks.android.lbs.ProximityAlert"; private static final NumberFormat nf = new DecimalFormat("##.########"); private LocationManager locationManager; private EditText latitudeEditText; private EditText longitudeEditText; private Button findCoordinatesButton; private Button savePointButton; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); locationManager.requestLocationUpdates( LocationManager.GPS_PROVIDER, MINIMUM_TIME_BETWEEN_UPDATE, MINIMUM_DISTANCECHANGE_FOR_UPDATE, new MyLocationListener() ); latitudeEditText = (EditText) findViewById(R.id.point_latitude); longitudeEditText = (EditText) findViewById(R.id.point_longitude); findCoordinatesButton = (Button) findViewById(R.id.find_coordinates_button); savePointButton = (Button) findViewById(R.id.save_point_button); findCoordinatesButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { populateCoordinatesFromLastKnownLocation(); } }); savePointButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { saveProximityAlertPoint(); } }); } private void saveProximityAlertPoint() { Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); if (location==null) { Toast.makeText(this, "No last known location. Aborting...", Toast.LENGTH_LONG).show(); return; } saveCoordinatesInPreferences((float)location.getLatitude(), (float)location.getLongitude()); addProximityAlert(location.getLatitude(), location.getLongitude()); } private void addProximityAlert(double latitude, double longitude) { Intent intent = new Intent(PROX_ALERT_INTENT); PendingIntent proximityIntent = PendingIntent.getBroadcast(this, 0, intent, 0); locationManager.addProximityAlert( latitude, // the latitude of the central point of the alert region longitude, // the longitude of the central point of the alert region POINT_RADIUS, // the radius of the central point of the alert region, in meters PROX_ALERT_EXPIRATION, // time for this proximity alert, in milliseconds, or -1 to indicate no expiration proximityIntent // will be used to generate an Intent to fire when entry to or exit from the alert region is detected ); IntentFilter filter = new IntentFilter(PROX_ALERT_INTENT); registerReceiver(new ProximityIntentReceiver(), filter); } private void populateCoordinatesFromLastKnownLocation() { Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER); if (location!=null) { latitudeEditText.setText(nf.format(location.getLatitude())); longitudeEditText.setText(nf.format(location.getLongitude())); } } private void saveCoordinatesInPreferences(float latitude, float longitude) { SharedPreferences prefs = this.getSharedPreferences(getClass().getSimpleName(), Context.MODE_PRIVATE); SharedPreferences.Editor prefsEditor = prefs.edit(); prefsEditor.putFloat(POINT_LATITUDE_KEY, latitude); prefsEditor.putFloat(POINT_LONGITUDE_KEY, longitude); prefsEditor.commit(); } private Location retrievelocationFromPreferences() { SharedPreferences prefs = this.getSharedPreferences(getClass().getSimpleName(), Context.MODE_PRIVATE); Location location = new Location("POINT_LOCATION"); location.setLatitude(prefs.getFloat(POINT_LATITUDE_KEY, 0)); location.setLongitude(prefs.getFloat(POINT_LONGITUDE_KEY, 0)); return location; } public class MyLocationListener implements LocationListener { public void onLocationChanged(Location location) { Location pointLocation = retrievelocationFromPreferences(); float distance = location.distanceTo(pointLocation); Toast.makeText(ProxAlertActivity.this, "Distance from Point:"+distance, Toast.LENGTH_LONG).show(); } public void onStatusChanged(String s, int i, Bundle b) { } public void onProviderDisabled(String s) { } public void onProviderEnabled(String s) { } } }
In the onCreate method we hook up the location manager with a custom class that implements the LocationListener interface and allows to get notified on location changes via the onLocationChanged method. We will see how to handle the updates later. We also find the various UI widgets and attach OnClickListeners to the buttons.
When the user wants to find his current coordinates, the “populateCoordinatesFromLastKnownLocation” method is invoked. Inside that, we use the getLastKnownLocation method and retrieve a Location object. The EditTexts are then populated with the retrieved location information.
Similarly, when the user wants to save the point and provide alerts for that (“saveProximityAlertPoint”), the location info is first retrieved. Then, we save the latitude and longitude information as preference data using the SharedPreferences class and more specifically the SharedPreferences.Editor. Finally, we create a PendingIntent by using the getBroadcast static method. For the encapsulated Intent, we create an IntentFilter and use the registerReceiver method to associate a custom BroadcastReceiver with the specific intent filter. Note that this binding could alternatively be achieved in a declarative way using the manifest file.
Now let’s examine how we handle user’s location changes. In the implemented method of the “MyLocationListener” class, we extract the stored location info (“retrievelocationFromPreferences”) from the SharedPreferences class. Then, we use the distanceTo method to calculate the distance between the two locations, the current one and the one corresponding to the point of interest. This is done for debugging purposes, so that we know if we have actually entered the area around the point.
The final step is to handle the events of entering the area of the point of interest. This is done inside the “ProximityIntentReceiver” class that extends the BroadcastReceiver and responds to the custom intent that we attached to the location manager when adding the proximity alert. The handling occurs inside the onReceive method, which gets invoked upon event. Inside that, we retrieve the value of the KEY_PROXIMITY_ENTERING key from the associated intent, which indicates whether a proximity alert is entering (true) or exiting (false). The code is the following:
package com.javacodegeeks.android.lbs; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.location.LocationManager; import android.util.Log; public class ProximityIntentReceiver extends BroadcastReceiver { private static final int NOTIFICATION_ID = 1000; @Override public void onReceive(Context context, Intent intent) { String key = LocationManager.KEY_PROXIMITY_ENTERING; Boolean entering = intent.getBooleanExtra(key, false); if (entering) { Log.d(getClass().getSimpleName(), "entering"); } else { Log.d(getClass().getSimpleName(), "exiting"); } NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, null, 0); Notification notification = createNotification(); notification.setLatestEventInfo(context, "Proximity Alert!", "You are near your point of interest.", pendingIntent); notificationManager.notify(NOTIFICATION_ID, notification); } private Notification createNotification() { Notification notification = new Notification(); notification.icon = R.drawable.ic_menu_notifications; notification.when = System.currentTimeMillis(); notification.flags |= Notification.FLAG_AUTO_CANCEL; notification.flags |= Notification.FLAG_SHOW_LIGHTS; notification.defaults |= Notification.DEFAULT_VIBRATE; notification.defaults |= Notification.DEFAULT_LIGHTS; notification.ledARGB = Color.WHITE; notification.ledOnMS = 1500; notification.ledOffMS = 1500; return notification; } }
The code is pretty straightforward. After we determine whether we have an entering or exiting proximity alert, we are ready to provide a custom notification. To do so, we first take reference of the appropriate service, i.e. the NotificationManager. Through that service, we may send alerts to the user, wrapped around Notification objects. The notifications can be customized upon will and may include vibration, flashing lights etc. We also added a specific icon that will appear to the status bar. The setLatestEventInfo is preferred when we just want to add a basic title and text message. You can find more about notifications here. Additionally, we can use a PendingIntent in order to define an activity to be invoked when the user acknowledges the notification by clicking on it. However, to keep things simple, I do not use an intent to be launched in my example.
Finally, let’s see what the Android manifest file looks like:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.javacodegeeks.android.lbs" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name"> <activity android:name=".ProxAlertActivity" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> <uses-sdk android:minSdkVersion="3" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.VIBRATE" /> </manifest>
Nothing special here, just remember to add the necessary permissions, i.e.
We are now ready to test our application. Launch the Eclipse configuration. Then, go to the DDMS view of Eclipse and look for the “Emulation Control” tab. There, among other things, you will find the “Location Controls” section, which can send mock location data to the emulator. In the “Manual” tab, just hit the “Send” button, there are already some coordinates set up.
After that, the GPS provider will have a last known location that can provide upon request. So, hit the “Find Coordinates” button and this is what you will get:
Then, hit the “Save Point” in order to declare the current location as a point of interest. The location coordinates will be saved in the preferences and the proximity alert will be added to the location manager.
Next, let’s simulate the fact that the user leaves the location. Change the value of the coordinates, for example change latitude as follows: 37.422006 ? 37.522006. We are now quite far from the point of interest. Let’s suppose now that we are approaching it, thus change the latitude to a closer value, for example: 37.522006 ? 37.423006.
This is inside the radius of the point (this was set to 1000 meters) thus the alert is triggered and our receiver gets notified. There, we create our notification and send it via the notification manager. The notification appears at the status bar as follows:
That’s it, a quick guide on how to implement proximity alerts with the Android platform. You can find here the Eclipse project created for the needs of this tutorial.
- Android Location Based Services Application – GPS location
- Android Reverse Geocoding with Yahoo API – PlaceFinder
- “Android Full Application Tutorial” series
- Boost your Android XML parsing with XML Pull
- Android Text-To-Speech Application
- Install Android OS on your PC with VirtualBox
Here an android cook book, i want to share it http://www.filesonic.com/file/qyB8Efv/Android-Cookbook.pdf
Hi,
I am new to Android app development.I have followed the above code. But, I am getting an error ‘Error receiving broadcast intent’. The Logcat shows Null pointer exception caused due to the PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, null, 0); Please help. I have been stuck on this for so long. I have read lot of articles about this on net, nothing seems to be working for me.Thanks
Hi! Nice post, very useful!
Everything works fine, but if I’m inside of the radius, the distance is < 1000m, the Intent doesn't fire! Has anybody an idea?
I need same thing .please help me
i m new to this how run this code.can u explain this plz
ERROR Messagejava.lang.NullPointerException
log file – No error in log throws above exception
I’m modifying some code of ProximityIntentReceiver to successful running on 4.0.3 emulator…
I append a new Intent in the onReceive() while creating pendingIntent , which can be any name you like, and use Notification.Builder to create notification.
Help it works!
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0,
new Intent(PROX_ALERT_INTENT), 0);
private Notification createNotification(Context context, String title,
String content, PendingIntent pIntent) {
Notification notification = new Notification.Builder(context)
.setContentTitle(title).setContentText(content)
.setContentIntent(pIntent).getNotification();
notification.icon = R.drawable.ic_menu_notifications;
notification.when = System.currentTimeMillis();
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notification.flags |= Notification.FLAG_SHOW_LIGHTS;
notification.defaults |= Notification.DEFAULT_VIBRATE;
notification.defaults |= Notification.DEFAULT_LIGHTS;
notification.ledARGB = Color.WHITE;
notification.ledOnMS = 1500;
notification.ledOffMS = 1500;
return notification;
}
thanks for posting this, however like others, it does not work. trying to run against 4.0.3 HTC one x, when the intent fires for the notification, it crashes with the same error Karan receives,
Error receiving broadcast intent’. Can you check your code again? I have also not been able to fix this.
Nevermind fixed it myself: PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
change null in the pending intent to say “intent”
will it work when app is closed?
No, uses services
First of all, thx a lot for your post. I am also trying to setup a little test system to create and later receive proximity alerts. The difference is simply that I takek the coordinates of a mapView (via getCenter) and then do not register a broadcast receiver programatically but in xml (AndroidManifest). So far, it does not work, and sometimes it leads to an immediate alert which is really strange. My post is here on stackoverflow: http://stackoverflow.com/questions/12686533/android-proximity-alerts-on-galaxy-nexus-with-android-4-1-1-does-it-really-wor
It would be great if you or others could have a look!
hi..i want to change your code so that the latitude and longitude are saved in database instead of using shared preferences. then i want to request the location update in my application and check if the location is near the location in the database and alert the user. How can i do that. please guide me. i was doing it for weeks but it’s not working. Thank You.
Hey could you figure out how to work on this… I know its long you have asked this question. Even I am trying to implement something similiar for my project…Would appreciate if you can guide me..
Hai disqus_DqWeJjq6kz,
Have you done what you asked? Me too facing same problem from last 2 weeks Will you please send me your code if you did it to shylendramadda@gmail.com. please help me. Thanks in advance..
did you find solution to your problem? even i am trying to implement same thing
hi…i tried to implement your tutorial. I want to make some changes. i want to fetch the location and save in in database. later i want to check for location update and see if it match the location in the database and alert the user. how can i do this using database instead of using shared preferences?Thank you
Great tutorials,
Thank you so much,
Totally Great
hi i tried your tutorial.but i am not getting the notification.please help me out
Hey did you figure out why your are not receiving notifications..
I did not get the notification as well. Did you figure it out?
I have a question ?
did you notice ..
LocationManager.addProximityAlert() get paused when the device is asleep…
if suppose i want to have my service be active when device assleep…
Can you tell me what will be the reason for this..
Any body there ?who can help me while doing an android Project
pls send this project as a zip format to my mail id…..
Thanks for the tutorial, but app crashes when i press the save button.
Can anyone please try to find the reason.
Does not crash in device, but does not run in background.
How to modify the code to make it run in background forever.
Tnx
Is it possible to add multiple proximity alerts i mean multiple locations to get alerts ?
I need same thing.please help me
Can you email me the code in a zip file. secondly, I want to implement indoor localization app using android. I want to use fingerprinting localization algorithms. any information or code please??
Thanks a lot! Finally a simple and working tutorial!
please can u send your project to my email please sir
adeljaffan@gmail.com