shkoo avatar shkoo committed 450a197

Moved icarus functionality into a service, made it autostart on boot if necessary
based on configured settings

Comments (0)

Files changed (12)

AndroidManifest.xml

       package="to.rcpt.icarus"
       android:versionCode="1"
       android:versionName="1.0">
-    <application android:label="@string/app_name" android:debuggable="true">
-        <activity android:name=".IcarusMission"
+    <application android:label="@string/app_name" android:debuggable="true" android:name=".IcarusApplication">
+        <activity android:name=".IcarusManager"
                   android:label="@string/app_name">
             <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">
+            
+            <intent-filter>
+                <action android:name="android.intent.action.BOOT_COMPLETED"/>
+            </intent-filter>
+        </receiver>
+            <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>
Add a comment to this file

res/drawable/icaruslogo.png

Added
New image
Add a comment to this file

res/raw/icarusactivated.wav

Binary file added.

res/xml/icarusprefs.xml

+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen
+  xmlns:android="http://schemas.android.com/apk/res/android">
+<CheckBoxPreference android:key="enableLogging" android:title="Enable Logging" android:summaryOn="Logging is enabled" android:summaryOff="Logging is disabled"></CheckBoxPreference>
+<EditTextPreference android:summary="Destination phone number of SMS status messages" android:title="Report to" android:key="targetPhoneNumber" android:defaultValue="2096270247"></EditTextPreference>
+</PreferenceScreen>

src/to/rcpt/icarus/DataSource.java

 import android.telephony.TelephonyManager;
 
 public 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 List<SensorEventAdapter> adapterlist;
 
 	private LocationManager gps;
+	private TelephonyManager telephonyManager;
+	private String locationProvider;
 
-	private String provider;
+	public void addDataListener(IcarusListener newListener) {
+		listeners.add(newListener);
+	}
 
-	private IcarusMission mission;
+	public void removeDataListener(IcarusListener oldListener) {
+		listeners.remove(oldListener);
+	}
 
