Namespaces

Variants
Actions

Please note that as of October 24, 2014, the Nokia Developer Wiki will no longer be accepting user contributions, including new entries, edits and comments, as we begin transitioning to our new home, in the Windows Phone Development Wiki. We plan to move over the majority of the existing entries over the next few weeks. Thanks for all your past and future contributions.

Revision as of 03:14, 27 January 2014 by kiran10182 (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Creating Emergency alerting app ("Dead man switches")

From Wiki
Jump to: navigation, search
Featured Article
26 Jan
2014

This article details the creation of an Emergency alerting app ("Dead man switch").

Note.pngNote: This is an entry in the Nokia Asha Wiki Competition 2013H2.

Article Metadata
Code Example
Source file: Media:DMS.zip
Tested with
SDK: New Asha SDK
Devices(s): Nokia Asha 501
CompatibilityArticle
Created: shai.i (15 Dec 2013)
Last edited: kiran10182 (27 Jan 2014)

Contents

Introduction

This article describes an emergency alerting app for Nokia Asha platform.

An emergency alerting app can be used to address a number of different use-cases:

  • Location fixing if a user is incapable of reporting their position/problem - for example by a patient that is lost of confused or too injured to respond properly. In this case the app can notify help of where the patient is located. This could be triggered locally by the user or via push notification.
  • Identity information - provide an "About me" text in the phone's screen once an alert has been triggered
  • Personal safety. If the user feels threatened an app can sound a very high pitch annoying alarm that can help deter the attacker as well as alerting the close surrounding of your situation and also notifying your selected contacts of your position.


The app in this article provides the functionality for a user/patient to alert their approximate position via SMS and play loud sounds when the alarm is triggered locally by the user. It does not at this point include remote triggering of the alarm.


Implementation

The app should be very easy to operate and trigger an alert, however its also a good idea to have different settings to customize parameters such as duration to continue to press the button to actually trigger the alert in order to avoid false alerts.

Main screen

The screenshot below shows the main screen of the app. As you can see it composed of mainly a big red button that you can press and based on the settings parameters an alert trigger will occur after X seconds of continuing pressed (with count down effects, beeps, button animation, etc).

Main screen

The full code of the main screen is given here:

package com.future.dms.ui;
 
import java.io.IOException;
import java.util.Timer;
import java.util.TimerTask;
import java.util.Vector;
 
import javax.microedition.io.Connector;
import javax.microedition.location.Criteria;
import javax.microedition.location.Location;
import javax.microedition.location.LocationProvider;
import javax.microedition.location.QualifiedCoordinates;
import javax.microedition.media.Manager;
import javax.microedition.media.MediaException;
import javax.microedition.media.Player;
import javax.wireless.messaging.MessageConnection;
import javax.wireless.messaging.TextMessage;
 
import com.future.dms.Settings;
import com.sun.lwuit.Button;
import com.sun.lwuit.Command;
import com.sun.lwuit.Component;
import com.sun.lwuit.Display;
import com.sun.lwuit.Font;
import com.sun.lwuit.Form;
import com.sun.lwuit.Image;
import com.sun.lwuit.Label;
import com.sun.lwuit.TextArea;
import com.sun.lwuit.events.ActionEvent;
import com.sun.lwuit.events.ActionListener;
import com.sun.lwuit.layouts.BorderLayout;
 
public class MainForm extends Form implements ActionListener {
private Command settingsCommand, aboutCommand, stopCommand;
private Button redButton;
private Label countDownLabel;
private Player beepSound, alarmSound;
private Timer timer;
private TimerTask countDownTask;
public MainForm()
{
super("");
timer = new Timer();
countDownTask = null;
setLayout(new BorderLayout());
setScrollable(false);
beepSound = null;
try
{
settingsCommand = new Command("Settings",Image.createImage(getClass().getResourceAsStream("/settings.png")));
aboutCommand = new Command("About",Image.createImage(getClass().getResourceAsStream("/about.png")));
stopCommand = new Command("Stop",Image.createImage(getClass().getResourceAsStream("/stop.png")));
addCommand(aboutCommand);
setDefaultCommand(settingsCommand);
addCommandListener(this);
final Image regular_red_button = Image.createImage(getClass().getResourceAsStream("/red_200.png"));
final Image pressed_red_button = Image.createImage(getClass().getResourceAsStream("/red_pressed_200.png"));
countDownLabel = new Label();
countDownLabel.getStyle().setFont(Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_PLAIN, Font.SIZE_LARGE));
countDownLabel.getStyle().setFgColor(0xffff0000);
countDownLabel.getStyle().setAlignment(Component.CENTER);
 
try
{
beepSound = Manager.createPlayer(getClass().getResourceAsStream("/countdown_beep.wav"), "audio/wav");
} catch (Exception e)
{}
redButton = new Button() {
public void pressed()
{
countDownTask = new TimerTask() {
int countDownStart = Settings.get().getTimeToHoldToTrigger();
boolean playCountDownSounds = Settings.get().isPlaySoundWhileHold();
public void run()
{
countDownStart--;
if (countDownStart < 0)
{
redButton.setIcon(regular_red_button);
startAlert();
return;
}
Display.getInstance().callSerially(new Runnable() {
public void run()
{
countDownLabel.setText("Alert triggers in: "+countDownStart);
}
});
if(playCountDownSounds && beepSound!=null)
try
{
beepSound.start();
} catch (MediaException e)
{}
 
}
};
redButton.setIcon(pressed_red_button);
timer.schedule(countDownTask, 1000, 1000);
if(Settings.get().isPlaySoundWhileHold() && beepSound!=null)
try
{
beepSound.start();
} catch (MediaException e)
{}
countDownLabel.setText("Alert triggers in: "+Settings.get().getTimeToHoldToTrigger());
if (!MainForm.this.contains(countDownLabel))
MainForm.this.addComponent(BorderLayout.NORTH, countDownLabel);
MainForm.this.revalidate();
super.pressed();
}
public void pointerReleased(int x, int y)
{
if (countDownTask != null)
countDownTask.cancel();
countDownTask = null;
MainForm.this.removeComponent(countDownLabel);
redButton.setIcon(regular_red_button);
MainForm.this.revalidate();
try
{
beepSound.stop();
} catch (MediaException e)
{}
super.pointerReleased(x, y);
}
};
redButton.setUIID("Label");
redButton.setVerticalAlignment(Component.CENTER);
redButton.getUnselectedStyle().setAlignment(Component.CENTER);
redButton.getSelectedStyle().setAlignment(Component.CENTER);
redButton.getPressedStyle().setAlignment(Component.CENTER);
redButton.setIcon(regular_red_button);
addComponent(BorderLayout.CENTER, redButton);
} catch (IOException e1)
{}
}
 
public void startAlert()
{
if (countDownTask != null)
countDownTask.cancel();
countDownTask = null;
Display.getInstance().callSerially(new Runnable() {
public void run()
{
MainForm.this.removeAll();
MainForm.this.removeAllCommands();
TextArea t = new TextArea(2, 20);
t.setUIID("Label");
t.setEditable(false);
t.setFocusable(false);
t.setGrowByContent(true);
t.setText(Settings.get().getAboutMeText());
MainForm.this.setScrollableY(true);
MainForm.this.addComponent(BorderLayout.CENTER, t);
MainForm.this.addCommand(stopCommand);
MainForm.this.revalidate();
MainForm.this.repaint();
}
});
if (Settings.get().isPlayAlertSound())
{
try
{
alarmSound = Manager.createPlayer(getClass().getResourceAsStream("/alarm.mp3"), "audio/mp3");
alarmSound.setLoopCount(-1);
alarmSound.start();
} catch (Exception e)
{}
}
if (Settings.get().isSendSMS())
{
sendSMSToContacts();
}
}
 
public void sendSMSToContacts()
{
new Thread()
{
public void run()
{
Vector v = Settings.get().getSmsPhonesList();
String message = Settings.get().getAlertText();
replace(message,"[location]",getLocation());
for (int i = 0; i < v.size(); i++)
{
try {
MessageConnection conn = (MessageConnection) Connector.open("sms://"+v.elementAt(i)+":0");
TextMessage msg = (TextMessage)conn.newMessage(MessageConnection.TEXT_MESSAGE);
msg.setPayloadText("");
conn.send(msg);
conn.close();
}
catch (Exception e) {}
}
}
}.start();
}
 
private static String getLocation()
{
try {
double lat,lon;
QualifiedCoordinates qc=null;
Criteria cr=new Criteria();
cr.setHorizontalAccuracy(500);
cr.setVerticalAccuracy(500);
cr.setPreferredPowerConsumption(Criteria.POWER_USAGE_LOW);
LocationProvider lp=LocationProvider.getInstance(cr);
Location loc=lp.getLocation(60);
qc=loc.getQualifiedCoordinates();
/*AddressInfo adinfo = loc.getAddressInfo();
String place="";
if(adinfo!=null){
place = adinfo.getField(AddressInfo.COUNTRY);
}*/

 
// To get the latitude and longitude
lat = qc.getLatitude();
lon = qc.getLongitude();
return lat+","+lon;
}
catch (Exception e)
{
return "UNKNOWN";
}
}
 
private String replace( String str, String pattern, String replace )
{
int s = 0;
int e = 0;
StringBuffer result = new StringBuffer();
 
while ( (e = str.indexOf( pattern, s ) ) >= 0 )
{
result.append(str.substring( s, e ) );
result.append( replace );
s = e+pattern.length();
}
result.append( str.substring( s ) );
return result.toString();
}
 
public void actionPerformed(ActionEvent ae)
{
if (ae.getCommand() == settingsCommand)
{
UIManager.get().showSettings();
return;
}
if (ae.getCommand() == aboutCommand)
{
new AboutForm(this).show();
return;
}
if (ae.getCommand() == stopCommand)
{
if (alarmSound != null)
try
{
alarmSound.close();
alarmSound = null;
} catch (Exception e) {}
MainForm.this.removeAll();
MainForm.this.removeAllCommands();
setScrollable(false);
addCommand(aboutCommand);
setDefaultCommand(settingsCommand);
addComponent(BorderLayout.CENTER, redButton);
MainForm.this.revalidate();
MainForm.this.repaint();
return;
}
 
}
 
}

