Commit e4572697 authored by Kristin Muterspaw's avatar Kristin Muterspaw
Browse files

SensorSampleActivity nows uses SensorSampleService to do all communication...

SensorSampleActivity nows uses SensorSampleService to do all communication with the sensors and writing to the database. If it's a sample then the Service is started, contacts the sensor, writes to the database once, and then the service is told to stop sampling. If it's streaming, then a handler is started to continuously contact and write to the database. The Service now uses a 'Wake Lock' which means the CPU stays on even if the screen is off. Can stream with screen off now.
parent e6375048
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="fieldscience.cs.earlham.edu.fieldday" >
package="fieldscience.cs.earlham.edu.fieldday">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-feature android:name="android.hardware.bluetooth_le"/>
<uses-feature android:name="android.hardware.camera2" android:required="false" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-feature android:name="android.hardware.bluetooth_le" />
<uses-feature
android:name="android.hardware.camera2"
android:required="false" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/MainTheme" >
android:theme="@style/MainTheme">
<activity
android:name=".MainScreenActivity"
android:label="@string/app_name"
......@@ -55,9 +59,12 @@
android:label="My Documents Viewer"
android:theme="@style/MainTheme">
</activity>
<service
android:name=".BluetoothLeService"
android:enabled="true" />
<service
android:name=".SensorSampleService"
android:exported="false" />
</application>
</manifest>
......@@ -10,8 +10,6 @@ import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class BluetoothLeService extends Service {
......@@ -46,15 +44,15 @@ public class BluetoothLeService extends Service {
public static String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";
private BluetoothGattService gattService;
private Map<UUID, String> services = new HashMap<UUID, String>();
private final BluetoothSensor.Listener listener = new BluetoothSensor.Listener() {
@Override
public void onCharacteristicRead(UUID characteristicUUID, String data) {
broadcastUpdate(ACTION_DATA_AVAILABLE, data);
}
@Override
public void onServiceInformation(){
public void onServiceInformation() {
for (BluetoothGattService s : btSensor.getServices()){
if (s.getUuid().equals(UUID_BLE_SENSOR_SERVICE)) {
gattService = btSensor.getService(UUID_BLE_SENSOR_SERVICE);
......@@ -88,6 +86,11 @@ public class BluetoothLeService extends Service {
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
......
......@@ -78,8 +78,11 @@ public class BluetoothSensor implements Parcelable {
public boolean connect(Context context){
gattClient.connect(context, device);
// This listener is used when changes happen in the GattClient class. The GattClient class
// is used for communication with the actual bluetooth device.
// This listener is used when changes happen in the GattClient class. It is implemented in
// the GattClient class and anytime these functions are called in that class, the listener in
// this class are called as well. The GattClient class is used for communication with the
// actual bluetooth device. This is used for talking to the BluetoothService and
// BluetoothSensorFragment.
GattClient.Listener listener = new GattClient.Listener() {
@Override
public void onMessageReceived(byte[] data, BluetoothGattCharacteristic characteristic) {
......@@ -112,18 +115,18 @@ public class BluetoothSensor implements Parcelable {
this.mListener = listener;
}
public static interface Listener {
public interface Listener {
void onCharacteristicRead(UUID characteristicUUID, String data);
void onServiceInformation();
}
// Required by android Parcelable class
// Required by Android Parcelable class
@Override
public int describeContents() {
return 0;
}
// Required by android Parcelable class
// Required by Android Parcelable class
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeParcelable(device, 0);
......
......@@ -35,6 +35,8 @@ public class BluetoothSensorFragment extends ListFragment {
private ArrayList<aSensor> ourSensors;
SensorFragmentCommunication callback;
private static final String TAG = BluetoothSensorFragment.class.getSimpleName();
private ServiceConnection btServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
......@@ -44,7 +46,7 @@ public class BluetoothSensorFragment extends ListFragment {
getActivity().finish();
}
boolean connection = btService.connect(btSensor);
if (!connection){
if (!connection) {
Log.e("Sensor Sample Activity", "Unable to connect to device");
}
}
......@@ -63,16 +65,22 @@ public class BluetoothSensorFragment extends ListFragment {
} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
btService = null;
btHandler.removeCallbacks(btRunner);
btHandler.removeCallbacksAndMessages(btRunner);
} else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
btService.readMessage();
btHandler.postDelayed(btRunner, 5000);
if (!registered) {
Log.d(TAG, "Services Discovered.");
btHandler.postDelayed(btRunner, 5000);
}
} else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
if (btService != null) {
if (BluetoothLeService.current_request.equals(BluetoothLeService.LIST_OF_SENSORS)) {
Log.d(TAG, "Receiving List of Sensors");
parseResponse(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
btHandler.removeCallbacksAndMessages(btRunner);
} else if (BluetoothLeService.current_request.equals(BluetoothLeService.SENSOR_VALUES)){
parseResponse(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
Log.d(TAG, "Receiving updated Sensor Values");
}
}
}
......@@ -108,7 +116,6 @@ public class BluetoothSensorFragment extends ListFragment {
@Override
public void run() {
getSensorValues();
btHandler.postDelayed(this, senseInterval);
}
};
......@@ -156,7 +163,7 @@ public class BluetoothSensorFragment extends ListFragment {
@Override
public void onDetach(){
super.onDetach();
btHandler.removeCallbacks(btRunner);
btHandler.removeCallbacksAndMessages(btRunner);
if (btService != null) {
btService.disconnect();
btService.close();
......@@ -174,8 +181,9 @@ public class BluetoothSensorFragment extends ListFragment {
for (int i = 1; i < tmp.length + 1; i++) {
tx[i] = tmp[i - 1];
}
Log.d(TAG, "Writing message for sensor names.");
btService.writeMessage(tx, BluetoothLeService.LIST_OF_SENSORS);
btHandler.removeCallbacks(btRunner);
btHandler.removeCallbacksAndMessages(btRunner);
}
public void getSensorValues() {
......@@ -187,11 +195,13 @@ public class BluetoothSensorFragment extends ListFragment {
for (int i = 1; i < tmp.length + 1; i++) {
tx[i] = tmp[i - 1];
}
Log.d(TAG, "Updating Sensor Values");
btService.writeMessage(tx, BluetoothLeService.SENSOR_VALUES);
}
public void displayData() {
String sensor = "";
Log.d(TAG, "Response: " + response);
for (int i = 0; i < response.length(); i++) {
char c = response.charAt(i);
if (c == ';') {
......
package fieldscience.cs.earlham.edu.fieldday;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
......@@ -11,7 +17,7 @@ import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.TextWatcher;
......@@ -22,18 +28,16 @@ import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
public class SensorSampleActivity extends Activity implements SensorFragmentCommunication {
public class SensorSampleActivity extends Activity {
public LocationManager locationManager;
public LocationListener locationListener;
......@@ -44,13 +48,11 @@ public class SensorSampleActivity extends Activity implements SensorFragmentComm
public static Resources res;
public static Spinner siteSpinner, sectorSpinner;
public static EditText spotET;
public static String tripName, site, sector, spot, lastFragment;
public static String tripName, site, sector, spot, sensor;
public ReadingsDatabase db;
public RadioGroup logIntervalButton;
public long logInterval = 5000;
public ArrayList<aSensor> listSensors;
private Handler dbHandler;
private Runnable dbRunner;
public Boolean streaming = false, remote_db;
public Drawable streamOn, streamOff;
public ImageButton exportButton, streamButton, sampleButton;
......@@ -58,6 +60,46 @@ public class SensorSampleActivity extends Activity implements SensorFragmentComm
public static final String WHICH_SENSOR = "Which Sensor";
public static final String REMOTE_DB = "Remote DB Connected";
BluetoothSensor bluetoothSensor;
private SensorSampleService sampleService;
private Context context;
private ArrayAdapter<aSensor> adapter;
private ServiceConnection sampleServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
sampleService = ((SensorSampleService.LocalBinder) service).getService();
sampleService.connectSensor(sensor, bluetoothSensor);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
private final BroadcastReceiver sampleServiceReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (SensorSampleService.ACTION_CONNECTED.equals(intent.getAction())){
Log.d("SensorSampleActivity", "Received Connected Message!");
enableButtons();
ArrayList<aSensor> sensors = intent.getParcelableArrayListExtra(SensorSampleService.SENSOR_LIST);
for (aSensor s: sensors) {
Log.d("SensorSampleActivity", "Sensor: " + s.getName());
Log.d("SensorSampleActivity", "Sensor Value: " + s.getLastValueString());
}
adapter.clear();
adapter.addAll(sensors);
adapter.notifyDataSetChanged();
} else if (SensorSampleService.ACTION_UPDATED_LIST.equals(intent.getAction())) {
Log.d("SensorSampleActivity", "Received Updated List Message!");
ArrayList<aSensor> sensors = intent.getParcelableArrayListExtra(SensorSampleService.SENSOR_LIST);
adapter.clear();
adapter.addAll(sensors);
adapter.notifyDataSetChanged();
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
......@@ -65,8 +107,14 @@ public class SensorSampleActivity extends Activity implements SensorFragmentComm
setContentView(R.layout.activity_sensorsample);
remote_db = getIntent().getBooleanExtra(REMOTE_DB, false);
initializeVariables();
context = this;
listSensors = new ArrayList<aSensor>();
String sensor = getIntent().getStringExtra(WHICH_SENSOR);
Intent sensorServiceIntent = new Intent(this, SensorSampleService.class);
bindService(sensorServiceIntent, sampleServiceConnection, Context.BIND_AUTO_CREATE);
registerReceiver(sampleServiceReceiver, makeSensorUpdateFilter());
sensor = getIntent().getStringExtra(WHICH_SENSOR);
if (sensor.equals("bluetooth")){
bluetoothSensor = getIntent().getParcelableExtra(DEVICE);
streamButton.setImageDrawable(res.getDrawable(R.drawable.stream_unavailable, null));
......@@ -74,17 +122,11 @@ public class SensorSampleActivity extends Activity implements SensorFragmentComm
sampleButton.setClickable(false);
streamButton.setClickable(false);
}
lastFragment = sensor;
displaySensor(sensor);
dbHandler = new Handler();
dbRunner = new Runnable() {
@Override
public void run() {
writeToDB();
dbHandler.postDelayed(this, logInterval);
}
};
ListView mSensors = (ListView) findViewById(android.R.id.list);
adapter = new SensorListAdapter(this, listSensors);
mSensors.setAdapter(adapter);
Intent i = new Intent(context, SensorSampleService.class);
logIntervalButton.setOnClickListener(new View.OnClickListener() {
@Override
......@@ -99,22 +141,20 @@ public class SensorSampleActivity extends Activity implements SensorFragmentComm
if (!streaming) {
streamButton.setImageDrawable(streamOn);
streaming = true;
writeToDB();
if (lastFragment.equals("bluetooth")){
BluetoothSensorFragment bt = (BluetoothSensorFragment)
getFragmentManager().findFragmentByTag("bluetooth");
bt.sensingControl(true, logInterval);
}
dbHandler.postDelayed(dbRunner, logInterval);
// Start the SensorSampleService supplying the type of sensor connected --
// bluetooth or built-in and the logging interval to use.
Intent i = new Intent(context, SensorSampleService.class);
i.putExtra(SensorSampleService.LOG_INTERVAL, logInterval);
i.putExtra(SensorSampleService.SAMPLE_ONCE, false);
i.putExtra(SensorSampleService.TRIP_NAME, tripName);
startService(i);
} else {
streamButton.setImageDrawable(streamOff);
dbHandler.removeCallbacks(dbRunner);
streaming = false;
if (lastFragment.equals("bluetooth")){
BluetoothSensorFragment bt = (BluetoothSensorFragment)
getFragmentManager().findFragmentByTag("bluetooth");
bt.sensingControl(false, 0);
}
sampleService.stopSampling();
Intent i = new Intent(context, SensorSampleService.class);
stopService(i);
}
}
});
......@@ -138,11 +178,34 @@ public class SensorSampleActivity extends Activity implements SensorFragmentComm
t.setGravity(Gravity.CENTER, 0, 0);
t.show();
} else if (!db.addSpot(tripName, site, sector, spot)) {
Toast t = Toast.makeText(SensorSampleActivity.this, "The Spot number breaks the primary keys of the database. Please insert a new spot.", Toast.LENGTH_LONG);
t.setGravity(Gravity.CENTER, 0, 0);
t.show();
Dialog d = new AlertDialog.Builder(SensorSampleActivity.this, AlertDialog.THEME_HOLO_LIGHT)
.setTitle("Are you sure?")
.setMessage("There's already a spot with this trip, site, and sector. Make sure you are at the same geolocation that is " +
"associated with this spot.")
.setNegativeButton("Change Spot", null)
.setPositiveButton("I'm sure.", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent i = new Intent(context, SensorSampleService.class);
i.putExtra(SensorSampleService.LOG_INTERVAL, logInterval);
i.putExtra(SensorSampleService.SAMPLE_ONCE, true);
i.putExtra(SensorSampleService.TRIP_NAME, tripName);
i.putExtra(SensorSampleService.SITE, site);
i.putExtra(SensorSampleService.SECTOR, sector);
i.putExtra(SensorSampleService.SPOT, spot);
startService(i);
}
}).create();
d.show();
} else {
writeToDB();
Intent i = new Intent(context, SensorSampleService.class);
i.putExtra(SensorSampleService.LOG_INTERVAL, logInterval);
i.putExtra(SensorSampleService.SAMPLE_ONCE, true);
i.putExtra(SensorSampleService.TRIP_NAME, tripName);
i.putExtra(SensorSampleService.SITE, site);
i.putExtra(SensorSampleService.SECTOR, sector);
i.putExtra(SensorSampleService.SPOT, spot);
startService(i);
}
}
});
......@@ -161,102 +224,43 @@ public class SensorSampleActivity extends Activity implements SensorFragmentComm
}
@Override
protected void onPause(){
protected void onPause() {
super.onPause();
}
@Override
protected void onStop(){
protected void onStop() {
super.onStop();
dbHandler.removeCallbacks(dbRunner);
}
@Override
protected void onDestroy(){
protected void onDestroy() {
super.onDestroy();
locationManager.removeUpdates(locationListener);
dbHandler.removeCallbacks(dbRunner);
}
public void displaySensor(String sensor) {
FragmentManager fragMan = getFragmentManager();
Fragment frag = fragMan.findFragmentById(R.id.sensor_container);
switch (sensor) {
case "built-in":
frag = new BuiltInSensorsFragment();
fragMan.beginTransaction()
.add(R.id.sensor_container, frag, sensor)
.addToBackStack(null)
.commit();
break;
case "bluetooth":
BluetoothSensorFragment btf = BluetoothSensorFragment.newInstance(bluetoothSensor);
fragMan.beginTransaction()
.add(R.id.sensor_container, btf, sensor)
.addToBackStack(null)
.commit();
break;
if (sampleService != null) {
sampleService.stopSelf();
}
lastFragment = sensor;
}
public String getTimestamp(){
long seconds = System.currentTimeMillis() / 1000;
//return seconds;
SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.ENGLISH);
return s.format(new Date());
}
public void writeToDB() {
double[] geoInfo = {lastLatitude, lastLongitude, lastElevation};
boolean success = false;
String message = "";
// The user has connected and downloaded data from a remote database
if (remote_db) {
for (aSensor sensor : listSensors) {
if (sensor.getLastValues() != null) {
String table = "";
if (streaming) {
table = "fieldday_streaming";
db.addReading(sensor, sensor.getLastValues()[0], sensor.getLastValueQuality(), getTimestamp(),
site, sector, spot, geoInfo, tripName, lastAccuracy, lastSatellites, "", "", table);
} else {
table = "fieldday_reading";
success = db.addReading(sensor, sensor.getLastValues()[0], sensor.getLastValueQuality(), getTimestamp(),
site, sector, spot, geoInfo, tripName, lastAccuracy, lastSatellites, "", "", table);
message = "";
if (success) {
message = "Successfully took a sample";
} else {
message = "Could not write to the database. Try again.";
}
}
}
}
if (success){
Toast t = Toast.makeText(SensorSampleActivity.this, message, Toast.LENGTH_SHORT);
t.setGravity(Gravity.CENTER, 0, 0);
t.show();
}
} else {
Toast t = Toast.makeText(SensorSampleActivity.this, "You can't write to the database without connecting a remote schema", Toast.LENGTH_SHORT);
t.setGravity(Gravity.CENTER, 0, 0);
t.show();
if (sampleServiceConnection != null){
unregisterReceiver(sampleServiceReceiver);
unbindService(sampleServiceConnection);
}
}
public void setSensorList(ArrayList<aSensor> sensorList){
listSensors = sensorList;
}
public void enableButtons(){
public void enableButtons() {
streamButton.setClickable(true);
sampleButton.setClickable(true);
streamButton.setImageDrawable(res.getDrawable(R.drawable.stream_button, null));
sampleButton.setImageDrawable(res.getDrawable(R.drawable.sample_button, null));
}
private static IntentFilter makeSensorUpdateFilter() {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(SensorSampleService.ACTION_UPDATED_LIST);
intentFilter.addAction(SensorSampleService.ACTION_CONNECTED);
intentFilter.addAction(SensorSampleService.ACTION_DISCONNECTED);
return intentFilter;
}
public class myLocationListener implements LocationListener {
@Override
public void onLocationChanged(Location location) {
......@@ -294,14 +298,10 @@ public class SensorSampleActivity extends Activity implements SensorFragmentComm
logInterval = 1000 * 60;
break;
}
dbHandler.removeCallbacks(dbRunner);
if (streaming) {
dbHandler.postDelayed(dbRunner, logInterval);
}
Log.d("Changing Interval", "New Interval Time: " + String.valueOf(logInterval));
}
public void exportDatabase(){
public void exportDatabase() {
boolean success = db.copyDatabase("");
if (success){
Toast.makeText(this, "Successfully copied the database", Toast.LENGTH_LONG).show();
......@@ -310,7 +310,7 @@ public class SensorSampleActivity extends Activity implements SensorFragmentComm
}
}
public void setLocation(Location location){
public void setLocation(Location location) {
if (location != null) {
lastLongitude = location.getLongitude();
lastLatitude = location.getLatitude();
......@@ -324,7 +324,7 @@ public class SensorSampleActivity extends Activity implements SensorFragmentComm
}
}
private void initializeVariables(){
private void initializeVariables() {
res = getResources();
longAndLat = (TextView) findViewById(R.id.longandlat);
satellites = (TextView) findViewById(R.id.satellites);
......