Anonymous avatar Anonymous committed fc9c86b

* Changed minimum/target SDK versions to 8 min / 10 target (2.2.x min /
2.3.3+ target);
* Changed log files path and naming template;
* Implemented taking of snapshots with the camera (focus @ infinity);
* Reimplemented preferences activity - supported settings are interval before
logging activation, interval between subsequent camera snapshots, enabling of
GPS coordinate SMS messages, SMS message recipient's phone number, interval
before the application starts sending SMS messages and interval between SMS
messages;
* Refactored application logic to make it more encapsulated;
* Removed activation sound.

Comments (0)

Files changed (17)

AndroidManifest.xml

 <?xml version="1.0" encoding="utf-8"?>
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-      package="to.rcpt.icarus"
-      android:versionCode="1"
-      android:versionName="1.0">
-    <application android:label="@string/app_name" android:debuggable="true" android:name=".IcarusApplication">
-        <activity android:name=".IcarusManager"
-                  android:label="@string/app_name">
+          package="to.rcpt.icarus"
+          android:versionCode="1"
+          android:versionName="1.0">
+    
+    <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="10" />
+
+    <!--<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />-->
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.SEND_SMS" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+    <uses-permission android:name="android.permission.CAMERA" />
+    
+    <application android:label="@string/app_name"
+                 android:name=".IcarusApplication">
+        <activity android:name=".MainActivity"
+                  android:label="@string/app_name"
+                  android:screenOrientation="portrait">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
-        <activity android:name=".IcarusPreferenceActivity"
-        android:label="Icarus Preferences"/>
-<receiver android:name=".IcarusAutoStarter">
-            
+        <activity android:name=".PreferencesActivity"
+                  android:label="@string/app_name"
+                  android:screenOrientation="portrait">
+        </activity>
+        <receiver android:name=".IcarusAutoStarter">
             <intent-filter>
                 <action android:name="android.intent.action.BOOT_COMPLETED"/>
             </intent-filter>
         </receiver>
-            <service android:name=".IcarusService"/>
-        
+        <service android:name=".IcarusService"/>
     </application>
-    <uses-sdk android:minSdkVersion="4" />
-
-
-<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
-<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
-<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
-<uses-permission android:name="android.permission.SEND_SMS"></uses-permission>
-<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>
-
-
-
-
-
-
-
-<uses-permission android:name="android.permission.WAKE_LOCK"></uses-permission>
-
-
-<uses-permission android:name="android.permission.CAMERA"></uses-permission>
-
-
-
-
-
 </manifest> 

res/layout/icarus_layout.xml

+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+
+    <ToggleButton
+        android:id="@+id/startButton"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="5dip"
+        android:textOn="@string/start_button_text_on"
+        android:textOff="@string/start_button_text_off" />
+    
+    <TextView
+        android:id="@+id/statusTextView"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/status_inactive" />
+
+    <LinearLayout
+        android:id="@+id/sensorsList"
+        android:layout_width="fill_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1"
+    	android:orientation="vertical" >
+    </LinearLayout>
+
+</LinearLayout>

res/layout/preferences_layout.xml

+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:orientation="vertical" >
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" >
+
+        <CheckBox
+            android:id="@+id/logDelayCheckBox"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dip"
+            android:text="@string/log_delay_caption" />
+
+        <EditText
+            android:id="@+id/logDelayEditText"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_margin="5dip"
+            android:ems="10"
+            android:inputType="number" >
+
+        </EditText>
+
+        <TextView
+            android:id="@+id/logDelayUnitsTextView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dip"
+            android:textSize="16dip"
+            android:text="@string/units_minutes" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" >
+
+        <TextView
+            android:id="@+id/cameraIntervalTextView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dip"
+            android:textSize="16dip"
+            android:text="@string/camera_interval_caption" />
+
+        <EditText
+            android:id="@+id/cameraIntervalEditText"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_margin="5dip"
+            android:ems="10"
+            android:inputType="number" >
+
+        </EditText>
+
+        <TextView
+            android:id="@+id/cameraIntervalUnitsTextView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dip"
+            android:textSize="16dip"
+            android:text="@string/units_seconds" />
+
+    </LinearLayout>
+    
+    <CheckBox
+        android:id="@+id/smsEnableCheckBox"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_margin="5dip"
+        android:text="@string/sms_enable_caption" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" >
+
+        <TextView
+            android:id="@+id/smsPhoneTextBox"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dip"
+            android:textSize="16dip"
+            android:text="@string/sms_phone_caption" />
+
+        <EditText
+            android:id="@+id/smsPhoneEditText"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_margin="5dip"
+            android:ems="10"
+            android:inputType="phone" >
+
+        </EditText>
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" >
+
+        <CheckBox
+            android:id="@+id/smsDelayCheckBox"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dip"
+            android:text="@string/sms_delay_caption" />
+
+        <EditText
+            android:id="@+id/smsDelayEditText"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_margin="5dip"
+            android:ems="10"
+            android:inputType="number" >
+
+        </EditText>
+
+        <TextView
+            android:id="@+id/smsDelayUnitsTextView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dip"
+            android:textSize="16dip"
+            android:text="@string/units_minutes" />
+
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" >
+
+        <TextView
+            android:id="@+id/smsIntervalTextView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dip"
+            android:textSize="16dip"
+            android:text="@string/sms_interval_caption" />
+
+        <EditText
+            android:id="@+id/smsIntervalEditText"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1"
+            android:layout_margin="5dip"
+            android:ems="10"
+            android:inputType="number" >
+
+        </EditText>
+
+        <TextView
+            android:id="@+id/smsIntervalUnitsTextView"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_margin="5dip"
+            android:textSize="16dip"
+            android:text="@string/units_minutes" />
+
+    </LinearLayout>
+
+</LinearLayout>
Add a comment to this file