Some important methods in the above code are:

  • getLocation() - to get the user current GPS location.
  • sendSMSToContacts() - sends the alert message to the selected contacts of the phone via SMS
  • startAlert() - is called when the alert is to be triggered.

Settings screen

The app settings screen is shown below. It has options to control the behaviour of the app - settings that are not visible on the screenshot include setting the text of the alert SMS message, selecting which contacts should receive the SMS, etc)

Settings screen

The following code is the full code for the settings screen.

package com.future.dms.ui;
 
import java.io.IOException;
 
import com.future.dms.Settings;
import com.sun.lwuit.Button;
import com.sun.lwuit.CheckBox;
import com.sun.lwuit.Command;
import com.sun.lwuit.Container;
import com.sun.lwuit.Dialog;
import com.sun.lwuit.Font;
import com.sun.lwuit.Form;
import com.sun.lwuit.Image;
import com.sun.lwuit.TextArea;
import com.sun.lwuit.events.ActionEvent;
import com.sun.lwuit.events.ActionListener;
import com.sun.lwuit.layouts.BorderLayout;
import com.sun.lwuit.layouts.BoxLayout;
 
public class SettingsForm extends Form implements ActionListener
{
private Command saveCommand, backCommand;
private Button numberOfSecondsForAlarm,editSMSMessageButton, editAboutMeMessageButton, editContactsButton;
private CheckBox showCountDownChkbox, playBeepWhileHoldChkbox, playAlertSoundChkbox, sendSMSChkbox, ShareSMSStatusFBChkbox;
public SettingsForm()
{
super("Settings");
setLayout(new BoxLayout(BoxLayout.Y_AXIS));
try
{
saveCommand = new Command("Save",Image.createImage(getClass().getResourceAsStream("/check.png")));
} catch (IOException e)
{}
addCommand(saveCommand);
setBackCommand(backCommand = new Command("Back"));
addCommandListener(this);
Container c;
c = new Container(new BorderLayout());
//c.setUIID("Button");
TextArea t = new TextArea("Number of seconds to hold button to activate alert:");
t.setUIID("Label");
t.setEditable(false);
t.setFocusable(false);
t.setGrowByContent(true);
t.getUnselectedStyle().setFont(Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_MEDIUM));
t.getPressedStyle().setFont(Font.createSystemFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_MEDIUM));
c.addComponent(BorderLayout.CENTER, t);
numberOfSecondsForAlarm = new Button();
numberOfSecondsForAlarm.setUIID("Label");
c.setLeadComponent(numberOfSecondsForAlarm);
numberOfSecondsForAlarm.addActionListener(this);
c.addComponent(BorderLayout.EAST, numberOfSecondsForAlarm);
addComponent(c);
showCountDownChkbox = new CheckBox("Show countdown timer on press");
addComponent(showCountDownChkbox);
playBeepWhileHoldChkbox = new CheckBox("Play beeps on countdown");
addComponent(playBeepWhileHoldChkbox);
playAlertSoundChkbox = new CheckBox("Play Alarm sound on alert");
addComponent(playAlertSoundChkbox);
sendSMSChkbox = new CheckBox("Send SMS to selected contacts on alert");
addComponent(sendSMSChkbox);
ShareSMSStatusFBChkbox = new CheckBox("Post alert to Facebook");
ShareSMSStatusFBChkbox.addActionListener(this);
//addComponent(ShareSMSStatusFBChkbox);
 
