Technology Moves as Fast as We Move It by Brian Buikema
Part 4 – Developing Proximity Alerts for Mobile Applications using the Android Platform
I’ve decided to add this fourth part to discuss making some beneficial enhancements to the Proximity Alerts Mobile Application we developed in the earlier posts (Part 1, Part 2, and Part 3). Our goal in Parts 1- 3 was to create an Android-based mobile application that demonstrated some basic proximity alert functionality. And we successfully completed this, but our method of processing proximity alerts was in the foreground, the method of alerting the user was somewhat crude, e.g., vibrate phone and display toast, and the alerts were automatically enabled with no way for the user to control alert enable/disabling.
Enhancements
Therefore, we will implement three major enhancements in this post as follows:
- Process proximity alerts in the background by moving processing to an Android Service. We will call our service PromityAlertService.
- We will use Android’s built-in NotificationManager to provide true Android notifications that appear and can be controlled within the status bar.
- And we will provide a checkbox in the ProximityAlertSetActivity screen allowing the user to enable/disable alerts anytime. We will default to disabled to ensure that an alert is not fired immediately (since you are standing in the actual proximity of the alert when creating/editing the alert!).
Moving Proximity Alert Detection Processing to the Background by Creating an Android Service
More coming soon!
Enhancing Proximity Alert Notifications
When we add or save a proximity alert, we add the alert to the LocationManager via our AlertHelper class. Note: I have renamed the addProximityAlerts(…) method we used previously, to addProximityAlertsToLocationManager(…) to be more precise. It is within this method that we add a PendingIntent that will perform a broadcast to our ProximityIntentReceiver (you may recall we created the ProximityIntentReceiver in a previous post).
The ProximityAlertHelper’s addProximityAlertsToLocationManager(…) method is as follows:
public boolean addProximityAlertsToLocationManager(Context caller, ArrayList<ProximityAlert> alerts) {
if (initialized) {
String context = Context.LOCATION_SERVICE;
LocationManager mgr = ((LocationManager) caller.getSystemService(context));
removeAlerts(caller);
for (int i=1; i <alerts.size(); i++) {
ProximityAlert alert = alerts.get(i);
if (alert.Enabled) {
Intent intent = new Intent(ProximityAlertActivity.class.getName());
intent.putExtra("id", alert.Id);
PendingIntent proximityIntent = PendingIntent.getBroadcast(caller, i, intent, PendingIntent.FLAG_CANCEL_CURRENT);
float proxRadius = Float.parseFloat(PreferenceManager.getDefaultSharedPreferences(caller).getString("proxradius", "2"));
mgr.addProximityAlert(alert.Lat, alert.Lng, proxRadius, -1, proximityIntent);
proximityIntentsMap.put(alert, proximityIntent);
}
}
return true;
}
return false;
}
Note that we add the alert’s id field to the intent in line 13. We will use this later to retrieve the alert object upon processing a proximity alert notification event.
Let’s focus our attention on ProximityIntentReceiver class and discuss one addition to the onReceive(…) method. We make our notification request to the NotificationManager here, on line 14, as follows:
public class ProximityIntentReceiver extends BroadcastReceiver implements IDebugSwitch {
@Override
public void onReceive(Context context, Intent intent) {
String key = LocationManager.KEY_PROXIMITY_ENTERING;
Boolean entering = intent.getBooleanExtra(key, false);
if (entering) {
ProximityAlert alert;
try {
alert = ProximityAlertHelper.getInstance().getAlert(intent.getExtras().getLong("id"));
ArrayList<ProximityAlert> alerts = new ArrayList<ProximityAlert>();
alerts.add(alert);
NotifierHelper.sendNotification(context, ProximityAlertActivity.class, alerts);
}
catch (Exception ex) {
Log.e(AppSettings.DEBUG_TAG, "General failure in ProximityIntentReceiver", ex);
throw;
}
}
}
}
Note, in line 10 the actual event’s alert object is retrieved using the id field we earlier stored in the intent.
And finally, I have created a helper class called NotifierHelper to handle the heavy lifting of adding a notification to the NoticationManager as follows:
public class NotifierHelper implements IDebugSwitch {
private static final int NOTIFY_1 = 0x1001;
public static void sendNotification(Context context, Class<?> activityToLaunch, ArrayList<ProximityAlert> alerts) {
// String values should be stored in "strings.xml"
// I show them here for convenience.
NotificationManager notifier = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
// Create this outside the button so we can increment the number drawn over the notification icon,
// indicating the number of alerts for this event.
final Notification notify = new Notification(R.drawable.android_32, "", System.currentTimeMillis());
// Add status bar notification
notify.icon = R.drawable.android_32;
notify.tickerText = "Proximo has new Proximity Alerts!";
notify.when = System.currentTimeMillis();
notify.number = alerts.size();
notify.flags |= Notification.FLAG_AUTO_CANCEL;
// Add lights
notify.flags |= Notification.FLAG_SHOW_LIGHTS;
notify.ledARGB = Color.WHITE;
notify.ledOnMS = 500;
notify.ledOffMS = 500;
// Add vibs
notify.vibrate = new long[] {100, 200, 200, 200, 200, 200, 1000, 200, 200, 200, 1000, 200};
Intent toLaunch = new Intent(context, activityToLaunch);
toLaunch.putExtra("id", alerts.get(0).Id);
PendingIntent intentBack = PendingIntent.getActivity(context, 0, toLaunch, 0);
notify.setLatestEventInfo(context, "Proximity Alerts", "You have " + alerts.size() + " proximity alerts!", intentBack);
notifier.notify(NOTIFY_1, notify);
}
public static void clear(Activity caller) {
NotificationManager notifier = (NotificationManager) caller.getSystemService(Context.NOTIFICATION_SERVICE);
notifier.cancelAll();
}
}
Providing the Ability to Enable and Disable Proximity Alerts
Enabling and disabling proximity alerts is a fundamental feature that must not be overlooked. Let’s first update the ProximityAlert entity to define an Enabled field. The updates to the class are as follows:
public class ProximityAlert extends BaseEntity<ProximityAlert> {
private static long nextId = 0;
public long Id = -1;
public double Lat = 0;
public double Lng = 0;
public String Name = "";
public String Desc = "";
public String Expiration = "";
public int Month = -1;
public int DayOfMonth = -1;
public int Year = -1;
public ArrayList<String> Items = new ArrayList<String>();
public boolean Enabled = false;
public boolean IsNew = true;
public ProximityAlert(long id, double lat, double lng, String name, String desc, int month, int dayOfMonth, int year, boolean enabled) {
this.Lat = lat;
this.Lng = lng;
this.Id = id;
this.Name = name;
this.Desc = desc;
this.Month = month;
this.DayOfMonth = dayOfMonth;
this.Year = year;
this.Enabled = enabled;
}
public ProximityAlert(double lat, double lng, String name, String desc, int month, int dayOfMonth, int year, boolean enabled) {
this(nextId++, lat, lng, name, desc, month, dayOfMonth, year, enabled);
}
public ProximityAlert(long id, String name, String desc) {
this(id, 0.0, 0.0, name, desc, Calendar.getInstance().get(Calendar.MONTH), Calendar.getInstance().get(Calendar.DAY_OF_MONTH), Calendar.getInstance().get(Calendar.YEAR), false);
}
public JSONObject serializeToObj() {
JSONObject serializedObj = new JSONObject();
try {
serializedObj.put("Id", this.Id);
serializedObj.put("Lat", this.Lat);
serializedObj.put("Lng", this.Lng);
serializedObj.put("Name", this.Name);
serializedObj.put("Desc", this.Desc);
serializedObj.put("Month", this.Month);
serializedObj.put("DayOfMonth", this.DayOfMonth);
serializedObj.put("Year", this.Year);
serializedObj.put("Enabled", this.Enabled);
ArrayList<JSONObject> itemObjs = new ArrayList<JSONObject>();
for (String item:this.Items) {
JSONObject serializedItemObj = new JSONObject();
serializedItemObj.put("Item", item);
itemObjs.add(serializedItemObj);
}
serializedObj.put("Items", new JSONArray(itemObjs));
}
catch(Exception ex) {
if (debug) Log.e(AppSettings.DEBUG_TAG, ex.getMessage(), ex);
}
return serializedObj;
}
public ProximityAlert deserializeFromObj(JSONObject obj) throws JSONException {
this.Id = obj.getLong("Id");
this.Lat = obj.getDouble("Lat");
this.Lng = obj.getDouble("Lng");
this.Name = obj.getString("Name");
this.Desc = obj.getString("Desc");
this.Month = obj.getInt("Month");
this.DayOfMonth = obj.getInt("DayOfMonth");
this.Year = obj.getInt("Year");
this.Enabled = obj.getBoolean("Enabled");
if (obj.has("Items")) {
JSONArray jsonObjs = obj.getJSONArray("Items");
this.Items = new ArrayList<String>(jsonObjs.length());
for (int i=0; i<jsonObjs.length(); i++) {
JSONObject itemObj = jsonObjs.getJSONObject(i);
this.Items.add(itemObj.getString("Item"));
}
}
return this;
}
}
The highlighted lines above represent the changes required to support the Enabled field.
Now we take a look at how the Enabled field is used within the ProximityAlertHelper’s addProximityAlertsToLocationManager(…) method. Line 9 below now requires that the alert be enabled before adding it to the LocationManager.
public boolean addProximityAlertsToLocationManager(Context caller, ArrayList<ProximityAlert> alerts) {
if (initialized) {
String context = Context.LOCATION_SERVICE;
LocationManager mgr = ((LocationManager) caller.getSystemService(context));
removeAlerts(caller);
for (int i=1; i <alerts.size(); i++) {
ProximityAlert alert = alerts.get(i);
if (alert.Enabled) {
Intent intent = new Intent(ProximityAlertActivity.class.getName());
intent.putExtra("id", alert.Id);
PendingIntent proximityIntent = PendingIntent.getBroadcast(caller, i, intent, PendingIntent.FLAG_CANCEL_CURRENT);
float proxRadius = Float.parseFloat(PreferenceManager.getDefaultSharedPreferences(caller).getString("proxradius", "2"));
mgr.addProximityAlert(alert.Lat, alert.Lng, proxRadius, -1, proximityIntent);
proximityIntentsMap.put(alert, proximityIntent);
}
}
return true;
}
return false;
}
And lastly, let’s see the actual enabled field within the ProximityAlertSetActivity screen.
Note that the details of integrating the enabled field into the application are straightforward, and begin as follows:
boolean enabled = ((CheckBox) findViewById(R.id.CheckBox_alert_enabled)).isChecked();
I hope this discussion of enhancements to the Proximity Alert Mobile Application helps extend your understanding of how to properly build proximity alert applications using the Android platform.
I look forward to discussing new mobile development concepts in my next post! Please come back soon!
| Print article | This entry was posted by Brian Buikema on July 22, 2010 at 10:46 PM, and is filed under Android, Architecture & Design, Java, Location-Based, Mobile, Proximity Alerts, Technology, Uncategorized. Follow any responses to this post through RSS 2.0. You can leave a response or trackback from your own site. |

about 1 year ago
Nice post and this post helped me alot in my college assignement. Thank you seeking your information.
about 1 year ago
Brian, I am new to android and your posts on proximityalert are great. Am trying to put all of the classes together to build the project but am having a lot of difficulties (Am i missing any classes?). Do you have a link where i can download the source code for the project? Thanks, Antoine.
about 1 year ago
I write these posts to provide insight into concepts and usually do not focus 100% on projects. With that said, can you tell me exactly what you are having trouble with and I will try to help.
about 1 year ago
Brian, appreciate your effort in writing this excellent post. I try to build a project following your guide but am having some difficulties, one of which is the class of BaseEntity. I couldn’t find this class in Android API, could you explain whats the purpose of this class, source code would be most appreciated.
thanks.
Gan
about 1 year ago
Gan,
I believe I inadvertently excluded my BaseEntity class. Let me get this into the post this weekend. Check back Mon. Thanks for pointing this out.
about 1 year ago
It’s up. I just checked.