res/raw/icarusactivated.wav

Binary file removed.

res/values/strings.xml

 <resources><string name="app_name">Icarus</string>
+<string name="start_button_text_on">Stop Icarus</string><string name="start_button_text_off">Start Icarus</string>
+<string name="status_inactive">Sensor logging not activated.</string>
+<string name="status_no_gps">No GPS coords received yet.</string>
+<string name="status_delayed_start" >Logging will start at %1$tH:%1$tM:%1$tS</string><string name="menu_preferences_caption">Preferences</string><string name="log_delay_caption">Delay start by</string>
+<string name="camera_interval_caption">Camera interval:</string>
+
+
+
+<string name="sms_enable_caption">Send location SMS</string>
+<string name="sms_phone_caption">SMS phone:</string><string name="sms_delay_caption">Delay SMS by</string><string name="sms_interval_caption">Message interval:</string>
+
+<string name="units_minutes">minute(s)</string>
+<string name="units_seconds">second(s)</string>
+<string name="log_file_name">IcarusLog.%1$tm-%1$td.%1$tH-%1$tM-%1$tS.csv</string>
+<string name="photo_file_name">Icarus.%1$tm-%1$td.%1$tH-%1$tM-%1$tS.jpg</string>
+
+
+
 </resources>

src/to/rcpt/icarus/DataSource.java

 import java.util.LinkedList;
 import java.util.List;
 
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.location.Location;
 import android.location.LocationListener;
 import android.location.LocationManager;
+import android.os.BatteryManager;
 import android.os.Bundle;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
 