editSMSMessageButton = new Button("Edit SMS message");
editSMSMessageButton.addActionListener(this);
addComponent(editSMSMessageButton);
editAboutMeMessageButton = new Button("Edit About me text");
editAboutMeMessageButton.addActionListener(this);
addComponent(editAboutMeMessageButton);
editContactsButton = new Button("Select contacts to alert");
editContactsButton.addActionListener(this);
addComponent(editContactsButton);
}
 
private void loadSettings()
{
numberOfSecondsForAlarm.setText(Settings.get().getTimeToHoldToTrigger()+"");
showCountDownChkbox.setSelected(Settings.get().isShowCountDownWhileHold());
playBeepWhileHoldChkbox.setSelected(Settings.get().isPlaySoundWhileHold());
playAlertSoundChkbox.setSelected(Settings.get().isPlayAlertSound());
sendSMSChkbox.setSelected(Settings.get().isSendSMS());
ShareSMSStatusFBChkbox.setSelected(Settings.get().isShareOnFB());
}
 
private void saveSettings()
{
Settings.get().setTimeToHoldToTrigger(Integer.parseInt(numberOfSecondsForAlarm.getText()));
Settings.get().setShowCountDownWhileHold(showCountDownChkbox.isSelected());
Settings.get().setPlaySoundWhileHold(playBeepWhileHoldChkbox.isSelected());
Settings.get().setPlayAlertSound(playAlertSoundChkbox.isSelected());
Settings.get().setSendSMS(sendSMSChkbox.isSelected());
Settings.get().setShareOnFB(ShareSMSStatusFBChkbox.isSelected());
}
 
