package org.sana.android.procedure;
import org.sana.R;
import org.w3c.dom.Node;
import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
/**
* GpsElement is a ProcedureElement that is created when a "GPS" element is put
* into an XML procedure description. It allows the user to click on a button to
* grab the current GPS coordinates.
* <p/>
* <ul type="none">
* <li><b>Clinical Use </b> Defined by subclasses.</li>
* For field workers, this is often a useful feature to track patients and where
* they are seen by the health workers.
* <li><b>Collects </b> GPS coordinates as:<br/>
* <ul type="none"><li>
* <code>Latitude: <var>val</var> Longitude: <var>val</var><var>val</var>
* </li></ul>
* </li>
* </ul>
*
* @author Sana Development Team
*/
public class GpsElement extends ProcedureElement implements OnClickListener {
private Button getLocationButton;
private LocationManager locationManager;
private LocationListener locationListener;
private boolean gotCoordinates = false;
private Handler handler = null;
/** Disconnects from the location manager. */
@Override
protected void finalize() throws Throwable {
// We need to make sure that we are not leaving the GPS on.
if (locationManager != null && locationListener != null) {
locationManager.removeUpdates(locationListener);
}
super.finalize(); //not necessary if extending Object.
}
/** {@inheritDoc} */
@Override
public ElementType getType() {
return ElementType.GPS;
}
/** {@inheritDoc} */
@Override
protected View createView(Context c) {
LinearLayout gpsContainer = new LinearLayout(c);
gpsContainer.setOrientation(LinearLayout.VERTICAL);
locationManager = (LocationManager)c.getSystemService(
Context.LOCATION_SERVICE);
if (locationListener == null) {
locationListener = new SanaGPSListener();
}
if(TextUtils.isEmpty(question)) {
question = c.getString(R.string.question_standard_gps_element);
}
getLocationButton = new Button(c);
getLocationButton.setText("Grab GPS Location");
getLocationButton.setOnClickListener(this);
gpsContainer.addView(getLocationButton,
new LinearLayout.LayoutParams(-1,-1,0.1f));
return encapsulateQuestion(c, gpsContainer);
}
/** {@inheritDoc} */
@Override
public void onClick(View v) {
if (v == getLocationButton) {
Log.i(TAG, "Requesting GPS updates to get the current location. ");
gotCoordinates = false;
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 0, 0, locationListener);
getLocationButton.setEnabled(false);
getLocationButton.setText(
getString(R.string.gps_element_acquire_waiting));
Thread gpsWatchdog = new Thread() {
public void run() {
Log.i(TAG, "GPS watchdog turning off GPS.");
locationManager.removeUpdates(locationListener);
if (!gotCoordinates) {
Log.i(TAG, "GPS coordinates were not acquired.");
getLocationButton.setEnabled(true);
getLocationButton.setText(
getString(R.string.gps_element_acquire_waiting));
}
}
};
if (handler == null)
handler = new Handler();
handler.postDelayed(gpsWatchdog, 10000);
}
}
/** {@inheritDoc} */
public void setAnswer(String answer) {
this.answer = answer;
}
/** {@inheritDoc} */
public String getAnswer() {
return answer;
}
/** {@inheritDoc} */
public void buildXML(StringBuilder sb) {
sb.append("<Element type=\"" + getType().name() + "\" id=\"" + id);
sb.append("\" answer=\"" + getAnswer());
sb.append("\" concept=\"" + getConcept());
sb.append("\"/>\n");
}
/** Default constructor. */
private GpsElement(String id, String question, String answer,
String concept, String figure, String audio)
{
super(id, question, answer, concept, figure, audio);
setAnswer("Coordinates not acquired.");
}
/** @see ProcedureElement#fromXML(String, String, String, String, String, String, Node) */
public static GpsElement fromXML(String id, String question, String answer,
String concept, String figure, String audio, Node node)
{
return new GpsElement(id, question, answer, concept, figure, audio);
}
/**
* Listener for changes to GPS service.
*
* @author Sana Development Team
*/
private class SanaGPSListener implements LocationListener {
/** {@inheritDoc} */
@Override
public void onLocationChanged(Location location) {
// Called when the location has changed.
Log.d(TAG, "Got location update :" + location.toString()
+ ". Disabling GPS");
getLocationButton.setEnabled(false);
getLocationButton.setText("Coordinates acquired.");
gotCoordinates = true;
setAnswer("Latitude: " + location.getLatitude() + " Longitude: "
+ location.getLongitude());
locationManager.removeUpdates(locationListener);
}
/** {@inheritDoc} */
@Override
public void onProviderDisabled(String provider) {
// Called when the provider is disabled by the user.
Log.d(TAG, "Provider disabled: " + provider);
getLocationButton.setEnabled(true);
getLocationButton.setText("GPS turned off -- check settings.");
locationManager.removeUpdates(locationListener);
}
/** {@inheritDoc} */
@Override
public void onProviderEnabled(String provider) {
// Called when the provider is enabled by the user.
Log.d(TAG, "Provider enabled: " + provider);
// Do nothing, we should get a location update soon which will
// disable the listener.
}
/** {@inheritDoc} */
@Override
public void onStatusChanged (String provider, int status, Bundle extras)
{
// Called when the provider status changes.
Log.d(TAG, "Provider status changed: " + provider + " status: "
+ status);
if (status == LocationProvider.AVAILABLE) {
// Do nothing, we should get a location update soon which will
// disable the listener.
} else if (status == LocationProvider.OUT_OF_SERVICE ||
status == LocationProvider.TEMPORARILY_UNAVAILABLE)
{
getLocationButton.setEnabled(true);
getLocationButton.setText(
getString(R.string.gps_element_acquire_unavailable));
locationManager.removeUpdates(locationListener);
}
}
}
}