-public class DataSource implements LocationListener {
+class DataSource implements LocationListener {
+	
 	interface IcarusListener {
 		public void logDataEvent(String name, String data, long ts,
 				boolean gsmOk);
 	};
 
-	LinkedList<IcarusListener> listeners;
-
 	private class SensorEventAdapter implements SensorEventListener {
-		private DataSource ds;
-		private Sensor s;
 
 		public SensorEventAdapter(Sensor s, DataSource ds) {
 			this.s = s;
-			this.ds = ds;
 		}
 
 		public void onAccuracyChanged(Sensor sensor, int accuracy) {
 		public void onSensorChanged(SensorEvent event) {
 			String sdata = "";
 			for (float f : event.values)
-				sdata += " " + f;
-			this.ds.logDataEvent(event.sensor.getName(), sdata, 0);
+			{
+				if (sdata.length() > 0)
+					sdata += ",";
+				
+				sdata += f;
+			}
+			logDataEvent(event.sensor.getName(), sdata, 0);
 		}
 
 		public void startSensor() {
-			sensors.registerListener(this, s, SensorManager.SENSOR_DELAY_UI);
+			sensors.registerListener(this, s, SensorManager.SENSOR_DELAY_NORMAL);
 		}
 
 		public void stopSensor() {
 			sensors.unregisterListener(this);
 		}
+		
+		private Sensor s;
+	}
+	
+	private class BatteryReceiver extends BroadcastReceiver {
+		
+		@Override
+		public void onReceive(Context context, Intent intent) {
+			if (!active)
+				return;
+			
+            int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
+            int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
+            int temp = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, -1);
+            int voltage = intent.getIntExtra(BatteryManager.EXTRA_VOLTAGE, -1);
+            
+            String sdata = String.format("%d,%d,%d,%d", level, scale, temp, voltage);
+            logDataEvent("Battery", sdata, 0);
+        }
+		
+		public void start() {
+			active = true;
+		}
+		
+		public void stop() {
+			active = false;
+		}
+		
+		private boolean active = false;
 	}
 
 	private class StateListener extends PhoneStateListener {
-		private DataSource ds;
 
 		public StateListener(DataSource ds) {
 			this.ds = ds;
 			this.ds.gsmState = newGsmState;
 			this.ds.logDataEvent("gsmState changed", "" + gsmState, 0);
 		}
-	}
-
-	public int gsmState;
-
-	private SensorManager sensors;
-	private List<SensorEventAdapter> adapterlist;
-
-	private LocationManager gps;
-	private TelephonyManager telephonyManager;
-	private String locationProvider;
-
-	public void addDataListener(IcarusListener newListener) {
-		listeners.add(newListener);
-	}
-
-	public void removeDataListener(IcarusListener oldListener) {
-		listeners.remove(oldListener);
+		
+		private DataSource ds;
 	}
 
 	public DataSource(Context ctx) {
 		c.setAltitudeRequired(true);
 		c.setSpeedRequired(true);
 		locationProvider = gps.getBestProvider(c, true);
+		
+		// set up battery receiver
+		batteryRecvr = new BatteryReceiver();
+		IntentFilter batteryFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+		ctx.registerReceiver(batteryRecvr, batteryFilter);
+	}
+
+	public void onLocationChanged(Location l) {
+		String data = String.format("%f,%f,%f", l.getLatitude(), l.getLongitude(), l.getAltitude());
+		logDataEvent("gps", data, l.getTime());
+	}
+
+	public void onProviderDisabled(String provider) {
+	}
+
+	public void onProviderEnabled(String provider) {
+	}
+
+	public void onStatusChanged(String provider, int status, Bundle extras) {
+	}
+
+	public void addDataListener(IcarusListener newListener) {
+		listeners.add(newListener);
+	}
+
+	public void removeDataListener(IcarusListener oldListener) {
+		listeners.remove(oldListener);
 	}
 
 	public void start() {
 		gps.requestLocationUpdates(locationProvider, 0, 0, this);
 		for (SensorEventAdapter s : adapterlist)
 			s.startSensor();
-	}
-
-	public void logDataEvent(String name, String data, long ts) {
-		for (IcarusListener listener : listeners)
-			listener.logDataEvent(name, data, ts,
-					this.gsmState == ServiceState.STATE_IN_SERVICE);
-	}
-
-	public void onLocationChanged(Location l) {
-		logDataEvent("gps", l.getLatitude() + "," + l.getLongitude() + " "
-				+ l.getAltitude(), l.getTime());
-	}
-
-	public void onProviderDisabled(String providezr) {
-	}
-
-	public void onProviderEnabled(String providezr) {
-	}
-
-	public void onStatusChanged(String providezr, int status, Bundle extras) {
+		batteryRecvr.start();
 	}
 
 	public void stop() {
 		gps.removeUpdates(this);
 		for (SensorEventAdapter s : adapterlist)
 			s.stopSensor();
+		batteryRecvr.stop();
+	}
+
+	private int gsmState;
+	
+	private LinkedList<IcarusListener> listeners;
+	
+	private SensorManager sensors;
+	private List<SensorEventAdapter> adapterlist;
+
+	private LocationManager gps;
+	private TelephonyManager telephonyManager;
+	private String locationProvider;
+	
+	private BatteryReceiver batteryRecvr;
+
+	private void logDataEvent(String name, String data, long ts) {
+		for (IcarusListener listener : listeners)
+			listener.logDataEvent(name, data, ts,
+					this.gsmState == ServiceState.STATE_IN_SERVICE);
 	}
 }

src/to/rcpt/icarus/IcarusApplication.java

 package to.rcpt.icarus;
 
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-
 public class IcarusApplication extends android.app.Application {
+	
+	static final int NOTIFY_ICARUS_ACTIVE = 0;
+	static final String PREFERENCES = "to.rcpt.icarus.PREFERENCES";
+	
 	private DataSource ds;
-
-	SharedPreferences prefs;
-
-	static final int NOTIFY_ICARUS_ACTIVE = 0;
+	private IcarusPreferences prefs;
 
 	public DataSource getDataSource() {
 		if (ds == null)
 			ds = new DataSource(this);
 		return ds;
 	}
-
-	public SharedPreferences getIcarusPreferences() {
+	
+	public IcarusPreferences getPreferences() {
 		if (prefs == null)
-			prefs = PreferenceManager.getDefaultSharedPreferences(this);
+			prefs = new IcarusPreferences(this);
 		return prefs;
 	}
 }

src/to/rcpt/icarus/IcarusAutoStarter.java

 
 	@Override
 	public void onReceive(final Context ctx, Intent intent) {
-		IcarusService.activateIfNecessary(ctx);
+		IcarusService.startService(ctx);
 	}
 };

src/to/rcpt/icarus/IcarusCamera.java

+package to.rcpt.icarus;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import android.content.Context;
+import android.hardware.Camera;
+import android.os.Environment;
+import android.view.SurfaceView;
+
+class IcarusCamera {
+	
+	public IcarusCamera(Context ctx) {
+		super();
+		
+		IcarusApplication app = (IcarusApplication) ctx.getApplicationContext();
+		IcarusPreferences prefs = app.getPreferences();
+		cameraInterval = prefs.getCameraInterval() * 1000;
+		
+		active = false;
+		timer = new Timer(true);
+		preview = new SurfaceView(ctx);
+		camera = Camera.open();
+		
+		configureCamera();
+		
+		try {
+			camera.setPreviewDisplay(preview.getHolder());
+		} catch (IOException e) {
+			throw new RuntimeException(e);
+		}
+		
+		String storagePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/_ExternalSD";
+		File photosDir = new File(storagePath, "icarus_photos");
+		photosDir.mkdir();
+		photosPath = photosDir.getAbsolutePath();
+	}
+	
+	public void start() {
+		timer.schedule(cameraTask, 0, cameraInterval);
+		active = true;
+	}
+	
+	public void stop() {
+		timer.purge();
+		camera.stopPreview();
+		active = false;
+	}
+	
+	public void release() {
+		camera.release();
+		camera = null;
+		timer.cancel();
+	}
+	
+	private int cameraInterval; // milliseconds
+	
+	private boolean active;
+	private Timer timer;
+	private SurfaceView preview;
+	private Camera camera;
+	private String photosPath;
+	
+	private Camera.PictureCallback photoCallback = new Camera.PictureCallback() {
+		
+		@Override
+		public void onPictureTaken(byte[] data, Camera camera) {
+			String fileName = String.format("Icarus.%1$tm-%1$td.%1$tH-%1$tM-%1$tS.jpg", Calendar.getInstance());
+			File f = new File(photosPath, fileName);
+			try {
+				f.createNewFile();
+				FileOutputStream stream = new FileOutputStream(f, true);
+				stream.write(data);
+				stream.flush();
+				stream.close();
+			} catch (Exception e) {
+				//
+			}			
+		}
+	};
+	
+	private TimerTask cameraTask = new TimerTask() {
+		
+		@Override
+		public void run() {
+			if (active) {
+				camera.startPreview();
+				camera.takePicture(null, null, photoCallback);
+			}
+		}
+	};
+	
+	private void configureCamera() {
+		Camera.Parameters params = camera.getParameters();
+		
+		params.setFocusMode(Camera.Parameters.FOCUS_MODE_INFINITY);
+		
+		params.setExposureCompensation(-4);
+		
+		params.setPictureFormat(0x100); // JPEG
+		
+		List<Camera.Size> sizes = params.getSupportedPictureSizes();
+		Camera.Size maxSize = sizes.get(0);
+		params.setPictureSize(maxSize.width, maxSize.height);
+		
+		params.setJpegQuality(100);
+		
+		camera.setParameters(params);
+	}
+}

src/to/rcpt/icarus/IcarusLogger.java

 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.Calendar;
 
-import android.telephony.SmsManager;
-import android.text.format.Time;
-import android.widget.CompoundButton;
+import android.os.Environment;
 
-public class IcarusLogger implements DataSource.IcarusListener {
-
-	public final String defaultDestPhoneNumber = "2096270247";
-	String destPhoneNumber;
-	final String logfileName = "IcarusLog";
-	final long sendInterval = 600000;
-
-	private PrintWriter file;
-	private SmsManager smsmanager;
-	private long lastSent;
-
-	private boolean logging;
-
-	public IcarusLogger(IcarusApplication app) {
-		smsmanager = SmsManager.getDefault();
-		lastSent = 0;
+class IcarusLogger implements DataSource.IcarusListener {
+	public IcarusLogger() {
 		logging = false;
-
-		destPhoneNumber = app.getIcarusPreferences().getString(
-				"targetPhoneNumber", defaultDestPhoneNumber);
 	}
 
 	public void logDataEvent(String name, String data, long ts,
 			return;
 
 		// update the file
-		file.println(System.currentTimeMillis() + " " + name + ": " + data
-				+ " " + ts);
-
-		if (name != "gps") // only send SMS for gps coords
-			return;
-
-		// send SMS if okay to do so
-		boolean sendIntervalExceeded = lastSent < (ts - sendInterval);
-		if (hasService && sendIntervalExceeded) {
-			smsmanager.sendTextMessage(destPhoneNumber, null, data, null, null);
-			lastSent = ts;
+		try {
+			file.println(String.format("%tc,%s,%s,%d", Calendar.getInstance(), name, data, ts));
+		} catch (Exception e) {
+			//
 		}
 	}
 
-	public void onCheckedChanged(CompoundButton arg0, boolean isChecked) {
-		logging = isChecked;
-		if (isChecked)
-			this.startLogging();
-		else
-			this.stopLogging();
-	}
-
-	public void startLogging() {
+	public void start() {
 		try {
-			Time t = new Time();
-			t.setToNow();
-			File f = new File("/sdcard", logfileName + "." + t.format3339(true));
+			String logsPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/_ExternalSD/icarus_logs";
+			File logsDir = new File(logsPath);
+			logsDir.mkdir();
+			String fileName = String.format("IcarusLog.%1$tm-%1$td.%1$tH-%1$tM-%1$tS.csv", Calendar.getInstance());
+			File f = new File(logsPath, fileName);
 			try {
 				f.createNewFile();
 			} catch (IOException e) {
 		this.logDataEvent("log", "Logging Started", 0, false);
 	}
 
-	public void stopLogging() {
+	public void stop() {
 		if (logging)
 			return;
 		file.close();
 		logging = false;
 	}
 
+	private boolean logging;
+	private PrintWriter file;
 }

src/to/rcpt/icarus/IcarusManager.java

-package to.rcpt.icarus;
-
-import java.util.HashMap;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-import android.view.Menu;
-import android.view.MenuItem;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-public class IcarusManager extends Activity implements
-		DataSource.IcarusListener {
-
-	private TextView tv;
-	private LinearLayout ll;
-	private int index;
-	private HashMap<String, TextView> dataViews;
-
-	public void logDataEvent(String name, String data, long ts,
-			boolean hasService) {
-		if (ts > 0)
-			tv.setText("GPS Timestamp: " + ts + " Cell service: "
-					+ (hasService ? "yes" : "no"));
-		TextView t;
-		if (!dataViews.containsKey(name)) {
-			t = new TextView(this);
-			ll.addView(t, index++);
-			dataViews.put(name, t);
-		} else
-			t = dataViews.get(name);
-		t.setText(name + " " + data);
-	}
-
-	public IcarusApplication getIcarusApplication() {
-		return (IcarusApplication) getApplicationContext();
-	}
-
-	@Override
-	public void onCreate(Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-
-		// camera = Camera.open();
-		// camera.setPreviewCallback(this);
-
-		index = 0;
-		ll = new LinearLayout(this);
-		ll.setOrientation(LinearLayout.VERTICAL);
-
-		tv = new TextView(this);
-		tv.setText("No GPS fix received yet");
-		ll.addView(tv, index++);
-
-		setContentView(ll);
-
-		dataViews = new HashMap<String, TextView>();
-
-	}
-
-	@Override
-	public void onPause() {
-		super.onPause();
-		getIcarusApplication().getDataSource().removeDataListener(this);
-	}
-
-	@Override
-	public void onResume() {
-		super.onResume();
-		getIcarusApplication().getDataSource().addDataListener(this);
-		IcarusService.activateIfNecessary(this);
-	}
-
-	@Override
-	public boolean onCreateOptionsMenu(Menu menu) {
-		super.onCreateOptionsMenu(menu);
-		menu.add(0, 0, 0, "Settings");
-		return super.onCreateOptionsMenu(menu);
-	}
-
-	@Override
-	public boolean onOptionsItemSelected(MenuItem item) {
-		startActivity(new Intent(this, IcarusPreferenceActivity.class));
-		return (true);
-	}
-
-}

src/to/rcpt/icarus/IcarusMessageSender.java

+package to.rcpt.icarus;
+
+import java.util.Calendar;
+
+import android.content.Context;
+import android.telephony.SmsManager;
+
+class IcarusMessageSender implements DataSource.IcarusListener {
+	
+	public IcarusMessageSender(Context ctx) {
+		super();
+		
+		IcarusApplication app = (IcarusApplication) ctx.getApplicationContext();
+		IcarusPreferences prefs = app.getPreferences();
+		smsStart = prefs.getSmsStart();
+		smsPhone = prefs.getSmsPhone();
+		smsInterval = prefs.getSmsInterval() * 60000;
+		
+		smsmanager = SmsManager.getDefault();
+		lastSent = 0;
+		active = false;
+	}
+
+	public void logDataEvent(String name, String data, long ts,
+			boolean hasService) {
+		if (!active)
+			return;
+
+		// only send SMS for GPS coordinates
+		if (name != "gps")
+			return;
+		
+		// need to have cell service
+		if (!hasService)
+			return;
+		
+		// need to have a valid phone number
+		if (smsPhone.equals(""))
+			return;
+		
+		// need to be after the start date/time
+		if (smsStart != null && Calendar.getInstance().before(smsStart))
+			return;
+		
+		// need to be more than smsInterval milliseconds since the last SMS
+		if (ts - lastSent < smsInterval) 
+			return;
+		
+		try {
+			smsmanager.sendTextMessage(smsPhone, null, data, null, null);
+		} catch (Exception e) {
+			//
+		}
+		lastSent = ts;
+	}
+	
+	public void start() {
+		active = true;
+	}
+	
+	public void stop() {
+		active = false;
+	}
+	
+	private Calendar smsStart;
+	private String smsPhone;
+	private int smsInterval; // milliseconds
+	
+	private boolean active;
+	private SmsManager smsmanager;
+	private long lastSent;
+	
+}

src/to/rcpt/icarus/IcarusPreferenceActivity.java

-package to.rcpt.icarus;
-
-import android.os.Bundle;
-import android.preference.PreferenceActivity;
-
-public class IcarusPreferenceActivity extends PreferenceActivity {
-	@Override
-	protected void onCreate(Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-
-		addPreferencesFromResource(R.xml.icarusprefs);
-	}
-}

src/to/rcpt/icarus/IcarusPreferences.java

+package to.rcpt.icarus;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+
+class IcarusPreferences {
+
+	private static final String LOGGING_DELAY_ENABLED = "LOGGING_DELAY_ENABLED";
+	private static final String LOGGING_DELAY_INTERVAL = "LOGGING_DELAY_INTERVAL";
+	private static final String CAMERA_INTERVAL = "CAMERA_INTERVAL";
+	private static final String SMS_ENABLED = "SMS_ENABLED";
+	private static final String SMS_PHONE = "SMS_PHONE";
+	private static final String SMS_DELAY_ENABLED = "SMS_DELAY_ENABLED";
+	private static final String SMS_DELAY_INTERVAL = "SMS_DELAY_INTERVAL";
+	private static final String SMS_INTERVAL = "SMS_INTERVAL";
+	private static final String LOGGING_START = "LOGGING_START";
+	private static final String SMS_START = "SMS_START";
+	
+	public IcarusPreferences(Context ctx) {
+		prefs = ctx.getSharedPreferences(IcarusApplication.PREFERENCES, 0);
+		editor = null;
+		dateFormat = new SimpleDateFormat("MM dd HH:mm:ss z yyyy");
+	}
+	
+	public boolean getLoggingDelayEnabled() {
+		return prefs.getBoolean(LOGGING_DELAY_ENABLED, false);
+	}
+	
+	public int getLoggingDelayInterval() {
+		return prefs.getInt(LOGGING_DELAY_INTERVAL, 15);
+	}
+	
+	public int getCameraInterval() {
+		return prefs.getInt(CAMERA_INTERVAL, 5);
+	}
+	
+	public boolean getSmsEnabled() {
+		return prefs.getBoolean(SMS_ENABLED, false);
+	}
+	
+	public String getSmsPhone() {
+		return prefs.getString(SMS_PHONE, "");
+	}
+	
+	public boolean getSmsDelayEnabled() {
+		return prefs.getBoolean(SMS_DELAY_ENABLED, true);
+	}
+	
+	public int getSmsDelayInterval() {
+		return prefs.getInt(SMS_DELAY_INTERVAL, 60);
+	}
+	
+	public int getSmsInterval() {
+		return prefs.getInt(SMS_INTERVAL, 10);
+	}
+	
+	public Calendar getLoggingStart() {
+		String str = prefs.getString(LOGGING_START, "");
+		
+		if (str.equals(""))
+			return null;
+		
+		try {
+			Date date = dateFormat.parse(str);
+			Calendar cal = Calendar.getInstance();
+			cal.setTime(date);
+			return cal;
+		} catch (ParseException e) {
+			return null;
+		}
+	}
+	
+	public Calendar getSmsStart() {
+		String str = prefs.getString(SMS_START, "");
+		
+		if (str.equals(""))
+			return null;
+		
+		try {
+			Date date = dateFormat.parse(str);
+			Calendar cal = Calendar.getInstance();
+			cal.setTime(date);
+			return cal;
+		} catch (ParseException e) {
+			return null;
+		}
+	}
+	
+	public void startUpdates() {
+		editor = prefs.edit();
+	}
+	
+	public void commitUpdates() {
+		editor.commit();
+		editor = null;
+	}
+	
+	public void setLoggingDelayEnabled(boolean value) {
+		editor.putBoolean(LOGGING_DELAY_ENABLED, value);
+	}
+	
+	public void setLoggingDelayInterval(int value) {
+		editor.putInt(LOGGING_DELAY_INTERVAL, value);
+	}
+	
+	public void setCameraInterval(int value) {
+		editor.putInt(CAMERA_INTERVAL, value);
+	}
+	
+	public void setSmsEnabled(boolean value) {
+		editor.putBoolean(SMS_ENABLED, value);
+	}
+	
+	public void setSmsPhone(String value) {
+		editor.putString(SMS_PHONE, value);
+	}
+	
+	public void setSmsDelayEnabled(boolean value) {
+		editor.putBoolean(SMS_DELAY_ENABLED, value);
+	}
+	
+	public void setSmsDelayInterval(int value) {
+		editor.putInt(SMS_DELAY_INTERVAL, value);
+	}
+	
+	public void setSmsInterval(int value) {
+		editor.putInt(SMS_INTERVAL, value);
+	}
+	
+	public void setLoggingStart(Calendar cal) {
+		if (cal == null) {
+			editor.putString(LOGGING_START, "");
+		} else {
+			String str = dateFormat.format(cal.getTime());
+			editor.putString(LOGGING_START, str);
+		}
+	}
+	
+	public void setSmsStart(Calendar cal) {
+		if (cal == null) {
+			editor.putString(SMS_START, "");
+		} else {
+		String str = dateFormat.format(cal.getTime());
+		editor.putString(SMS_START, str);
+		}
+	}
+
+	private SharedPreferences prefs;
+	private SharedPreferences.Editor editor;
+	private SimpleDateFormat dateFormat;
+	
+}

src/to/rcpt/icarus/IcarusService.java

 package to.rcpt.icarus;
 
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Timer;
+import java.util.TimerTask;
+
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.media.MediaPlayer;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.PowerManager;
 
-public class IcarusService extends Service implements
-		OnSharedPreferenceChangeListener {
+public class IcarusService extends Service {
 
-	public static void activateIfNecessary(Context ctx) {
-		if (isEnabled(ctx)) {
-			Intent startIntent = new Intent(ctx, IcarusService.class);
-			ctx.startService(startIntent);
-		}
+	public static void startService(Context ctx) {
+		Intent startIntent = new Intent(ctx, IcarusService.class);
+		ctx.startService(startIntent);
 	}
-
-	boolean active;
+	
+	public static void stopService(Context ctx) {
+		Intent stopIntent = new Intent(ctx, IcarusService.class);
+		ctx.stopService(stopIntent);
+	}
 
 	public IcarusService() {
 		super();
 		active = false;
 	}
 
-	IcarusApplication getIcarusApplication() {
+	@Override
+	public void onCreate() {
+		super.onCreate();
+		
+		handler = new Handler();
+		
+		IcarusApplication app = getIcarusApplication();
+		IcarusPreferences prefs = app.getPreferences();
+		ds = app.getDataSource();
+
+		logger = new IcarusLogger();
+		ds.addDataListener(logger);
+		
+		if (prefs.getSmsEnabled()) {
+			ms = new IcarusMessageSender(this);
+			ds.addDataListener(ms);
+		} else {
+			ms = null;
+		}
+
+		pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
+		cam =  new IcarusCamera(this);
+		wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "IcarusMission");
+		
+		wl.acquire();
+
+		Calendar loggingStart = prefs.getLoggingStart();
+		if (loggingStart != null && Calendar.getInstance().before(loggingStart))
+			startDelayed(loggingStart.getTime());
+		else
+			start();
+
+		Notification n = updateNotification(this, true);
+		startForeground(IcarusApplication.NOTIFY_ICARUS_ACTIVE, n);
+	}
+
+	@Override
+	public void onDestroy() {
+		stop();
+		cam.release();
+		updateNotification(this, false);
+		wl.release();
+		super.onDestroy();
+	}
+
+	@Override
+	public IBinder onBind(Intent intent) {
+		return null;
+	}
+
+	private boolean active;
+	private IcarusLogger logger;
+	private DataSource ds;
+	private IcarusCamera cam;
+	private IcarusMessageSender ms;
+	private PowerManager pm;
+	private PowerManager.WakeLock wl;
+	private Handler handler;
+	private Timer timer;
+	
+	private TimerTask delayedStartTask = new TimerTask() {
+		
+		@Override
+		public void run() {
+			handler.post(new Runnable() {
+				
+				@Override
+				public void run() {
+					start();
+					timer.cancel();
+					timer = null;
+				}
+			});
+		}
+	};
+	
+	private void startDelayed(Date date) {
+		timer = new Timer(true);		
+		timer.schedule(delayedStartTask, date);
+	}
+
+	private void start() {
+		if (active)
+			return;
+		
+		logger.start();
+		if (ms != null)
+			ms.start();
+		ds.start();
+		cam.start();
+		
+		active = true;
+	}
+
+	private void stop() {
+		if (timer != null) {
+			timer.cancel();
+			timer = null;
+		}
+		
+		if (!active)
+			return;
+		
+		cam.stop();
+		ds.stop();
+		if (ms != null)
+			ms.stop();
+		logger.stop();
+		
+		active = false;
+	}
+
+	private IcarusApplication getIcarusApplication() {
 		return (IcarusApplication) getApplicationContext();
 	}
 
-	static boolean isEnabled(Context ctx) {
-		return ((IcarusApplication) ctx.getApplicationContext())
-				.getIcarusPreferences().getBoolean("enableLogging", false);
-	}
-
-	static Notification updateNotification(Context ctx, boolean enabled) {
+	private static Notification updateNotification(Context ctx, boolean enabled) {
 		NotificationManager notificationManager = (NotificationManager) ctx
 				.getSystemService(Context.NOTIFICATION_SERVICE);
 
 					"Icarus Active", System.currentTimeMillis());
 
 			n.flags |= Notification.FLAG_ONGOING_EVENT;
-			Intent ni = new Intent(ctx, IcarusManager.class);
+			Intent ni = new Intent(ctx, MainActivity.class);
 
 			PendingIntent pi = PendingIntent.getActivity(ctx, 0, ni, 0);
 			n.setLatestEventInfo(ctx, "Icarus", "Icarus Active", pi);
 			return null;
 		}
 	}
-
-	public void start(Context ctx) {
-		if (active)
-			return;
-		ds.start();
-		logger.startLogging();
-		wl.acquire();
-		active = true;
-
-		MediaPlayer mp = MediaPlayer.create(ctx, R.raw.icarusactivated);
-		mp.start();
-	}
-
-	public void stop() {
-		if (!active)
-			return;
-		ds.stop();
-		logger.stopLogging();
-		wl.release();
-		active = false;
-	}
-
-	@Override
-	public IBinder onBind(Intent intent) {
-		return null;
-	}
-
-	private IcarusLogger logger;
-	private PowerManager pm;
-	private PowerManager.WakeLock wl;
-	private DataSource ds;
-
-	public void logDataEvent(String name, String data, long ts,
-			boolean hasService) {
-		logger.logDataEvent(name, data, ts, hasService);
-	}
-
-	@Override
-	public void onCreate() {
-		super.onCreate();
-
-		logger = new IcarusLogger(getIcarusApplication());
-
-		getIcarusApplication().getDataSource().addDataListener(logger);
-
-		pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
-		wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "IcarusMission");
-		ds = getIcarusApplication().getDataSource();
-
-		start(this);
-		getIcarusApplication().getIcarusPreferences()
-				.registerOnSharedPreferenceChangeListener(this);
-		// The API for making a service run in the foreground changed between
-		// API versions 4 and 5. For it to be effective, we must use the call
-		// corresponding to the API version of the device that we're running on.
-
-		// This code is for API versions 5 and later to make the service
-		// ineligible for killing:
-		// Notification n = updateNotification(this, true);
-		// startForeground(NOTIFY_ICARUS_ACTIVE, n);
-
-		// This code is for API versions 4 and earlier to make the service
-		// ineligible for killing:
-		updateNotification(this, true);
-		setForeground(true);
-	}
-
-	@Override
-	public void onDestroy() {
-		super.onDestroy();
-		stop();
-		getIcarusApplication().getIcarusPreferences()
-				.unregisterOnSharedPreferenceChangeListener(this);
-		updateNotification(this, false);
-	}
-
-	public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
-			String key) {
-		if (!isEnabled(this))
-			stopSelf();
-
-	}
 }

src/to/rcpt/icarus/MainActivity.java

+package to.rcpt.icarus;
+
+import java.util.Calendar;
+import java.util.HashMap;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+public class MainActivity extends Activity implements
+		DataSource.IcarusListener {
+
+	public void logDataEvent(String name, String data, long ts, boolean hasService) {
+		if (ts > 0)
+			statusTextView.setText(String.format("GPS timestamp: %d; Cell service: %s",
+												 ts, (hasService ? "yes" : "no")));
+		
+		TextView sensorView;
+		if (!dataViews.containsKey(name)) {
+			sensorView = new TextView(this);
+			sensorsList.addView(sensorView);
+			dataViews.put(name, sensorView);
+		} else
+			sensorView = dataViews.get(name);
+		
+		sensorView.setText(String.format("%s: %s", name, data));
+	}
+
+	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		
+		setContentView(R.layout.icarus_layout);
+
+		startButton = (ToggleButton) findViewById(R.id.startButton);
+		statusTextView = (TextView) findViewById(R.id.statusTextView);
+		sensorsList = (LinearLayout) findViewById(R.id.sensorsList);
+		
+		startButton.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+			
+			@Override
+			public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+				if (isChecked)
+					startIcarus();
+				else
+					stopIcarus();
+			}
+		});
+
+		dataViews = new HashMap<String, TextView>();
+	}
+
+	@Override
+	public void onResume() {
+		super.onResume();
+		getIcarusApplication().getDataSource().addDataListener(this);
+	}
+
+	@Override
+	public void onPause() {
+		getIcarusApplication().getDataSource().removeDataListener(this);
+		super.onPause();
+	}
+	
+	@Override
+	public void onDestroy() {
+		IcarusService.stopService(this);
+		super.onDestroy();
+		System.runFinalizersOnExit(true);
+		System.exit(0);
+	}
+	
+	@Override
+	public boolean onPrepareOptionsMenu(Menu menu) {
+		if (startButton.isChecked())
+			return false;
+		
+		menu.clear();
+		menu.add(R.string.menu_preferences_caption);
+		
+		return true;
+	}
+	
+	@Override
+	public boolean onOptionsItemSelected(MenuItem item) {
+		Intent startIntent = new Intent(this, PreferencesActivity.class);
+		startActivity(startIntent);
+		return true;
+	}
+
+	private ToggleButton startButton;
+	private TextView statusTextView;
+	private LinearLayout sensorsList;
+	private HashMap<String, TextView> dataViews;
+
+	private IcarusApplication getIcarusApplication() {
+		return (IcarusApplication)getApplicationContext();
+	}
+
+	private void stopIcarus() {
+		getIcarusApplication().getDataSource().removeDataListener(this);
+		IcarusService.stopService(this);
+		statusTextView.setText(R.string.status_inactive);
+		sensorsList.removeAllViews();
+		dataViews.clear();
+	}
+	
+	private void startIcarus() {
+		initializeStartDates();
+		getIcarusApplication().getDataSource().addDataListener(this);
+		IcarusService.startService(this);
+	}
+	
+	private void initializeStartDates() {
+		IcarusPreferences prefs = getIcarusApplication().getPreferences();
+		Calendar loggingStart = null;
+		Calendar smsStart = null;
+		
+		if (prefs.getLoggingDelayEnabled()) {
+			int loggingDelay = prefs.getLoggingDelayInterval();
+			loggingStart = Calendar.getInstance();
+			loggingStart.add(Calendar.MINUTE, loggingDelay);
+		}
+		
+		if (prefs.getSmsDelayEnabled()) {
+			int smsDelay = prefs.getSmsDelayInterval();
+			// SMS delay starts running after logging has started
+			if (loggingStart == null)
+				smsStart = Calendar.getInstance();
+			else
+				smsStart = (Calendar) loggingStart.clone();
+			smsStart.add(Calendar.MINUTE, smsDelay);
+		}
+		
+		prefs.startUpdates();
+		prefs.setLoggingStart(loggingStart);
+		prefs.setSmsStart(smsStart);
+		prefs.commitUpdates();
+		
+		if (loggingStart == null) {
+			statusTextView.setText(R.string.status_no_gps);
+		} else {
+			String statusFormat = getString(R.string.status_delayed_start);
+			statusTextView.setText(String.format(statusFormat, loggingStart));
+		}
+	}
+}

src/to/rcpt/icarus/PreferencesActivity.java

+package to.rcpt.icarus;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.CheckBox;
+import android.widget.EditText;
+
+public class PreferencesActivity extends Activity {
+	
+	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+		
+		setContentView(R.layout.preferences_layout);
+
+		logDelayCheckBox = (CheckBox) findViewById(R.id.logDelayCheckBox);
+		logDelayEditText = (EditText) findViewById(R.id.logDelayEditText);
+		cameraIntervalEditText = (EditText) findViewById(R.id.cameraIntervalEditText);
+		smsEnableCheckBox = (CheckBox) findViewById(R.id.smsEnableCheckBox);
+		smsPhoneEditText = (EditText) findViewById(R.id.smsPhoneEditText);
+		smsDelayCheckBox = (CheckBox) findViewById(R.id.smsDelayCheckBox);
+		smsDelayEditText = (EditText) findViewById(R.id.smsDelayEditText);
+		smsIntervalEditText = (EditText) findViewById(R.id.smsIntervalEditText);
+		
+		loadPreferences();
+	}
+	
+	@Override
+	public void onDestroy() {
+		savePreferences();
+		super.onDestroy();
+	}
+	
+	private CheckBox logDelayCheckBox;
+	private EditText logDelayEditText;
+	private EditText cameraIntervalEditText;
+	private CheckBox smsEnableCheckBox;
+	private EditText smsPhoneEditText;
+	private CheckBox smsDelayCheckBox;
+	private EditText smsDelayEditText;
+	private EditText smsIntervalEditText;
+
+	private IcarusApplication getIcarusApplication() {
+		return (IcarusApplication)getApplicationContext();
+	}
+	
+	private void loadPreferences() {
+		IcarusPreferences prefs = getIcarusApplication().getPreferences();
+		
+		logDelayCheckBox.setChecked(prefs.getLoggingDelayEnabled());
+		logDelayEditText.setText(Integer.toString(prefs.getLoggingDelayInterval()));
+		cameraIntervalEditText.setText(Integer.toString(prefs.getCameraInterval()));
+		smsEnableCheckBox.setChecked(prefs.getSmsEnabled());
+		smsPhoneEditText.setText(prefs.getSmsPhone());
+		smsDelayCheckBox.setChecked(prefs.getSmsDelayEnabled());
+		smsDelayEditText.setText(Integer.toString(prefs.getSmsDelayInterval()));
+		smsIntervalEditText.setText(Integer.toString(prefs.getSmsInterval()));
+	}
+	
+	private void savePreferences() {
+		IcarusPreferences prefs = getIcarusApplication().getPreferences();
+		
+		prefs.startUpdates();
+		prefs.setLoggingDelayEnabled(logDelayCheckBox.isChecked());
+		prefs.setLoggingDelayInterval(Integer.parseInt(logDelayEditText.getText().toString()));
+		prefs.setCameraInterval(Integer.parseInt(cameraIntervalEditText.getText().toString()));
+		prefs.setSmsEnabled(smsEnableCheckBox.isChecked());
+		prefs.setSmsPhone(smsPhoneEditText.getText().toString());
+		prefs.setSmsDelayEnabled(smsDelayCheckBox.isChecked());
+		prefs.setSmsDelayInterval(Integer.parseInt(smsDelayEditText.getText().toString()));
+		prefs.setSmsInterval(Integer.parseInt(smsIntervalEditText.getText().toString()));
+		prefs.commitUpdates();		
+	}
+
+}
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.