public void actionPerformed(ActionEvent ae)
{
if (ae.getCommand() == saveCommand)
{
saveSettings();
}
if (ae.getCommand() == backCommand)
{
if (Dialog.show("Note", "Any changes will not be saved", "Save", "Dont save"))
{
saveSettings();
}
UIManager.get().showMain();
return;
}
if (ae.getComponent() == numberOfSecondsForAlarm)
{
new EditSecondsForm(this).show();
return;
}
if (ae.getComponent() == editSMSMessageButton)
{
new EditSMSForm(this).show();
return;
}
if (ae.getComponent() == editAboutMeMessageButton)
{
new EditAboutMeForm(this).show();
return;
}
if (ae.getComponent() == editContactsButton)
{
new SelectContactsForm(this).show();
return;
}
}
 
public void show()
{
Settings.get().loadSettings();
loadSettings();
super.show();
}
 
public void showBack()
{
numberOfSecondsForAlarm.setText(EditSecondsForm.SELECTED_SECONDS+"");
super.showBack();
}
}

An interesting piece of code that goes along with the settings screen is the "settings domai"n class

package com.future.dms;
 
import java.util.Hashtable;
import java.util.Vector;
 
import com.sun.lwuit.io.Storage;
 
 
public class Settings {
 
private static Settings instance;
 
private int timeToHoldToTrigger = 3;
private boolean showCountDownWhileHold = true;
private boolean playSoundWhileHold = true;
private boolean playAlertSound = true;
private boolean sendSMS = false;
private Vector smsPhonesList = new Vector();
 
private boolean shareOnFB = false;
 
private static final String ALERT_TEXT_DEFAULT = "I have triggered an alert at [location], send help to my position.";
private static final String ABOUT_TEXT_DEFAULT = "This text will be visible on the screen after you trigger the alert, you can write here general information about you, your home address, known allergies or other medical issues.";
private String alertText = "";
 
private String aboutMeText = "";
 
private Settings()
{
loadSettings();
}
 
public static Settings get()
{
if (instance == null)
instance = new Settings();
return instance;
}
 
public void loadSettings()
{
alertText = (String) Storage.getInstance().readObject("DMS_SMSText");
if (alertText == null)
alertText = ALERT_TEXT_DEFAULT;
 
aboutMeText = (String) Storage.getInstance().readObject("DMS_AboutMeText");
if (aboutMeText == null)
aboutMeText = ABOUT_TEXT_DEFAULT;
Hashtable hash = (Hashtable) Storage.getInstance().readObject("DMS_SETTINGS");
if (hash == null)
return;
timeToHoldToTrigger = Integer.parseInt((String) hash.get("timeToHoldToTrigger"));
showCountDownWhileHold = Integer.parseInt((String) hash.get("showCountDownWhileHold")) == 1;
playSoundWhileHold = Integer.parseInt((String) hash.get("playSoundWhileHold")) == 1;
playAlertSound = Integer.parseInt((String) hash.get("playAlertSound")) == 1;
sendSMS = Integer.parseInt((String) hash.get("sendSMS")) == 1;
smsPhonesList = (Vector) hash.get("smsPhonesList");
shareOnFB = Integer.parseInt((String) hash.get("shareOnFB")) == 1;
}
 
public void saveSettings()
{
Hashtable hash = new Hashtable();
hash.put("timeToHoldToTrigger", String.valueOf(timeToHoldToTrigger));
hash.put("showCountDownWhileHold", showCountDownWhileHold?"1":"0");
hash.put("playSoundWhileHold", playSoundWhileHold?"1":"0");
hash.put("playAlertSound", playAlertSound?"1":"0");
hash.put("sendSMS", sendSMS?"1":"0");
hash.put("smsPhonesList", String.valueOf(timeToHoldToTrigger));
hash.put("shareOnFB", shareOnFB?"1":"0");
hash.put("smsPhonesList", smsPhonesList);
Storage.getInstance().writeObject("DMS_SMSText", alertText);
}
 
public void saveAlertText(String text)
{
Storage.getInstance().writeObject("DMS_SMSText",alertText = text);
}
 
public String getAlertText()
{
return alertText;
}
 
public void saveAboutMeText(String text)
{
Storage.getInstance().writeObject("DMS_AboutMeText",aboutMeText = text);
}
 
public String getAboutMeText()
{
return aboutMeText;
}
 
/**
* @return the timeToHoldToTrigger
*/

public int getTimeToHoldToTrigger()
{
return timeToHoldToTrigger;
}
 
/**
* @param timeToHoldToTrigger the timeToHoldToTrigger to set
*/

public void setTimeToHoldToTrigger(int timeToHoldToTrigger)
{
this.timeToHoldToTrigger = timeToHoldToTrigger;
}
 
/**
* @return the showCountDownWhileHold
*/

public boolean isShowCountDownWhileHold()
{
return showCountDownWhileHold;
}
 
/**
* @param showCountDownWhileHold the showCountDownWhileHold to set
*/

public void setShowCountDownWhileHold(boolean showCountDownWhileHold)
{
this.showCountDownWhileHold = showCountDownWhileHold;
}
 
/**
* @return the playSoundWhileHold
*/

public boolean isPlaySoundWhileHold()
{
return playSoundWhileHold;
}
 
/**
* @param playSoundWhileHold the playSoundWhileHold to set
*/

public void setPlaySoundWhileHold(boolean playSoundWhileHold)
{
this.playSoundWhileHold = playSoundWhileHold;
}
 
/**
* @return the playAlertSound
*/

public boolean isPlayAlertSound()
{
return playAlertSound;
}
 
/**
* @param playAlertSound the playAlertSound to set
*/

public void setPlayAlertSound(boolean playAlertSound)
{
this.playAlertSound = playAlertSound;
}
 
/**
* @return the sendSMS
*/

public boolean isSendSMS()
{
return sendSMS;
}
 
/**
* @param sendSMS the sendSMS to set
*/

public void setSendSMS(boolean sendSMS)
{
this.sendSMS = sendSMS;
}
 
/**
* @return the smsPhonesList
*/

public Vector getSmsPhonesList()
{
return smsPhonesList;
}
 
/**
* @param smsPhonesList the smsPhonesList to set
*/

public void setSmsPhonesList(Vector smsPhonesList)
{
this.smsPhonesList = smsPhonesList;
}
 
/**
* @return the shareOnFB
*/

public boolean isShareOnFB()
{
return shareOnFB;
}
 
/**
* @param shareOnFB the shareOnFB to set
*/

public void setShareOnFB(boolean shareOnFB)
{
this.shareOnFB = shareOnFB;
}
}

Summary

The full source code for this project is attached, so you can look at the different pieces and see how they are implemented: File:DMS.zip

Many more features that can be added to this app such as sharing the alert to Facebook post via the new OAuth API as well as the SMS (if you note I had already begun doing this with the settings object but did not have time to complete this for now).

Another feature is using a server for push notification to connect with the alerting user or who ever found the phone if the user is unable to respond, but this is beyond the scope of this article implementation.

This page was last modified on 27 January 2014, at 03:14.
290 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×