-	public DataSource(IcarusMission m) {
-		mission = m;
+	public DataSource(Context ctx) {
+		listeners = new LinkedList<IcarusListener>();
 
 		// set up service state listener
 		gsmState = -1;
-		TelephonyManager tm = (TelephonyManager) m
+		telephonyManager = (TelephonyManager) ctx
 				.getSystemService(Context.TELEPHONY_SERVICE);
-		tm.listen(new StateListener(this),
+		telephonyManager.listen(new StateListener(this),
 				PhoneStateListener.LISTEN_SERVICE_STATE);
 
 		// set up sensor listener
-		sensors = (SensorManager) m.getSystemService(Context.SENSOR_SERVICE);
+		sensors = (SensorManager) ctx.getSystemService(Context.SENSOR_SERVICE);
 		adapterlist = new LinkedList<SensorEventAdapter>();
 		for (Sensor s : sensors.getSensorList(Sensor.TYPE_ALL))
 			adapterlist.add(new SensorEventAdapter(s, this));
 
 		// set up gps
-		gps = (LocationManager) m.getSystemService(Context.LOCATION_SERVICE);
+		gps = (LocationManager) ctx.getSystemService(Context.LOCATION_SERVICE);
 		Criteria c = new Criteria();
 		c.setAccuracy(Criteria.ACCURACY_FINE);
 		c.setAltitudeRequired(true);
 		c.setSpeedRequired(true);
-		provider = gps.getBestProvider(c, true);
+		locationProvider = gps.getBestProvider(c, true);
+	}
+
+	public void start() {
+		gps.requestLocationUpdates(locationProvider, 0, 0, this);
+		for (SensorEventAdapter s : adapterlist)
+			s.startSensor();
 	}
 
 	public void logDataEvent(String name, String data, long ts) {
-		mission.logDataEvent(name, data, ts,
-				this.gsmState == ServiceState.STATE_IN_SERVICE);
+		for (IcarusListener listener : listeners)
+			listener.logDataEvent(name, data, ts,
+					this.gsmState == ServiceState.STATE_IN_SERVICE);
 	}
 
 	public void onLocationChanged(Location l) {
-		this.logDataEvent("gps", l.getLatitude() + "," + l.getLongitude() + " "
+		logDataEvent("gps", l.getLatitude() + "," + l.getLongitude() + " "
 				+ l.getAltitude(), l.getTime());
 	}
 
 	public void onStatusChanged(String providezr, int status, Bundle extras) {
 	}
 
-	public void pause() {
+	public void stop() {
 		gps.removeUpdates(this);
 		for (SensorEventAdapter s : adapterlist)
 			s.stopSensor();
 	}
-
-	public void resume() {
-		gps.requestLocationUpdates(provider, 0, 0, this);
-		for (SensorEventAdapter s : adapterlist)
-			s.startSensor();
-	}
 }

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 {
+	private DataSource ds;
+
+	SharedPreferences prefs;
+
+	static final int NOTIFY_ICARUS_ACTIVE = 0;
+
+	public DataSource getDataSource() {
+		if (ds == null)
+			ds = new DataSource(this);
+		return ds;
+	}
+
+	public SharedPreferences getIcarusPreferences() {
+		if (prefs == null)
+			prefs = PreferenceManager.getDefaultSharedPreferences(this);
+		return prefs;
+	}
+}

src/to/rcpt/icarus/IcarusAutoStarter.java

+package to.rcpt.icarus;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class IcarusAutoStarter extends BroadcastReceiver {
+
+	@Override
+	public void onReceive(final Context ctx, Intent intent) {
+		IcarusService.activateIfNecessary(ctx);
+	}
+};

src/to/rcpt/icarus/IcarusLogger.java

 import android.telephony.SmsManager;
 import android.widget.CompoundButton;
 
-public class IcarusLogger implements CompoundButton.OnCheckedChangeListener {
+public class IcarusLogger implements DataSource.IcarusListener {
 
-	public final String destPhoneNumber = "2096270247";
+	public final String defaultDestPhoneNumber = "2096270247";
+	String destPhoneNumber;
 	final String logfileName = "IcarusLog";
-	final long sendInterval = 60000;
+	final long sendInterval = 600000;
 
 	private PrintWriter file;
 	private SmsManager smsmanager;
 
 	private boolean logging;
 
-	public IcarusLogger() {
+	public IcarusLogger(IcarusApplication app) {
 		smsmanager = SmsManager.getDefault();
 		lastSent = 0;
 		logging = false;
+
+		destPhoneNumber = app.getIcarusPreferences().getString(
+				"targetPhoneNumber", defaultDestPhoneNumber);
 	}
 
 	public void logDataEvent(String name, String data, long ts,

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/IcarusMission.java

-package to.rcpt.icarus;
-
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.HashMap;
-
-import android.app.Activity;
-import android.content.Context;
-import android.hardware.Camera;
-import android.os.Bundle;
-import android.os.PowerManager;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-import android.widget.ToggleButton;
-
-public class IcarusMission extends Activity implements Camera.PictureCallback,
-		Camera.PreviewCallback {
-
-	private class ExitActivityListener implements OnClickListener {
-		private IcarusMission act;
-
-		public ExitActivityListener(IcarusMission a) {
-			act = a;
-		}
-
-		public void onClick(View v) {
-			act.finish();
-		}
-	}
-
-	private TextView tv;
-	private LinearLayout ll;
-	private int index;
-	private HashMap<String, TextView> dataViews;
-
-	private DataSource ds;
-	private IcarusLogger logger;
-	private PowerManager pm;
-	private PowerManager.WakeLock wl;
-
-	// private Camera camera;
-	private int previewFrames;
-	final int maxPreviewFrames = 20;
-
-	public void logDataEvent(String name, String data, long ts,
-			boolean hasService) {
-		logger.logDataEvent(name, data, ts, hasService);
-		if (ts > 0)
-			tv.setText("GPS Timestamp: " + ts);
-		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);
-	}
-
-	@Override
-	public void onCreate(Bundle savedInstanceState) {
-		super.onCreate(savedInstanceState);
-
-		logger = new IcarusLogger();
-		ds = new DataSource(this);
-
-		pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
-		wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "IcarusMission");
-
-		// camera = Camera.open();
-		// camera.setPreviewCallback(this);
-
-		index = 0;
-		ll = new LinearLayout(this);
-		ll.setOrientation(LinearLayout.VERTICAL);
-
-		ToggleButton b = new ToggleButton(this);
-		b.setTextOff("Start mission");
-		b.setTextOn("Stop mission");
-		b.setOnCheckedChangeListener(logger);
-		ll.addView(b, index++);
-
-		TextView destNum = new TextView(this);
-		destNum.setText("Destination #: " + logger.destPhoneNumber);
-		ll.addView(destNum, index++);
-
-		tv = new TextView(this);
-		tv.setText("Hello, Icarus");
-		ll.addView(tv, index++);
-
-		Button exitButton = new Button(this);
-		exitButton.setOnClickListener(new ExitActivityListener(this));
-		exitButton.setText("Exit");
-		ll.addView(exitButton, index++);
-
-		setContentView(ll);
-
-		dataViews = new HashMap<String, TextView>();
-	}
-
-	@Override
-	public void onDestroy() {
-		super.onDestroy();
-		ds.pause();
-	}
-
-	public void onPictureTaken(byte[] pictureData, Camera c) {
-		tv.setText("Picture Callback");
-		c.stopPreview();
-		this.writeImage(pictureData);
-	}
-
-	public void onPreviewFrame(byte[] pictureData, Camera c) {
-		tv.setText("Preview Callback");
-		if (previewFrames > maxPreviewFrames)
-			c.takePicture(null, null, this);
-		else
-			previewFrames += 1;
-	}
-
-	@Override
-	public void onResume() {
-		super.onResume();
-		wl.acquire();
-		ds.resume();
-		// camera.startPreview();
-		// previewFrames = 0;
-	}
-
-	private void writeImage(byte[] pictureData) {
-		String fn = "/sdcard/autopic-" + System.currentTimeMillis() + ".jpg";
-		try {
-			FileOutputStream file = new FileOutputStream(fn);
-			file.write(pictureData);
-			file.close();
-		} catch (IOException e) {
-			throw new RuntimeException(e);
-		}
-	}
-
-}

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/IcarusService.java

+package to.rcpt.icarus;
+
+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.IBinder;
+import android.os.PowerManager;
+
+public class IcarusService extends Service implements
+		OnSharedPreferenceChangeListener {
+
+	public static void activateIfNecessary(Context ctx) {
+		if (isEnabled(ctx)) {
+			Intent startIntent = new Intent(ctx, IcarusService.class);
+			ctx.startService(startIntent);
+		}
+	}
+
+	boolean active;
+
+	public IcarusService() {
+		super();
+		active = false;
+	}
+
+	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) {
+		NotificationManager notificationManager = (NotificationManager) ctx
+				.getSystemService(Context.NOTIFICATION_SERVICE);
+
+		if (enabled) {
+			Notification n = new Notification(R.drawable.icaruslogo,
+					"Icarus Active", System.currentTimeMillis());
+
+			n.flags |= Notification.FLAG_ONGOING_EVENT;
+			Intent ni = new Intent(ctx, IcarusManager.class);
+
+			PendingIntent pi = PendingIntent.getActivity(ctx, 0, ni, 0);
+			n.setLatestEventInfo(ctx, "Icarus", "Icarus Active", pi);
+			notificationManager.notify(IcarusApplication.NOTIFY_ICARUS_ACTIVE,
+					n);
+
+			return n;
+		} else {
+			notificationManager.cancel(IcarusApplication.NOTIFY_ICARUS_ACTIVE);
+			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();
+
+	}
+}
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.